|
| 1 | +# Linear combination of two pseudos |
| 2 | +# Note that this data structure is deliberately not yet exported for now |
| 3 | +# as we may want to find a different solution for representing virtual crystal elements |
| 4 | +# in the future; users should use virtual_crystal_element to implicitly construct |
| 5 | +# these objects. |
| 6 | +# |
| 7 | +struct PspLinComb{T, P <: NormConservingPsp} <: NormConservingPsp |
| 8 | + lmax::Int |
| 9 | + h::Vector{Matrix{T}} |
| 10 | + hidx_to_psp_proj::Vector{Vector{Tuple{Int,Int}}} # map per angular momentum and projector index of this object to the tuple (psp index, projector index) |
| 11 | + coefficients::Vector{T} |
| 12 | + pseudos::Vector{P} |
| 13 | + identifier::String # String identifying the PSP |
| 14 | + description::String # Descriptive string |
| 15 | +end |
| 16 | + |
| 17 | +function PspLinComb(coefficients::Vector{T}, pseudos::Vector{<:NormConservingPsp}; |
| 18 | + description="psp linear combination") where {T} |
| 19 | + @assert length(coefficients) == length(pseudos) |
| 20 | + @assert sum(coefficients) ≈ one(eltype(coefficients)) |
| 21 | + @assert !any(p -> p isa PspLinComb, pseudos) |
| 22 | + |
| 23 | + # These assumptions we could lift, but we make tham to make our lifes easier |
| 24 | + @assert allequal(charge_ionic, pseudos) |
| 25 | + @assert allequal(has_valence_density, pseudos) |
| 26 | + @assert allequal(has_core_density, pseudos) |
| 27 | + @assert allequal(p -> p.lmax, pseudos) |
| 28 | + |
| 29 | + TT = promote_type(T, eltype(eltype(first(pseudos).h))) |
| 30 | + lmax = first(pseudos).lmax |
| 31 | + h = Matrix{TT}[] |
| 32 | + hidx_to_psp_proj = Vector{Tuple{Int,Int}}[] # (ipsp, iproj) |
| 33 | + for l in 0:lmax |
| 34 | + proj_l_total = sum(p -> count_n_proj_radial(p, l), pseudos) |
| 35 | + hl = zeros(T, proj_l_total, proj_l_total) |
| 36 | + splits = Tuple{Int,Int}[] # (ipsp, iproj) |
| 37 | + |
| 38 | + for (ipsp, p) in enumerate(pseudos) |
| 39 | + iproj_offset = isempty(splits) ? 0 : last(splits)[2] |
| 40 | + rnge = iproj_offset .+ (1:count_n_proj_radial(p, l)) |
| 41 | + # TODO Is this the correct scaling of the coefficient in h ? |
| 42 | + hl[rnge, rnge] = p.h[l+1] * coefficients[ipsp] |
| 43 | + append!(splits, [(ipsp, iproj) for iproj in 1:count_n_proj_radial(p, l)]) |
| 44 | + end |
| 45 | + push!(h, hl) |
| 46 | + push!(hidx_to_psp_proj, splits) |
| 47 | + end |
| 48 | + @assert length(h) == length(hidx_to_psp_proj) == lmax+1 |
| 49 | + |
| 50 | + identifier = "" |
| 51 | + PspLinComb(lmax, h, hidx_to_psp_proj, coefficients, pseudos, identifier, description) |
| 52 | +end |
| 53 | + |
| 54 | +charge_ionic(psp::PspLinComb) = charge_ionic(first(psp.pseudos)) |
| 55 | +has_valence_density(psp::PspLinComb) = all(has_valence_density, psp.pseudos) |
| 56 | +has_core_density(psp::PspLinComb) = any(has_core_density, psp.pseudos) |
| 57 | + |
| 58 | +function eval_psp_projector_real(psp::PspLinComb, i, l, r::Real) |
| 59 | + (ipsp, iproj) = psp.hidx_to_psp_proj[l+1][i] |
| 60 | + eval_psp_projector_real(psp.pseudos[ipsp], iproj, l, r) |
| 61 | +end |
| 62 | +function eval_psp_projector_fourier(psp, i, l, p::Real) |
| 63 | + (ipsp, iproj) = psp.hidx_to_psp_proj[l+1][i] |
| 64 | + eval_psp_projector_fourier(psp.pseudos[ipsp], iproj, l, p) |
| 65 | +end |
| 66 | + |
| 67 | +function eval_psp_energy_correction(T, psp::PspLinComb) |
| 68 | + sum(c * eval_psp_energy_correction(T, p) for (c, p) in zip(psp.coefficients, psp.pseudos)) |
| 69 | +end |
| 70 | + |
| 71 | +macro make_psplincomb_call(fn) |
| 72 | + quote |
| 73 | + function $fn(psp::PspLinComb, arg::Real) |
| 74 | + sum(c * $fn(pp, arg) for (c, pp) in zip(psp.coefficients, psp.pseudos)) |
| 75 | + end |
| 76 | + end |
| 77 | +end |
| 78 | +@make_psplincomb_call DFTK.eval_psp_local_real |
| 79 | +@make_psplincomb_call DFTK.eval_psp_local_fourier |
| 80 | +@make_psplincomb_call DFTK.eval_psp_density_valence_real |
| 81 | +@make_psplincomb_call DFTK.eval_psp_density_valence_fourier |
| 82 | +@make_psplincomb_call DFTK.eval_psp_density_core_real |
| 83 | +@make_psplincomb_call DFTK.eval_psp_density_core_fourier |
0 commit comments