-
Notifications
You must be signed in to change notification settings - Fork 87
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Co-authored-by: Michael F. Herbst <info@michael-herbst.com>
- Loading branch information
1 parent
829af2a
commit 0224d7f
Showing
8 changed files
with
192 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
""" | ||
Computes minus the derivatives of the energy with respect to atomic positions. | ||
""" | ||
function forces(basis, ρ, Psi, occupation) | ||
# By generalized Hellmann-Feynman, dE/dR = ∂E/∂R. The atomic | ||
# positions come up explicitly only in the external and nonlocal | ||
# part of the energy, | ||
|
||
# TODO this assumes that the build_external and build_nonlocal are consistent with model.atoms | ||
# Find a way to move this computation closer to each term | ||
|
||
# minus signs here because f = -∇E | ||
model = basis.model | ||
T = real(eltype(Psi[1])) | ||
|
||
forces = [] | ||
for (type, positions) in model.atoms | ||
@assert type.psp != nothing | ||
# force for this atom type | ||
f = zeros(Vec3{T}, length(positions)) | ||
|
||
# Local part | ||
@assert model.build_external !== nothing | ||
# energy = sum of form_factor(G) * struct_factor(G) * rho(G) | ||
# where struct_factor(G) = cis(-2T(π) * dot(G, r)) | ||
form_factors = [Complex{T}(eval_psp_local_fourier(type.psp, model.recip_lattice * G)) | ||
for G in G_vectors(basis)] ./ sqrt(model.unit_cell_volume) | ||
form_factors[1] = 0 | ||
|
||
for (ir, r) in enumerate(positions) | ||
f[ir] -= real(sum(conj(ρ.fourier[iG]) .* | ||
form_factors[iG] .* | ||
cis(-2T(π) * dot(G, r)) .* | ||
(-2T(π)) .* G .* im | ||
for (iG, G) in enumerate(G_vectors(basis)))) | ||
end | ||
|
||
# Nonlocal part | ||
@assert model.build_nonlocal !== nothing | ||
C = build_projection_coefficients_(type.psp) | ||
for (ir, r) in enumerate(positions) | ||
fr = zeros(T, 3) | ||
for idir = 1:3 | ||
for (ik, kpt) in enumerate(basis.kpoints) | ||
# energy terms are of the form <psi, P C P' psi>, where P(G) = form_factor(G) * structure_factor(G) | ||
qs = [model.recip_lattice * (kpt.coordinate + G) for G in G_vectors(kpt)] | ||
form_factors = build_form_factors(type.psp, qs) | ||
structure_factors = [cis(-2T(π)*dot(kpt.coordinate + G, r)) for G in G_vectors(kpt)] | ||
P = structure_factors .* form_factors ./ sqrt(model.unit_cell_volume) | ||
dPdR = [-2T(π)*im*(kpt.coordinate + G)[idir] for G in G_vectors(kpt)] .* P | ||
|
||
# TODO BLASify this further | ||
for iband = 1:size(Psi[ik], 2) | ||
psi = Psi[ik][:, iband] | ||
fr[idir] -= basis.kweights[ik] * occupation[ik][iband] * | ||
real(dot(psi, P*C*dPdR'*psi) + dot(psi, dPdR*C*P'*psi)) | ||
end | ||
end | ||
end | ||
f[ir] += fr | ||
end | ||
|
||
push!(forces, f) | ||
end | ||
|
||
# Add Ewald forces | ||
forces_ewald = zeros(Vec3{T}, sum(length(positions) for (elem, positions) in model.atoms)) | ||
energy_nuclear_ewald(model; forces=forces_ewald) | ||
|
||
count = 1 | ||
for i = 1:length(model.atoms) | ||
for j = 1:length(model.atoms[i][2]) | ||
forces[i][j] += forces_ewald[count] | ||
count += 1 | ||
end | ||
end | ||
|
||
forces | ||
end | ||
forces(scfres) = forces(scfres.ham.basis, scfres.ρ, scfres.Psi, scfres.occupation) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
using DFTK | ||
using Test | ||
include("testcases.jl") | ||
|
||
@testset "Forces" begin | ||
function energy(pos) | ||
kgrid = [2, 1, 2] # k-Point grid | ||
Ecut = 5 # kinetic energy cutoff in Hartree | ||
|
||
# Setup silicon lattice | ||
lattice = silicon.lattice | ||
Si = Element(silicon.atnum, psp=load_psp(silicon.psp)) | ||
atoms = [Si => pos] | ||
model = model_dft(silicon.lattice, :lda_xc_teter93, atoms) | ||
kcoords, ksymops = bzmesh_ir_wedge(kgrid, lattice, atoms) | ||
basis = PlaneWaveBasis(model, Ecut, kcoords, ksymops) | ||
|
||
n_bands_scf = Int(model.n_electrons / 2) | ||
ham = Hamiltonian(basis, guess_density(basis)) | ||
scfres = self_consistent_field(ham, n_bands_scf, tol=1e-12) | ||
|
||
sum(values(scfres.energies)), forces(scfres) | ||
end | ||
|
||
pos1 = [(ones(3)+0.1*randn(3))/8, -ones(3)/8] | ||
disp = randn(3) | ||
ε = 1e-8 | ||
pos2 = [pos1[1]+ε*disp, pos1[2]] | ||
|
||
E1, F1 = energy(pos1) | ||
E2, F2 = energy(pos2) | ||
|
||
diff_findiff = -(E2-E1)/ε | ||
diff_forces = dot(F1[1][1], disp) | ||
|
||
@test abs(diff_findiff - diff_forces) < 1e-6 | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters