Skip to content

Commit 1345936

Browse files
committed
big squash
1 parent b9b052e commit 1345936

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+822
-405
lines changed

docs/src/developer/data_structures.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,10 +140,13 @@ For example let us check the normalization of the first eigenfunction
140140
at the first ``k``-point in reciprocal space:
141141

142142
```@example data_structures
143-
ψtest = scfres.ψ[1][:, 1]
143+
ψtest = scfres.ψ[1][:, :, 1]
144144
sum(abs2.(ψtest))
145145
```
146146

147+
The first index of `ψtest` is the component (e.g., the spin) and the second is the
148+
``G``-component.
149+
147150
We now perform an IFFT to get ψ in real space. The ``k``-point has to be
148151
passed because ψ is expressed on the ``k``-dependent basis.
149152
Again the function is normalised:

examples/custom_potential.jl

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,9 @@ compute_forces(scfres)
7272
tot_local_pot = DFTK.total_local_potential(scfres.ham)[:, 1, 1]; # use only dimension 1
7373

7474
# Extract other quantities before plotting them
75-
ρ = scfres.ρ[:, 1, 1, 1] # converged density, first spin component
76-
ψ_fourier = scfres.ψ[1][:, 1] # first k-point, all G components, first eigenvector
75+
ρ = scfres.ρ[:, 1, 1, 1] # converged density, first spin component
76+
ψ_fourier = scfres.ψ[1][1, :, 1] # first k-point, first (and only) component,
77+
# all G components, first eigenvector
7778

7879
# Transform the wave function to real space and fix the phase:
7980
ψ = ifft(basis, basis.kpoints[1], ψ_fourier)[:, 1, 1]

examples/error_estimates_forces.jl

