Skip to content

Commit

Permalink
Handling complex numbers for PairwisePotential
Browse files Browse the repository at this point in the history
  • Loading branch information
epolack committed Apr 29, 2022
1 parent eeb1a56 commit d1e54bf
Showing 1 changed file with 37 additions and 16 deletions.
53 changes: 37 additions & 16 deletions src/terms/pairwise.jl
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
function norm_cplx(x)
# TODO: ForwardDiff bug (https://github.com/JuliaDiff/ForwardDiff.jl/issues/324)
sqrt(sum(x.*x))
end

struct PairwisePotential
V
params
Expand All @@ -10,10 +15,10 @@ Lennard—Jones terms.
The potential is dependent on the distance between to atomic positions and the pairwise
atomic types:
For a distance `d` between to atoms `A` and `B`, the potential is `V(d, params[(A, B)])`.
The parameters `max_radius` is of `100` by default, and gives the maximum (reduced) distance
between nuclei for which we consider interactions.
The parameters `max_radius` is of `1000` by default, and gives the maximum (full,
non-reduced) distance between nuclei for which we consider interactions.
"""
function PairwisePotential(V, params; max_radius=100)
function PairwisePotential(V, params; max_radius=1000)
params = Dict(minmax(key[1], key[2]) => value for (key, value) in params)
PairwisePotential(V, params, max_radius)
end
Expand All @@ -36,8 +41,10 @@ end
@timing "forces: Pairwise" function compute_forces(term::TermPairwisePotential,
basis::PlaneWaveBasis{T}, ψ, occ;
kwargs...) where {T}
forces = zero(basis.model.positions)
energy_pairwise(basis.model, term.V, term.params; term.max_radius, forces)
TT = eltype(lattice)
forces = zero(TT, basis.model.positions)
energy_pairwise(basis.model, term.V, term.params; max_radius=term.max_radius,
forces=forces, kwargs...)
forces
end

Expand All @@ -56,11 +63,17 @@ end

# This could be factorised with Ewald, but the use of `symbols` would slow down the
# computationally intensive Ewald sums. So we leave it as it for now.
# TODO: *Beware* of using ForwardDiff to derive this function with complex numbers, use
# multiplications and not powers (https://github.com/JuliaDiff/ForwardDiff.jl/issues/324)
function energy_pairwise(lattice, symbols, positions, V, params;
max_radius=100, forces=nothing)
T = eltype(lattice)
max_radius=1000, forces=nothing, ph_disp=nothing, q=0)
@assert length(symbols) == length(positions)

T = complex(eltype(lattice))
if ph_disp !== nothing
T = promote_type(T, eltype(ph_disp[1]))
end

if forces !== nothing
@assert size(forces) == size(positions)
forces_pairwise = copy(forces)
Expand Down Expand Up @@ -96,32 +109,40 @@ function energy_pairwise(lattice, symbols, positions, V, params;

ti = positions[i]
tj = positions[j]
# Phonons `q` points
if !isnothing(ph_disp)
ti += ph_disp[i] * cis(2T(π)*dot(q, zeros(3)))
tj += ph_disp[j] * cis(2T(π)*dot(q, R)) + R
end
ai, aj = minmax(symbols[i], symbols[j])
param_ij =params[(ai, aj)]

Δr = lattice * (ti .- tj .- R)
dist = norm(Δr)
Δr = lattice * (ti .- tj)
dist = norm_cplx(Δr)

# the potential decays very quickly, so cut off at some point
dist > max_radius && continue
abs(dist) > max_radius && continue

any_term_contributes = true
energy_contribution = V(dist, param_ij)
energy_contribution = real(V(dist, param_ij))
sum_pairwise += energy_contribution
if forces !== nothing
# We use ForwardDiff for the forces
dE_ddist = ForwardDiff.derivative(d -> V(d, param_ij), dist)
dE_dti = lattice' * dE_ddist / dist * Δr
dE_ddist = ForwardDiff.derivative(real(zero(eltype(dist)))) do ε
res = V(dist + ε, param_ij)
[real(res), imag(res)]
end |> x -> complex(x...)
dE_dti = lattice' * ((dE_ddist / dist) * Δr)
# We need to "break" the symmetry for phonons; at equilibrium, expect
# the forces to be zero at machine precision.
forces_pairwise[i] -= dE_dti
forces_pairwise[j] += dE_dti
end
end # i,j
end # R
rsh += 1
end
energy = sum_pairwise / 2 # Divide by 2 (because of double counting)
if forces !== nothing
forces .= forces_pairwise ./ 2
forces .= forces_pairwise
end
energy
end

0 comments on commit d1e54bf

Please sign in to comment.