-
Notifications
You must be signed in to change notification settings - Fork 85
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
eeaf145
commit df1bd6c
Showing
5 changed files
with
191 additions
and
10 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
""" | ||
Computes minus the derivatives of the energy with respect to atomic positions. | ||
""" | ||
function forces(scfres) | ||
# 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 | ||
ham = scfres.ham | ||
basis = ham.basis | ||
model = basis.model | ||
Psi = scfres.Psi | ||
ρ = scfres.ρ | ||
T = real(eltype(ham)) | ||
|
||
forces = [] | ||
for (type, pos) in model.atoms | ||
@assert type.psp != nothing | ||
f = zeros(Vec3{T}, length(pos)) | ||
|
||
# Local part | ||
# energy = sum of form_factor(G) * struct_factor(G) * rho(G) | ||
# where struct_factor(G) = cis(-2T(π) * dot(G, r)) | ||
form_factors = [Complex{T}(1/sqrt(model.unit_cell_volume) * | ||
eval_psp_local_fourier(type.psp, model.recip_lattice * G)) | ||
for G in G_vectors(basis)] | ||
form_factors[1] = 0 | ||
|
||
for (ir, r) in enumerate(pos) | ||
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 | ||
C = build_projection_coefficients_(type.psp) | ||
for (ir, r) in enumerate(pos) | ||
fr = zeros(T, 3) | ||
for idir = 1:3 | ||
for (ik, kpt) in enumerate(basis.kpoints) | ||
form_factors = build_form_factors(type.psp, [model.recip_lattice * (kpt.coordinate + G) for G in kpt.basis]) | ||
structure_factors = [cis(-2T(π)*dot(kpt.coordinate + G, r)) for G in kpt.basis] | ||
P = structure_factors .* form_factors ./ sqrt(model.unit_cell_volume) | ||
dPdR = [-2T(π)*im*(kpt.coordinate + G)[idir] * cis(-2T(π)*dot(kpt.coordinate + G, r)) for G in kpt.basis] .* | ||
form_factors ./ | ||
sqrt(model.unit_cell_volume) | ||
# TODO BLASify this further | ||
for iband = 1:size(Psi[ik], 2) | ||
psi = Psi[ik][:, iband] | ||
fr[idir] -= basis.kweights[ik] * scfres.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 the ewald term | ||
charges_all = [charge_ionic(type) for (type, positions) in model.atoms for pos in positions] | ||
positions_all = [pos for (elem, positions) in model.atoms for pos in positions] | ||
forces_ewald = zeros(Vec3{T}, length(positions_all)) | ||
energy_ewald(model.lattice, charges_all, positions_all; forces=forces_ewald) | ||
|
||
offset = 1 | ||
for i = 1:length(model.atoms) | ||
for j = 1:length(model.atoms[i][2]) | ||
forces[i][j] += forces_ewald[offset] | ||
offset += 1 | ||
end | ||
end | ||
forces | ||
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
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,52 @@ | ||
using DFTK | ||
using Test | ||
function energy(pos) | ||
# Calculation parameters | ||
kgrid = [2, 1, 2] # k-Point grid | ||
supercell = [1, 1, 1] # Lattice supercell | ||
Ecut = 5 # kinetic energy cutoff in Hartree | ||
|
||
# Setup silicon lattice | ||
a = 10.263141334305942 # Silicon lattice constant in Bohr | ||
lattice = a / 2 .* [[0 1 1.]; [1 0 1.]; [1 1 0.]] | ||
Si = AtomType(14, psp=load_psp("hgh/lda/Si-q4")) | ||
# Si = AtomType(14) | ||
atoms = [Si => pos] | ||
|
||
model = Model(lattice; | ||
atoms=atoms, | ||
external=term_external(atoms), | ||
nonlocal=term_nonlocal(atoms), | ||
hartree=term_hartree(), | ||
xc=term_xc(:lda_xc_teter93)) | ||
|
||
kcoords, ksymops = bzmesh_ir_wedge(kgrid, lattice, atoms) | ||
basis = PlaneWaveBasis(model, Ecut, kcoords, ksymops) | ||
|
||
# Run SCF. Note Silicon is a semiconductor, so we use an insulator | ||
# occupation scheme. This will cause warnings in some models, because | ||
# e.g. in the :reduced_hf model silicon is a metal | ||
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) | ||
ham = scfres.ham | ||
|
||
energies = scfres.energies | ||
sum(values(energies)), forces(scfres) | ||
end | ||
|
||
using Random | ||
Random.seed!(0) | ||
|
||
pos1 = [(ones(3)+.1*randn(3))/8, -ones(3)/8] | ||
disp = randn(3) | ||
ε = 1e-7 | ||
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 |