Lines changed: 39 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -85,50 +85,60 @@ res, occ = DFTK.select_occupied_orbitals(basis_ref, res, scfres.occupation)
8585
# size ``N×N`` (``N`` being the number of electrons) whose solution is
8686
# ``U = S(S^*S)^{-1/2}`` where ``S`` is the overlap matrix ``ψ^*ϕ``.
8787
function compute_error(basis, ϕ, ψ)
88-
map(zip(ϕ, ψ)) do (ϕk, ψk)
88+
ϕ = to_composite_σG(basis, ϕ)
89+
ψ = to_composite_σG(basis, ψ)
90+
map(zip(basis.kpoints, ϕ, ψ)) do (kpt, ϕk, ψk)
8991
S = ψk'ϕk
9092
U = S*(S'S)^(-1/2)
91-
ϕk - ψk*U
93+
from_composite_σG(basis, kpt, ϕk - ψk*U)
9294
end
9395
end
9496
err = compute_error(basis_ref, ψr, ψ_ref);
9597

9698
# - Compute ``{\boldsymbol M}^{-1}R(P)`` with ``{\boldsymbol M}^{-1}`` defined in [^CDKL2021]:
9799
P = [PreconditionerTPA(basis_ref, kpt) for kpt in basis_ref.kpoints]
98-
map(zip(P, ψr)) do (Pk, ψk)
99-
DFTK.precondprep!(Pk, ψk)
100+
map(zip(P, ψr, basis_ref.kpoints)) do (Pk, ψk, kpt)
101+
DFTK.precondprep!(Pk, to_composite_σG(basis_ref, kpt, ψk))
100102
end
101-
function apply_M(φk, Pk, δφnk, n)
102-
DFTK.proj_tangent_kpt!(δφnk, φk)
103-
δφnk = sqrt.(Pk.mean_kin[n] .+ Pk.kin) .* δφnk
104-
DFTK.proj_tangent_kpt!(δφnk, φk)
105-
δφnk = sqrt.(Pk.mean_kin[n] .+ Pk.kin) .* δφnk
106-
DFTK.proj_tangent_kpt!(δφnk, φk)
103+
function apply_M(basis, kpt, φk, Pk, δφnk, n)
104+
fact = reshape(sqrt.(Pk.mean_kin[n] .+ Pk.kin), size(δφnk)...)
105+
DFTK.proj_tangent_kpt!(δφnk, basis, kpt, φk)
106+
δφnk = fact .* δφnk
107+
DFTK.proj_tangent_kpt!(δφnk, basis, kpt, φk)
108+
δφnk = fact .* δφnk
109+
DFTK.proj_tangent_kpt!(δφnk, basis, kpt, φk)
107110
end
108-
function apply_inv_M(φk, Pk, δφnk, n)
109-
DFTK.proj_tangent_kpt!(δφnk, φk)
110-
op(x) = apply_M(φk, Pk, x, n)
111+
function apply_inv_M(basis, kpt, φk, Pk, δφnk, n)
112+
DFTK.proj_tangent_kpt!(δφnk, basis, kpt, φk)
113+
op(x) = let
114+
x = from_composite_σG(basis, kpt, x)
115+
to_composite_σG(basis, kpt, apply_M(basis, kpt, φk, Pk, x, n))
116+
end
111117
function f_ldiv!(x, y)
112-
x .= DFTK.proj_tangent_kpt(y, φk)
118+
x_σG = from_composite_σG(basis, kpt, x)
119+
y_σG = from_composite_σG(basis, kpt, y)
120+
x_σG .= DFTK.proj_tangent_kpt(basis, kpt, y_σG, φk)
113121
x ./= (Pk.mean_kin[n] .+ Pk.kin)
114-
DFTK.proj_tangent_kpt!(x, φk)
122+
DFTK.proj_tangent_kpt!(x_σG, basis, kpt, φk)
123+
x
115124
end
116-
J = LinearMap{eltype(φk)}(op, size(δφnk, 1))
117-
δφnk = cg(J, δφnk; Pl=DFTK.FunctionPreconditioner(f_ldiv!),
118-
verbose=false, reltol=0, abstol=1e-15)
119-
DFTK.proj_tangent_kpt!(δφnk, φk)
125+
J = LinearMap{eltype(φk)}(op, prod(size(δφnk)[1:2]))
126+
δφnk_vec = to_composite_σG(basis, kpt, δφnk)
127+
δφnk_vec = cg(J, δφnk_vec; Pl=DFTK.FunctionPreconditioner(f_ldiv!),
128+
verbose=false, reltol=0, abstol=1e-15)
129+
DFTK.proj_tangent_kpt!(δφnk, basis, kpt, φk)
120130
end
121-
function apply_metric(φ, P, δφ, A::Function)
131+
function apply_metric(basis, φ, P, δφ, A::Function)
122132
map(enumerate(δφ)) do (ik, δφk)
123133
Aδφk = similar(δφk)
124134
φk = φ[ik]
125-
for n = 1:size(δφk,2)
126-
Aδφk[:,n] = A(φk, P[ik], δφk[:,n], n)
135+
for n = 1:size(δφk, 3)
136+
Aδφk[:, :, n] = A(basis, basis.kpoints[ik], φk, P[ik], δφk[:, :, n], n)
127137
end
128138
Aδφk
129139
end
130140
end
131-
Mres = apply_metric(ψr, P, res, apply_inv_M);
141+
Mres = apply_metric(basis, ψr, P, res, apply_inv_M);
132142

133143
# We can now compute the modified residual ``R_{\rm Schur}(P)`` using a Schur
134144
# complement to approximate the error on low-frequencies[^CDKL2021]:
@@ -151,14 +161,14 @@ resLF = DFTK.transfer_blochwave(res, basis_ref, basis)
151161
resHF = res - DFTK.transfer_blochwave(resLF, basis, basis_ref);
152162

153163
# - Compute ``{\boldsymbol M}^{-1}_{22}R_2(P)``:
154-
e2 = apply_metric(ψr, P, resHF, apply_inv_M);
164+
e2 = apply_metric(basis, ψr, P, resHF, apply_inv_M);
155165

156166
# - Compute the right hand side of the Schur system:
157167
## Rayleigh coefficients needed for `apply_Ω`
158-
Λ = map(enumerate(ψr)) do (ik, ψk)
159-
Hk = hamr.blocks[ik]
160-
Hψk = Hk * ψk
161-
ψk'Hψk
168+
Λ = map(enumerate(zip(ψr, hamr * ψr))) do (ik, (ψk, Hψk))
169+
ψk = to_composite_σG(basis, basis.kpoints[ik], ψk)
170+
Hψk = to_composite_σG(basis, basis.kpoints[ik], Hψk)
171+
from_composite_σG(basis, basis.kpoints[ik], ψk'Hψk)
162172
end
163173
ΩpKe2 = DFTK.apply_Ω(e2, ψr, hamr, Λ) .+ DFTK.apply_K(basis_ref, e2, ψr, ρr, occ)
164174
ΩpKe2 = DFTK.transfer_blochwave(ΩpKe2, basis_ref, basis)
@@ -204,7 +214,7 @@ end;
204214
# the actual error ``P-P_*``. Usually this is of course not the case, but this
205215
# is the "best" improvement we can hope for with a linearisation, so we are
206216
# aiming for this precision.
207-
df_err = df(basis_ref, occ, ψr, DFTK.proj_tangent(err, ψr), ρr)
217+
df_err = df(basis_ref, occ, ψr, DFTK.proj_tangent(basis, err, ψr), ρr)
208218
forces["F(P) - df(P)⋅(P-P_*)"] = f - df_err
209219
relerror["F(P) - df(P)⋅(P-P_*)"] = compute_relerror(f - df_err);
210220

examples/gross_pitaevskii.jl

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,9 @@ scfres.energies
5757
# We use the opportunity to explore some of DFTK internals.
5858
#
5959
# Extract the converged density and the obtained wave function:
60-
ρ = real(scfres.ρ)[:, 1, 1, 1] # converged density, first spin component
61-
ψ_fourier = scfres.ψ[1][:, 1]; # first k-point, all G components, first eigenvector
60+
ρ = real(scfres.ρ)[:, 1, 1, 1] # converged density, first spin component
61+
ψ_fourier = scfres.ψ[1][1, :, 1] # first k-point, first (and only) component,
62+
# all G components, first eigenvector
6263

6364
# Transform the wave function to real space and fix the phase:
6465
ψ = ifft(basis, basis.kpoints[1], ψ_fourier)[:, 1, 1]
@@ -93,7 +94,7 @@ E, ham = energy_hamiltonian(basis, scfres.ψ, scfres.occupation; ρ=scfres.ρ)
9394
H = ham.blocks[1];
9495

9596
# `H` can be used as a linear operator (efficiently using FFTs), or converted to a dense matrix:
96-
ψ11 = scfres.ψ[1][:, 1] # first k-point, first eigenvector
97+
ψ11 = scfres.ψ[1][1, :, 1] # first k-point, first component, first eigenvector
9798
Hmat = Array(H) # This is now just a plain Julia matrix,
9899
## which we can compute and store in this simple 1D example
99100
@assert norm(Hmat * ψ11 - H * ψ11) < 1e-10
@@ -107,4 +108,4 @@ A[1, end] = A[end, 1] = -1
107108
K = A / dx^2 / 2
108109
V = Diagonal(pot.(x) + C .* α .*.^-1)))
109110
H_findiff = K + V;
110-
maximum(abs.(H_findiff*ψ - (dot(ψ, H_findiff*ψ) / dot(ψ, ψ)) * ψ))
111+
maximum(abs, H_findiff*ψ - (dot(ψ, H_findiff*ψ) / dot(ψ, ψ)) * ψ)

