103103
104104"""
105105Solve density-functional perturbation theory problem,
106- that is return δψ where (Ω+K) δψ = rhs .
106+ that is return δψ where (Ω+K) δψ = -δHextψ .
107107"""
108- @timing function solve_ΩplusK (basis:: PlaneWaveBasis{T} , ψ, rhs , occupation;
108+ @timing function solve_ΩplusK (basis:: PlaneWaveBasis{T} , ψ, δHextψ , occupation;
109109 callback= ResponseCallback (), tol= 1e-10 ) where {T}
110110 # for now, all orbitals have to be fully occupied -> need to strip them beforehand
111111 check_full_occupation (basis, occupation)
@@ -118,9 +118,9 @@ that is return δψ where (Ω+K) δψ = rhs.
118118 unpack (x) = unpack_ψ (reinterpret_complex (x), size .(ψ))
119119 unsafe_unpack (x) = unsafe_unpack_ψ (reinterpret_complex (x), size .(ψ))
120120
121- # project rhs on the tangent space before starting
122- proj_tangent! (rhs , ψ)
123- rhs_pack = pack (rhs )
121+ # project δHextψ on the tangent space before starting
122+ proj_tangent! (δHextψ , ψ)
123+ δHextψ_pack = pack (δHextψ )
124124
125125 # preconditioner
126126 Pks = [PreconditionerTPA (basis, kpt) for kpt in basis. kpoints]
@@ -145,15 +145,15 @@ that is return δψ where (Ω+K) δψ = rhs.
145145 Ωδψ = apply_Ω (δψ, ψ, H, Λ)
146146 pack (Ωδψ + Kδψ)
147147 end
148- J = LinearMap {T} (ΩpK, size (rhs_pack , 1 ))
148+ J = LinearMap {T} (ΩpK, size (δHextψ_pack , 1 ))
149149
150- # solve (Ω+K) δψ = rhs on the tangent space with CG
150+ # solve (Ω+K) δψ = -δHextψ on the tangent space with CG
151151 function proj (x)
152152 δψ = unpack (x)
153153 proj_tangent! (δψ, ψ)
154154 pack (δψ)
155155 end
156- res = cg (J, rhs_pack ; precon= FunctionPreconditioner (f_ldiv!), proj, tol,
156+ res = cg (J, - δHextψ_pack ; precon= FunctionPreconditioner (f_ldiv!), proj, tol,
157157 callback, comm= basis. comm_kpts)
158158 (; δψ= unpack (res. x), res. converged, res. tol, res. residual_norm,
159159 res. n_iter)
@@ -219,10 +219,10 @@ function (cb::OmegaPlusKDefaultCallback)(info)
219219end
220220
221221"""
222- Solve the problem `(Ω+K) δψ = rhs ` (density-functional perturbation theory)
223- using a split algorithm, where `rhs` is typically
224- `-δHextψ` ( the negative matvec of an external perturbation with the SCF orbitals `ψ`) and
225- `δψ` is the corresponding total variation in the orbitals `ψ`. Additionally returns:
222+ Solve the problem `(Ω+K) δψ = -δHextψ ` (density-functional perturbation theory)
223+ using a split algorithm, where
224+ `δψ` is the total variation in the orbitals `ψ` corresponding to the external perturbation δHext.
225+ Additionally returns:
226226 - `δρ`: Total variation in density
227227 - `δHψ`: Total variation in Hamiltonian applied to orbitals
228228 - `δeigenvalues`: Total variation in eigenvalues
@@ -243,7 +243,8 @@ Input parameters:
243243 see [arxiv 2505.02319](https://arxiv.org/pdf/2505.02319) for more details.
244244"""
245245@timing function solve_ΩplusK_split (ham:: Hamiltonian , ρ:: AbstractArray{T} , ψ, occupation, εF,
246- eigenvalues, rhs;
246+ eigenvalues, δHextψ;
247+ δtemperature= zero (real (T)),
247248 tol= 1e-8 , verbose= true ,
248249 mixing= SimpleMixing (),
249250 occupation_threshold,
@@ -268,7 +269,7 @@ Input parameters:
268269 # = χ04P (-1 + E K2P (1 - χ02P K2P)^-1 R (-χ04P))
269270 # where χ02P = R χ04P E and K2P = R K E
270271 basis = ham. basis
271- @assert size (rhs [1 ]) == size (ψ[1 ]) # Assume the same number of bands in ψ and rhs
272+ @assert size (δHextψ [1 ]) == size (ψ[1 ])
272273 start_ns = time_ns ()
273274
274275 # TODO Better initial guess handling. Especially between the last iteration of the GMRES
@@ -281,10 +282,11 @@ Input parameters:
281282
282283 # compute δρ0 (ignoring interactions)
283284 δρ0 = let # Make sure memory owned by res0 is freed
284- res0 = apply_χ0_4P (ham, ψ, occupation, εF, eigenvalues, - rhs;
285+ res0 = apply_χ0_4P (ham, ψ, occupation, εF, eigenvalues, δHextψ;
286+ δtemperature,
285287 maxiter= maxiter_sternheimer, tol= tol * factor_initial,
286288 bandtolalg, occupation_threshold,
287- q, kwargs... ) # = - χ04P * rhs
289+ q, kwargs... ) # = χ04P * δHext
288290 callback ((; stage= :noninteracting , runtime_ns= time_ns () - start_ns,
289291 Axinfos= [(; basis, tol= tol* factor_initial, res0... )]))
290292 compute_δρ (basis, ψ, res0. δψ, occupation, res0. δoccupation;
@@ -308,42 +310,46 @@ Input parameters:
308310 @warn " Solve_ΩplusK_split solver not converged"
309311 end
310312
311- # Compute total change in Hamiltonian applied to ψ
313+ # Now we got δρ, but we're not done yet, because we want the full output of the four-point apply_χ0_4P,
314+ # so we redo an apply_χ0_4P
315+
316+ # Induced potential variation
312317 δVind = apply_kernel (basis, δρ; ρ, q) # Change in potential induced by δρ
313318
319+ # Total variation δHtot ψ
314320 # For phonon calculations, assemble
315321 # δHψ_k = δV_{q} · ψ_{k-q}.
316- δHψ = multiply_ψ_by_blochwave (basis, ψ, δVind, q) .- rhs
317-
318- # Compute total change in eigenvalues
319- δeigenvalues = map (ψ, δHψ) do ψk, δHψk
320- map (eachcol (ψk), eachcol (δHψk)) do ψnk, δHψnk
321- real (dot (ψnk, δHψnk)) # δε_{nk} = <ψnk | δH | ψnk>
322- end
323- end
322+ δHtotψ = multiply_ψ_by_blochwave (basis, ψ, δVind, q) .+ δHextψ
324323
325324 # Compute final orbital response
326325 # TODO Here we just use what DFTK did before the inexact Krylov business, namely
327326 # a fixed Sternheimer tolerance of tol / 10. There are probably
328327 # smarter things one could do here
329- resfinal = apply_χ0_4P (ham, ψ, occupation, εF, eigenvalues, δHψ;
328+ resfinal = apply_χ0_4P (ham, ψ, occupation, εF, eigenvalues, δHtotψ;
329+ δtemperature,
330330 maxiter= maxiter_sternheimer, tol= tol * factor_final,
331331 bandtolalg, occupation_threshold, q, kwargs... )
332332 callback ((; stage= :final , runtime_ns= time_ns () - start_ns,
333333 Axinfos= [(; basis, tol= tol* factor_final, resfinal... )]))
334+ # Compute total change in eigenvalues
335+ δeigenvalues = map (ψ, δHtotψ) do ψk, δHtotψk
336+ map (eachcol (ψk), eachcol (δHtotψk)) do ψnk, δHtotψnk
337+ real (dot (ψnk, δHtotψnk)) # δε_{nk} = <ψnk | δHtot | ψnk>
338+ end
339+ end
334340
335- (; resfinal. δψ, δρ, δHψ , δVind, δρ0, δeigenvalues, resfinal. δoccupation,
341+ (; resfinal. δψ, δρ, δHtotψ , δVind, δρ0, δeigenvalues, resfinal. δoccupation,
336342 resfinal. δεF, ε_adj, info_gmres)
337343end
338344
339- function solve_ΩplusK_split (scfres:: NamedTuple , rhs ; kwargs... )
345+ function solve_ΩplusK_split (scfres:: NamedTuple , δHextψ ; kwargs... )
340346 if (scfres. mixing isa KerkerMixing || scfres. mixing isa KerkerDosMixing)
341347 mixing = scfres. mixing
342348 else
343349 mixing = SimpleMixing ()
344350 end
345351 solve_ΩplusK_split (scfres. ham, scfres. ρ, scfres. ψ, scfres. occupation,
346- scfres. εF, scfres. eigenvalues, rhs ;
352+ scfres. εF, scfres. eigenvalues, δHextψ ;
347353 scfres. occupation_threshold, mixing,
348354 bandtolalg= BandtolBalanced (scfres), kwargs... )
349355end
0 commit comments