AtomsCalculators integration

AtomsCalculators.jl is an interface for doing standard computations (energies, forces, stresses, hessians) on atomistic structures. It is very much inspired by the calculator objects in the atomistic simulation environment.

DFTK by default ships a datastructure called a DFTKCalculator, which implements the AtomsCalculators interface. A DFTKCalculator can be constructed by passing three different named tuples, the model_kwargs, the basis_kwargs and the scf_kwargs. The first two named tuples are passed as keyword arguments when constructing the DFT model using model_DFT and its discretization using the PlaneWaveBasis. The last one is used as keyword arguments when running the self_consistent_field function on the resulting basis to solve the problem numerically. Thus when using the DFTKCalculator the user is expected to pass these objects exactly the keyword argument one would pass when constructing a model and basis and when calling self_consistent_field.

For example, to perform the calculation of the Tutorial using the AtomsCalculators interface we define the calculator as such:

using DFTK
using PseudoPotentialData

pd_lda_family = PseudoFamily("dojo.nc.sr.lda.v0_4_1.oncvpsp3.standard.upf")
model_kwargs  = (; functionals=LDA(), pseudopotentials=pd_lda_family)
basis_kwargs  = (; kgrid=[4, 4, 4], Ecut=7)
scf_kwargs    = (; tol=1e-5)
calc = DFTKCalculator(; model_kwargs, basis_kwargs, scf_kwargs)
DFTKCalculator(functionals=Xc(lda_x, lda_c_pw), pseudopotentials=PseudoFamily("dojo.nc.sr.lda.v0_4_1.oncvpsp3.standard.upf"), Ecut=7, kgrid=[4, 4, 4])

Note, that the scf_kwargs is optional and can be missing (then the defaults of self_consistent_field are used).

Kpoints from kpoint density

Note that DFTK's kgrid_from_maximal_spacing function can also be used with AbstractSystem objects to determine an appropriate kgrid paramter for the basis_kwargs. E.g. kgrid_from_maximal_spacing(system, 0.25u"1/Å") gives a k-point spacing of 0.25 per Angström for the passed system.

Based on this calc object we can perform a DFT calculation on bulk silicon according to the AtomsCalculators interface, e.g.

using AtomsBuilder
using AtomsCalculators
AC = AtomsCalculators

# Bulk silicon system of the Tutorial
silicon = bulk(:Si)
AC.potential_energy(silicon, calc)  # Compute total energy
-8.508511644161782 Eₕ

or we can compute the energy and forces:

results = AC.calculate((AC.Energy(), AC.Forces()), silicon, calc)
results.energy
-8.508511644216577 Eₕ
results.forces
2-element Vector{StaticArraysCore.SVector{3, Unitful.Quantity{Float64, 𝐋 𝐌 𝐓^-2, Unitful.FreeUnits{(a₀^-1, Eₕ), 𝐋 𝐌 𝐓^-2, nothing}}}}:
 [1.6710801476452107e-14 Eₕ a₀^-1, 1.6182868388824755e-14 Eₕ a₀^-1, 1.717232017165661e-14 Eₕ a₀^-1]
 [-1.671124810641963e-14 Eₕ a₀^-1, -1.6213589433617098e-14 Eₕ a₀^-1, -1.6888861047817083e-14 Eₕ a₀^-1]

Note that the results object returned by the call to AtomsCalculators.calculate also contains a state, which is a DFTK scfres. This can be used to speed up subsequent computations:

# This is basically for free, since already computed:
results2 = @time AC.calculate((AC.Energy(), AC.Forces()), silicon, calc, nothing, results.state);
  0.282030 seconds (744.74 k allocations: 76.959 MiB)

For an example using the DFTKCalculator, see Geometry optimization.