ext/DFTKGenericLinearAlgebraExt/DFTKGenericLinearAlgebraExt.jl

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,29 +29,29 @@ end
2929
DFTK.default_primes(::Any) = (2, )
3030

3131
# Generic fallback function, Float32 and Float64 specialization in fft.jl
32-
function DFTK.build_fft_plans!(tmp::AbstractArray{<:Complex})
32+
function DFTK.build_fft_plans!(tmp::AbstractArray{<:Complex}; region=1:ndims(tmp))
3333
# Note: FourierTransforms has no support for in-place FFTs at the moment
3434
# ... also it's extension to multi-dimensional arrays is broken and
3535
# the algo only works for some cases
3636
@assert all(ispow2, size(tmp))
3737

38-
# opFFT = AbstractFFTs.plan_fft(tmp) # TODO When multidim works
38+
# opFFT = AbstractFFTs.plan_fft(tmp) # TODO When multidim works
3939
# opBFFT = inv(opFFT).p
40-
opFFT = generic_plan_fft(tmp) # Fallback for now
40+
opFFT = generic_plan_fft(tmp) # Fallback for now
4141
opBFFT = generic_plan_bfft(tmp)
4242
# TODO Can be cut once FourierTransforms supports AbstractFFTs properly
4343
ipFFT = DummyInplace{typeof(opFFT)}(opFFT)
4444
ipBFFT = DummyInplace{typeof(opBFFT)}(opBFFT)
4545

