-
Notifications
You must be signed in to change notification settings - Fork 83
/
atoms_calculators.jl
80 lines (67 loc) · 2.8 KB
/
atoms_calculators.jl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# Define AtomsCalculators interface for DFTK.
#
# This interface is inspired by the one used in Molly.jl,
# see https://github.com/JuliaMolSim/Molly.jl/blob/master/src/types.jl
using AtomsBase
using AtomsCalculators
Base.@kwdef struct DFTKParameters
model_kwargs = (; )
basis_kwargs = (; )
scf_kwargs = (; )
end
struct DFTKState{T}
scfres::T
end
DFTKState() = DFTKState((; ψ=nothing, ρ=nothing))
mutable struct DFTKCalculator
params::DFTKParameters
state::DFTKState
end
"""
Construct a [AtomsCalculators](https://github.com/JuliaMolSim/AtomsCalculators.jl)
compatible calculator for DFTK. The `model_kwargs` are passed onto the
[`Model`](@ref) constructor, the `basis_kwargs` to the [`PlaneWaveBasis`](@ref)
constructor, the `scf_kwargs` to [`self_consistent_field`](@ref). At the very
least the DFT `functionals` and the `Ecut` needs to be specified.
By default the calculator preserves the symmetries that are stored inside the
`state` (the basis is re-built, but symmetries are fixed and not re-computed).
## Example
```julia-repl
julia> DFTKCalculator(; model_kwargs=(; functionals=[:lda_x, :lda_c_vwn]),
basis_kwargs=(; Ecut=10, kgrid=(2, 2, 2)),
scf_kwargs=(; tol=1e-4))
```
"""
function DFTKCalculator(params::DFTKParameters)
DFTKCalculator(params, DFTKState()) # Create dummy state if not given.
end
function DFTKCalculator(; verbose=false, model_kwargs, basis_kwargs, scf_kwargs)
if !verbose
scf_kwargs = merge(scf_kwargs, (; callback=identity))
end
params = DFTKParameters(; model_kwargs, basis_kwargs, scf_kwargs)
DFTKCalculator(params)
end
function compute_scf!(system::AbstractSystem, calculator::DFTKCalculator, state::DFTKState)
params = calculator.params
# We re-use the symmetries from the state to avoid issues
# with accidentally more symmetric structures.
symmetries = haskey(state.scfres, :basis) ? state.scfres.basis.model.symmetries : true
model = model_DFT(system; symmetries, params.model_kwargs...)
basis = PlaneWaveBasis(model; params.basis_kwargs...)
ρ = @something state.scfres.ρ guess_density(basis, system)
scfres = self_consistent_field(basis; ρ, state.scfres.ψ, params.scf_kwargs...)
calculator.state = DFTKState(scfres)
end
AtomsCalculators.@generate_interface function AtomsCalculators.potential_energy(
system::AbstractSystem, calculator::DFTKCalculator; state = calculator.state,
kwargs...)
compute_scf!(system, calculator, state)
calculator.state.scfres.energies.total
end
AtomsCalculators.@generate_interface function AtomsCalculators.forces(
system::AbstractSystem, calculator::DFTKCalculator; state = calculator.state,
kwargs...)
compute_scf!(system, calculator, state)
compute_forces_cart(calculator.state.scfres)
end