Implementing the interface
Note, this section is partly outdated!
You can either implement both of the calls e.g. for energy
AtomsCalculators.potential_energy(system, calculator; kwargs...)
and AtomsCalculators.calculate(AtomsCalculators.Energy(), system, calculator, ps=nothing, st=nothing; kwargs...)
Example implementations
Example potential_energy
implementation
using AtomsCalculators
using Unitful
struct MyType
end
AtomsCalculators.@generate_interface function AtomsCalculators.potential_energy(system, calculator::MyType; kwargs...)
# we can ignore kwargs... or use them to tune the calculation
# or give extra information like pairlist
# add your own definition here
return 0.0u"eV"
end
Completely equivalent implementation is
AtomsCalculators.@generate_interface function AtomsCalculators.calculate(
::AtomsCalculators.Energy,
system,
calculator::MyType,
ps=nothing,
st=nothing;
kwargs...
)
# we can ignore kwargs... or use them to tune the calculation
# or give extra information like pairlist
# add your own definition here
return ( energy = 0.0u"eV, )
end
Example virial
implementation
AtomsCalculators.@generate_interface function AtomsCalculators.virial(system, calculator::MyType; kwargs...)
# we can ignore kwargs... or use them to tune the calculation
# or give extra information like pairlist
# add your own definition here
return zeros(3,3) * u"eV"
end
Equivalent implementation is
AtomsCalculators.@generate_interface function AtomsCalculators.calculate(
::AtomsCalculators.Virial,
system,
calculator::MyType,
ps=nothing,
st=nothing;
kwargs...
)
# we can ignore kwargs... or use them to tune the calculation
# or give extra information like pairlist
# add your own definition here
return ( virial = zeros(3,3) * u"eV", state=nothing)
end
Implementing forces call
Basic example
AtomsCalculators.@generate_interface function AtomsCalculators.forces(system, calculator::MyType; kwargs...)
# we can ignore kwargs... or use them to tune the calculation
# or give extra information like pairlist
# add your own definition
return AtomsCalculators.zero_forces(system, calculator)
end
This creates both forces
and forces!
and calculate
command with Forces()
support.
AtomsCalculators.zero_forces(system, calculator)
is a function that creates zero forces for a given calculator and system combo. You can use this function to tune your force output.
Same way AtomsCalculators.promote_force_type(system, calculator)
creates a force type for the calculator for given input that can be used to allocate force data. You can also allocate for some other type, of your choosing or use the default one. You can overload promote_force_type
for your force type, this is automatically used by zero_forces
command to change the element type. If you wan to change array type overload zero_forces
for your calculator.
Alternatively the definition could have been done with
AtomsCalculators.@generate_interface function AtomsCalculators.calculate(
::AtomsCalculators.Forces,
system,
calculator::MyType,
ps=nothing,
st=nothing;
kwargs...
)
# we can ignore kwargs... or use them to tune the calculation
# or give extra information like pairlist
# add your own definition
return ( forces = zeros(AtomsCalculators.promote_force_type(system, calculator), length(system)), state=nothing )
end
or with non-allocating forces
struct MyOtherType
end
AtomsCalculators.@generate_interface function AtomsCalculators.forces!(f::AbstractVector, system, calculator::MyOtherType; kwargs...)
@assert length(f) == length(system)
# we can ignore kwargs... or use them to tune the calculation
# or give extra information like pairlist
# add your own definition
for i in eachindex(f)
# forces! adds to the force array
f[i] += zero(AtomsCalculators.promote_force_type(system, calculator))
end
return f
end
Other Automatically Generated Calls
Many methods have optimized calls when energy and forces (and/or virial) are calculated together. To allow access to these calls there are also calls
energy_forces
for potential energy and allocating forcesenergy_forces!
for potential energy and non-allocating forcesenergy_forces_virial
for potential energy, allocating forces and virialenergy_forces_virial!
for potential energy, non-allocating forces and virial
These all are generated automatically, if you have defined the corresponding individual methods. The main idea here is that you can implement more efficient methods by yourself.
Example implementation
function AtomsCalculators.potential_energy_forces(system, calculator::MyType; kwargs...)
# we can ignore kwargs... or use them to tune the calculation
# or give extra information like pairlist
# add your own definition here
E = 0.0u"eV"
f = zeros(AtomsCalculators.default_force_eltype, length(system))
return (;
:energy => E,
:forces => f
)
end
Defining this does not overload the corresponding non-allocating call - you need to do that separately.
Output for the combination methods is defined to have keys :energy
, :forces
and :virial
. You can access them with
output[:energy]
for energyoutput[:forces]
for forcesoutput[:virial]
for viral
The type of the output can be NamedTuple, as was in the example above, or any structure that has the keys implemented and also supports splatting. The reason for this is that this allows everyone to implement the performance functions without being restricted to certain output type, and to allow using haskey
to check the output.
Testing Function Calls
We have implemented function calls to help you testing the API. There is one call for each type of calls
test_potential_energy
to test potential_energy calltest_forces
to test both allocating and non-allocating force callstest_virial
to test virial calltest_energy_forces
to test both potential energy and force callstest_energy_forces_virial
to test everything
To get these access to these functions you need to call
using AtomsCalculators.AtomsCalculatorsTesting
To test our example potential MyType
we can do
using AtomsBase
using AtomsCalculators.AtomsCalculatorsTesting
hydrogen = isolated_system([
:H => [0, 0, 0.]u"Å",
:H => [0, 0, 1.]u"Å"
])
test_potential_energy(hydrogen, MyType())
test_forces(hydrogen, MyType())
test_virial(hydrogen, MyType())
test_forces(hydrogen, MyOtherType()) # this works
test_virial(hydrogen, MyOtherType()) # this will fail
# If you have energy and forces implemented use this over others
test_energy_forces(hydrogen, MyType())
# If you have energy, forces and virial implemented use this others
test_energy_forces_virial(hydrogen, MyType())
It is recommended that you use the test functions to test that your implementation supports the API fully!