46-
ipFFT, opFFT, ipBFFT, opBFFT
46+
(; ipFFT, opFFT, ipBFFT, opBFFT)
4747
end
4848

4949
struct GenericPlan{T}
5050
subplans
5151
factor::T
5252
end
5353

54-
function Base.:*(p::GenericPlan, X::AbstractArray)
54+
function Base.:*(p::GenericPlan, X::AbstractArray{T, 3}) where {T}
5555
pl1, pl2, pl3 = p.subplans
5656
ret = similar(X)
5757
for i = 1:size(X, 1), j = 1:size(X, 2)
@@ -86,4 +86,18 @@ function generic_plan_bfft(data::AbstractArray{T, 3}) where {T}
8686
plan_bfft(data[1, 1, :])], T(1))
8787
end
8888

89+
# TODO: Let's not bother with multicomponents yet.
90+
function generic_plan_fft(data::AbstractArray{T, 4}) where {T}
91+
@assert size(data, 1) == 1
92+
generic_plan_fft(data[1, :, :, :])
93+
end
94+
function generic_plan_bfft(data::AbstractArray{T, 4}) where {T}
95+
@assert size(data, 1) == 1
96+
generic_plan_bfft(data[1, :, :, :])
97+
end
98+
function Base.:*(p::GenericPlan, X::AbstractArray{T, 4}) where {T}
99+
@assert size(X, 1) == 1
100+
reshape(p * X[1, :, :, :], size(X)...)
101+
end
102+
89103
end

ext/DFTKIntervalArithmeticExt.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,8 @@ function _is_well_conditioned(A::AbstractArray{<:Interval}; kwargs...)
4242
end
4343

4444
function symmetry_operations(lattice::AbstractMatrix{<:Interval}, atoms, positions,
45-
magnetic_moments=[];
46-
tol_symmetry=max(SYMMETRY_TOLERANCE, maximum(radius, lattice)))
45+
magnetic_moments=[];
46+
tol_symmetry=max(SYMMETRY_TOLERANCE, maximum(radius, lattice)))
4747
@assert tol_symmetry < 1e-2
4848
symmetry_operations(IntervalArithmetic.mid.(lattice), atoms, positions, magnetic_moments;
4949
tol_symmetry)

ext/DFTKJLD2Ext.jl

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,12 @@ function load_scfres_jld2(jld, basis; skip_hamiltonian, strict)
8989
"($(jld["n_spin_components"])) and supplied basis " *
9090
"($(basis.model.n_spin_components))")
9191
end
92+
if jld["n_components"] != basis.model.n_components
93+
consistent_kpts = false
94+
errormsg = ("Mismatch in number of spin components between file " *
95+
"($(jld["n_components"])) and supplied basis " *
96+
"($(basis.model.n_components))")
97+
end
9298
end
9399
errormsg = MPI.bcast(errormsg, 0, MPI.COMM_WORLD)
94100
isempty(errormsg) || error(errormsg)

