|
55 | 55 | psp_groups = [group for group in model.atom_groups |
56 | 56 | if model.atoms[first(group)] isa ElementPsp] |
57 | 57 |
|
58 | | - # early return if no pseudopotential atoms |
| 58 | + # Early return if no pseudopotential atoms. |
59 | 59 | isempty(psp_groups) && return nothing |
60 | 60 |
|
61 | | - # energy terms are of the form <psi, P C P' psi>, where P(G) = form_factor(G) * structure_factor(G) |
| 61 | + # Energy terms are of the form <ψ, P C P' ψ>, where |
| 62 | + # P(G) = form_factor(G) * structure_factor(G). |
62 | 63 | forces = [zero(Vec3{T}) for _ = 1:length(model.positions)] |
| 64 | + |
63 | 65 | for group in psp_groups |
64 | 66 | element = model.atoms[first(group)] |
65 | 67 |
|
66 | 68 | C = build_projection_coefficients(T, element.psp) |
67 | 69 | for (ik, kpt) in enumerate(basis.kpoints) |
68 | | - # we compute the forces from the irreductible BZ; they are symmetrized later |
| 70 | + # We compute the forces from the irreductible BZ; they are symmetrized later. |
69 | 71 | G_plus_k = Gplusk_vectors(basis, kpt) |
70 | 72 | G_plus_k_cart = to_cpu(Gplusk_vectors_cart(basis, kpt)) |
71 | 73 | form_factors = build_form_factors(element.psp, G_plus_k_cart) |
|
77 | 79 | forces[idx] += map(1:3) do α |
78 | 80 | dPdR = [-2T(π)*im*p[α] for p in G_plus_k] .* P |
79 | 81 | ψk = ψ[ik] |
80 | | - dHψk = P * (C * (dPdR' * ψk)) |
| 82 | + δHψk = P * (C * (dPdR' * ψk)) |
81 | 83 | -sum(occupation[ik][iband] * basis.kweights[ik] * |
82 | | - 2real(dot(ψk[:, iband], dHψk[:, iband])) |
| 84 | + 2real(dot(ψk[:, iband], δHψk[:, iband])) |
83 | 85 | for iband=1:size(ψk, 2)) |
84 | 86 | end # α |
85 | 87 | end # r |
@@ -155,11 +157,12 @@ where ``\widehat{\rm proj}_i(p) = ∫_{ℝ^3} {\rm proj}_i(r) e^{-ip·r} dr``. |
155 | 157 | We store ``\frac{1}{\sqrt Ω} \widehat{\rm proj}_i(k+G)`` in `proj_vectors`. |
156 | 158 | """ |
157 | 159 | function build_projection_vectors(basis::PlaneWaveBasis{T}, kpt::Kpoint, |
158 | | - psps, psp_positions) where {T} |
| 160 | + psps::AbstractVector{<: NormConservingPsp}, |
| 161 | + psp_positions) where {T} |
159 | 162 | unit_cell_volume = basis.model.unit_cell_volume |
160 | 163 | n_proj = count_n_proj(psps, psp_positions) |
161 | 164 | n_G = length(G_vectors(basis, kpt)) |
162 | | - proj_vectors = zeros(Complex{T}, n_G, n_proj) |
| 165 | + proj_vectors = zeros(Complex{eltype(psp_positions[1][1])}, n_G, n_proj) |
163 | 166 | G_plus_k = to_cpu(Gplusk_vectors(basis, kpt)) |
164 | 167 |
|
165 | 168 | # Compute the columns of proj_vectors = 1/√Ω \hat proj_i(k+G) |
@@ -230,3 +233,120 @@ function build_form_factors(psp, G_plus_k::AbstractVector{Vec3{TT}}) where {TT} |
230 | 233 | end |
231 | 234 | form_factors |
232 | 235 | end |
| 236 | + |
| 237 | +# Helpers for phonon computations. |
| 238 | +function build_projection_coefficients(basis::PlaneWaveBasis{T}, psp_groups) where {T} |
| 239 | + psps = [basis.model.atoms[first(group)].psp for group in psp_groups] |
| 240 | + psp_positions = [basis.model.positions[group] for group in psp_groups] |
| 241 | + build_projection_coefficients(T, psps, psp_positions) |
| 242 | +end |
| 243 | +function build_projection_vectors(basis::PlaneWaveBasis, kpt::Kpoint, |
| 244 | + psp_groups::AbstractVector{<: AbstractVector{<: Int}}, |
| 245 | + positions) |
| 246 | + psps = [basis.model.atoms[first(group)].psp for group in psp_groups] |
| 247 | + psp_positions = [positions[group] for group in psp_groups] |
| 248 | + build_projection_vectors(basis, kpt, psps, psp_positions) |
| 249 | +end |
| 250 | +function PDPψk(basis, positions, psp_groups, kpt, kpt_minus_q, ψk) |
| 251 | + D = build_projection_coefficients(basis, psp_groups) |
| 252 | + P = build_projection_vectors(basis, kpt, psp_groups, positions) |
| 253 | + P_minus_q = build_projection_vectors(basis, kpt_minus_q, psp_groups, positions) |
| 254 | + P * (D * P_minus_q' * ψk) |
| 255 | +end |
| 256 | + |
| 257 | +function compute_dynmat_δH(::TermAtomicNonlocal, basis::PlaneWaveBasis{T}, ψ, occupation, |
| 258 | + δψ, δoccupation, q) where {T} |
| 259 | + S = complex(T) |
| 260 | + model = basis.model |
| 261 | + psp_groups = [group for group in model.atom_groups |
| 262 | + if model.atoms[first(group)] isa ElementPsp] |
| 263 | + |
| 264 | + # Early return if no pseudopotential atoms. |
| 265 | + isempty(psp_groups) && return nothing |
| 266 | + |
| 267 | + δforces = [zero(Vec3{S}) for _ = 1:length(model.positions)] |
| 268 | + for group in psp_groups |
| 269 | + δψ_plus_q = transfer_blochwave_equivalent_to_actual(basis, δψ, q) |
| 270 | + for (ik, kpt) in enumerate(basis.kpoints) |
| 271 | + ψk = ψ[ik] |
| 272 | + δψk_plus_q = δψ_plus_q[ik].ψk |
| 273 | + kpt_plus_q = δψ_plus_q[ik].kpt |
| 274 | + |
| 275 | + for idx in group |
| 276 | + δforces[idx] += map(1:3) do α |
| 277 | + δHψk = derivative_wrt_αs(model.positions, α, idx) do positions_αs |
| 278 | + PDPψk(basis, positions_αs, psp_groups, kpt_plus_q, kpt, ψ[ik]) |
| 279 | + end |
| 280 | + δHψk_plus_q = derivative_wrt_αs(model.positions, α, idx) do positions_αs |
| 281 | + PDPψk(basis, positions_αs, psp_groups, kpt, kpt, ψ[ik]) |
| 282 | + end |
| 283 | + -sum( 2occupation[ik][iband] * basis.kweights[ik] |
| 284 | + * dot(δψk_plus_q[:, iband], δHψk[:, iband]) |
| 285 | + + δoccupation[ik][iband] * basis.kweights[ik] |
| 286 | + * 2real(dot(ψk[:, iband], δHψk_plus_q[:, iband])) |
| 287 | + for iband=1:size(ψk, 2)) |
| 288 | + end |
| 289 | + end |
| 290 | + end |
| 291 | + end |
| 292 | + |
| 293 | + mpi_sum!(δforces, basis.comm_kpts) |
| 294 | +end |
| 295 | + |
| 296 | +@views function compute_dynmat(term::TermAtomicNonlocal, basis::PlaneWaveBasis{T}, ψ, |
| 297 | + occupation; δψs, δoccupations, q=zero(Vec3{T}), |
| 298 | + kwargs...) where {T} |
| 299 | + S = complex(T) |
| 300 | + model = basis.model |
| 301 | + positions = model.positions |
| 302 | + n_atoms = length(positions) |
| 303 | + n_dim = model.n_dim |
| 304 | + |
| 305 | + # Two contributions: dynmat_δH and dynmat_δ²H. |
| 306 | + |
| 307 | + # dynmat_δH |
| 308 | + dynmat_δH = zeros(S, 3, n_atoms, 3, n_atoms) |
| 309 | + for s = 1:n_atoms, α = 1:n_dim |
| 310 | + dynmat_δH[:, :, α, s] .-= stack( |
| 311 | + compute_dynmat_δH(term, basis, ψ, occupation, δψs[α, s], δoccupations[α, s], q) |
| 312 | + ) |
| 313 | + end |
| 314 | + |
| 315 | + psp_groups = [group for group in model.atom_groups |
| 316 | + if model.atoms[first(group)] isa ElementPsp] |
| 317 | + # Early return if no pseudopotential atoms. |
| 318 | + isempty(psp_groups) && return dynmat_δH |
| 319 | + |
| 320 | + # dynmat_δ²H |
| 321 | + dynmat_δ²H = zeros(S, 3, n_atoms, 3, n_atoms) |
| 322 | + δ²Hψ = zero.(ψ) |
| 323 | + for s = 1:n_atoms, α = 1:n_dim, β = 1:n_dim # zero if s ≠ t |
| 324 | + for (ik, kpt) in enumerate(basis.kpoints) |
| 325 | + δ²Hψ[ik] = derivative_wrt_αs(basis.model.positions, β, s) do positions_βs |
| 326 | + derivative_wrt_αs(positions_βs, α, s) do positions_βsαs |
| 327 | + PDPψk(basis, positions_βsαs, psp_groups, kpt, kpt, ψ[ik]) |
| 328 | + end |
| 329 | + end |
| 330 | + dynmat_δ²H[β, s, α, s] += sum(occupation[ik][n] * basis.kweights[ik] * |
| 331 | + dot(ψ[ik][:, n], δ²Hψ[ik][:, n]) |
| 332 | + for n=1:size(ψ[ik], 2)) |
| 333 | + end |
| 334 | + end |
| 335 | + |
| 336 | + dynmat_δH + dynmat_δ²H |
| 337 | +end |
| 338 | + |
| 339 | +# δH is the Fourier transform perturbation of the nonlocal potential due to a position |
| 340 | +# displacement e^{iq·r} of the α coordinate of atom s. |
| 341 | +function compute_δHψ_αs(::TermAtomicNonlocal, basis::PlaneWaveBasis{T}, ψ, α, s, q) where {T} |
| 342 | + model = basis.model |
| 343 | + psp_groups = [group for group in model.atom_groups |
| 344 | + if model.atoms[first(group)] isa ElementPsp] |
| 345 | + |
| 346 | + ψ_minus_q = transfer_blochwave_equivalent_to_actual(basis, ψ, -q) |
| 347 | + map(enumerate(basis.kpoints)) do (ik, kpt) |
| 348 | + derivative_wrt_αs(model.positions, α, s) do positions_αs |
| 349 | + PDPψk(basis, positions_αs, psp_groups, kpt, ψ_minus_q[ik].kpt, ψ_minus_q[ik].ψk) |
| 350 | + end |
| 351 | + end |
| 352 | +end |
0 commit comments