ext/DFTKWriteVTKExt.jl

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,14 @@ function DFTK.save_scfres(::Val{:vts}, filename::AbstractString, scfres::NamedTu
2424
ψblock = gather_kpts_block(scfres.basis, ψblock_dist)
2525

2626
if mpi_master()
27-
for ik = 1:length(basis.kpoints), n = 1:size(ψblock, 2)
28-
kpt_n_G = length(G_vectors(basis, basis.kpoints[ik]))
29-
ψnk_real = ifft(basis, basis.kpoints[ik], ψblock[1:kpt_n_G, n, ik])
30-
prefix = @sprintf "ψ_k%03i_n%03i" ik n
31-
vtkfile["$(prefix)_real"] = real.(ψnk_real)
32-
vtkfile["$(prefix)_imag"] = imag.(ψnk_real)
27+
for ik = 1:length(basis.kpoints)
28+
for σ = 1:basis.model.n_components, n = 1:size(ψblock, 3)
29+
kpt_n_G = length(G_vectors(basis, basis.kpoints[ik]))
30+
ψσnk_real = ifft(basis, basis.kpoints[ik], ψblock[:, 1:kpt_n_G, n, ik])
31+
prefix = @sprintf "ψ_k%03i_n%03i_σ%03i" ik n σ
32+
vtkfile["$(prefix)_real"] = real.(ψσnk_real)
33+
vtkfile["$(prefix)_imag"] = imag.(ψσnk_real)
34+
end
3335
end
3436
end
3537
end

src/DFTK.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ export compute_fft_size
7575
export G_vectors, G_vectors_cart, r_vectors, r_vectors_cart
7676
export Gplusk_vectors, Gplusk_vectors_cart
7777
export Kpoint
78+
export to_composite_σG
79+
export from_composite_σG
7880
export ifft
7981
export irfft
8082
export ifft!
@@ -86,6 +88,7 @@ include("Model.jl")
8688
include("structure.jl")
8789
include("bzmesh.jl")
8890
include("PlaneWaveBasis.jl")
91+
include("Psi.jl")
8992
include("fft.jl")
9093
include("orbitals.jl")
9194
include("input_output.jl")

src/Model.jl

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ struct Model{T <: Real, VT <: Real}
2626
n_electrons::Union{Int, Nothing}
2727
εF::Union{T, Nothing}
2828

29+
# Option for multicomponents computations.
30+
# TODO: Planed to replace current handling of spins, as it will allow full spin computations.
31+
n_components::Int
32+
2933
# spin_polarization values:
3034
# :none No spin polarization, αα and ββ density identical,
3135
# αβ and βα blocks zero, 1 spin component treated explicitly
@@ -106,7 +110,8 @@ function Model(lattice::AbstractMatrix{T},
106110
terms=[Kinetic()],
107111
temperature=zero(T),
108112
smearing=temperature > 0 ? Smearing.FermiDirac() : Smearing.None(),
109-
spin_polarization=determine_spin_polarization(magnetic_moments),
113+
n_components=1,
114+
spin_polarization=default_spin_polarization(magnetic_moments, n_components),
110115
symmetries=default_symmetries(lattice, atoms, positions, magnetic_moments,
111116
spin_polarization, terms),
112117
) where {T <: Real}
@@ -178,7 +183,8 @@ function Model(lattice::AbstractMatrix{T},
178183
Model{T,value_type(T)}(model_name,
179184
lattice, recip_lattice, n_dim, inv_lattice, inv_recip_lattice,
180185
unit_cell_volume, recip_cell_volume,
181-
n_electrons, εF, spin_polarization, n_spin, temperature, smearing,
186+
n_electrons, εF, n_components, spin_polarization, n_spin,
187+
temperature, smearing,
182188
atoms, positions, atom_groups, terms, symmetries)
183189
end
184190
function Model(lattice::AbstractMatrix{<:Integer}, atoms::Vector{<:Element},
@@ -224,6 +230,7 @@ function Model{T}(model::Model;
224230
model.temperature,
225231
model.smearing,
226232
model.εF,
233+
model.n_components,
227234
model.spin_polarization,
228235
model.symmetries,
229236
# Can be safely disabled: this has been checked for model
@@ -249,6 +256,11 @@ normalize_magnetic_moment(mm::AbstractVector)::Vec3{Float64} = mm
249256
"""Number of valence electrons."""
250257
n_electrons_from_atoms(atoms) = sum(n_elec_valence, atoms; init=0)
251258

259+
function default_spin_polarization(magnetic_moments, n_components)
260+
n_components > 1 && return :spinless
261+
determine_spin_polarization(magnetic_moments)
262+
end
263+
252264
"""
253265
Default logic to determine the symmetry operations to be used in the model.
254266
"""

0 commit comments

Comments
 (0)