Skip to content

Commit 0d69edd

Browse files
committed
Update to Wigner_sym, now wigner_d_matrix and relative test
1 parent 5aefec2 commit 0d69edd

File tree

6 files changed

+93
-74
lines changed

6 files changed

+93
-74
lines changed

src/common/spherical_harmonics.jl

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,40 @@ function ylm_real(l::Integer, m::Integer, rvec::AbstractVector{T}) where {T}
4747

4848
error("The case l = $l and m = $m is not implemented")
4949
end
50+
51+
"""
52+
This function returns the Wigner D matrix for real spherical harmonics,
53+
for a given l and symmetry operation, solving a randomized linear system.
54+
Such matrix gives the decomposition of a spherical harmonic after application
55+
of a symmetry operation back into the basis of spherical harmonics.
56+
57+
Yₗₘ₁(R̂r) = Σₘ₂ D(l,R̂)ₘ₁ₘ₂ * Yₗₘ₂(r)
58+
59+
The lattice is needed to convert reduced symmetries to Cartesian space.
60+
"""
61+
function wigner_d_matrix(l::Integer, Wcart::AbstractMatrix{T}) where {T}
62+
D = Matrix{T}(undef, 2*l+1, 2*l+1)
63+
if l == 0
64+
return D .= 1
65+
end
66+
rng = Xoshiro(1234)
67+
neq = 4*(2*l+1)
68+
for m1 in -l:l
69+
b = Vector{T}(undef, neq)
70+
A = Matrix{T}(undef, neq, 2*l+1)
71+
for n in 1:neq
72+
r = rand(rng, T, 3)
73+
r = r / norm(r)
74+
r0 = Wcart * r
75+
b[n] = DFTK.ylm_real(l, m1, r0)
76+
for m2 in -l:l
77+
A[n,m2+l+1] = DFTK.ylm_real(l, m2, r)
78+
end
79+
end
80+
κ = cond(A)
81+
@assert κ < 10.0 "The Wigner matrix computation is badly conditioned. κ(A)=$(κ)"
82+
D[m1+l+1,:] = A\b
83+
end
84+
85+
return D
86+
end

src/postprocess/band_structure.jl

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,9 @@ function compute_bands(scfres::NamedTuple,
6868
n_bands=default_n_bands_bandstructure(scfres), kwargs...)
6969
τ = haskey(scfres, ) ? scfres.τ : nothing
7070
nhubbard = haskey(scfres, :nhubbard) ? scfres.nhubbard : nothing
71-
occupation = scfres.occupation
72-
compute_bands(scfres.basis, kgrid; scfres.ρ, τ, nhubbard, occupation, scfres.εF, n_bands, kwargs...)
71+
compute_bands(scfres.basis, kgrid;
72+
scfres.ρ, τ, nhubbard, scfres.occupation,
73+
scfres.εF, n_bands, kwargs...)
7374
end
7475

7576
"""

src/postprocess/dos.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ function atomic_orbital_projectors(basis::PlaneWaveBasis{T};
166166
labels = []
167167
for (iatom, atom) in enumerate(basis.model.atoms)
168168
psp = atom.psp
169-
@assert !iszero(size(psp.r2_pswfcs[1], 1)) "FATAL ERROR: No Atomic projector found within the provided PseudoPotential."
169+
count_n_pswfc(psp)
170170
for l in 0:psp.lmax, n in 1:DFTK.count_n_pswfc_radial(psp, l)
171171
label = DFTK.pswfc_label(psp, n, l)
172172
if !isonmanifold((; iatom, atom.species, label))

src/scf/self_consistent_field.jl

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -179,8 +179,8 @@ Overview of parameters:
179179
if any(needs_τ, basis.terms)
180180
τ = compute_kinetic_energy_density(basis, ψ, occupation)
181181
end
182-
for (iterm, term) in enumerate(basis.terms)
183-
if typeof(term)==DFTK.TermHubbard{Vector{Matrix{ComplexF64}}, Vector{Any}}
182+
for term in basis.terms
183+
if isa(term, DFTK.TermHubbard)
184184
nhubbard = compute_nhubbard(term.manifold, basis, ψ, occupation; projectors=term.P, labels=term.labels).nhubbard
185185
end
186186
end
@@ -190,7 +190,6 @@ Overview of parameters:
190190
ρin, τ, nhubbard, α=damping, n_iter, nbandsalg.occupation_threshold,
191191
runtime_ns=time_ns() - start_ns, nextstate...,
192192
diagonalization=[nextstate.diagonalization])
193-
@show nhubbard
194193

195194
# Compute the energy of the new state
196195
if compute_consistent_energies

src/terms/hubbard.jl

Lines changed: 15 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -59,15 +59,14 @@ end
5959
"""
6060
Symmetrize the Hubbard occupation matrix according to the l quantum number of the manifold.
6161
"""
62-
function symmetrize_nhub(nhubbard::Array{Matrix{Complex{T}}}, lattice, symmetry, positions) where {T}
62+
function symmetrize_nhub(nhubbard::Array{Matrix{Complex{T}}}, lattice, symmetries, positions) where {T}
6363
# For now we apply symmetries only on nII terms, not on cross-atom terms (nIJ)
6464
# WARNING: To implement +V this will need to be changed!
6565

6666
nspins = size(nhubbard, 1)
6767
natoms = size(nhubbard, 2)
68-
nsym = length(symmetry)
68+
nsym = length(symmetries)
6969
l = Int64((size(nhubbard[1, 1, 1], 1)-1)/2)
70-
WigD = Wigner_sym(l, lattice, symmetry)
7170

7271
# Initialize the nhubbard matrix
7372
ns = Array{Matrix{Complex{T}}}(undef, nspins, natoms, natoms)
@@ -77,52 +76,22 @@ function symmetrize_nhub(nhubbard::Array{Matrix{Complex{T}}}, lattice, symmetry,
7776
size(nhubbard[σ, iatom, jatom], 2))
7877
end
7978

80-
for σ in 1:nspins, iatom in 1:natoms, isym in 1:nsym
81-
for m1 in 1:size(ns[σ, iatom, iatom], 1), m2 in 1:size(ns[σ, iatom, iatom], 2)
82-
sym_atom = find_symmetry_preimage(positions, positions[iatom], symmetry[isym])
83-
# TODO: Here QE flips spin for time-reversal in collinear systems, should we?
84-
for m0 in 1:size(nhubbard[σ, iatom, iatom], 1), m00 in 1:size(nhubbard[σ, iatom, iatom], 2)
85-
ns[σ, iatom, iatom][m1, m2] += WigD[m0, m1, isym] *
86-
nhubbard[σ, sym_atom, sym_atom][m0, m00] *
87-
WigD[m00, m2, isym]
88-
end
89-
end
90-
end
91-
ns .= ns / nsym
92-
end
93-
94-
"""
95-
This function returns the Wigner matrix for a given l and symmetry operation
96-
solving a randomized linear system.
97-
The lattice L is needed to convert reduced symmetries to Cartesian space.
98-
"""
99-
function Wigner_sym(l::Int64, L, symmetries::Vector{SymOp{T}}) where {T}
100-
nsym = length(symmetries)
101-
D = Array{Float64}(undef, 2*l+1, 2*l+1, nsym)
102-
if l == 0
103-
return D .= 1
104-
end
105-
Random.seed!(1234)
106-
for (isym, symmetry) in enumerate(symmetries)
107-
W = symmetry.W
108-
for m1 in -l:l
109-
b = Vector{Float64}(undef, 2*l+1)
110-
A = Matrix{Float64}(undef, 2*l+1, 2*l+1)
111-
for n in 1:2*l+1
112-
r = rand(Float64, 3)
113-
r = r / norm(r)
114-
r0 = L * W * inv(L) * r
115-
b[n] = DFTK.ylm_real(l, m1, r0)
116-
for m2 in -l:l
117-
A[n,m2+l+1] = DFTK.ylm_real(l, m2, r)
79+
for symmetry in symmetries
80+
Wcart = lattice * symmetry.W * inv(lattice)
81+
WigD = wigner_d_matrix(l, Wcart)
82+
for σ in 1:nspins, iatom in 1:natoms
83+
sym_atom = find_symmetry_preimage(positions, positions[iatom], symmetry)
84+
for m1 in 1:size(ns[σ, iatom, iatom], 1), m2 in 1:size(ns[σ, iatom, iatom], 2)
85+
# TODO: Here QE flips spin for time-reversal in collinear systems, should we?
86+
for m0 in 1:size(nhubbard[σ, iatom, iatom], 1), m00 in 1:size(nhubbard[σ, iatom, iatom], 2)
87+
ns[σ, iatom, iatom][m1, m2] += WigD[m0, m1] *
88+
nhubbard[σ, sym_atom, sym_atom][m0, m00] *
89+
WigD[m00, m2]
11890
end
11991
end
120-
@assert cond(A) > 1/2 "The Wigner matrix computaton is badly conditioned."
121-
D[m1+l+1,:,isym] = A\b
12292
end
12393
end
124-
125-
return D
94+
ns .= ns / nsym
12695
end
12796

12897
"""
@@ -243,7 +212,7 @@ struct Hubbard
243212
U
244213
end
245214
function (hubbard::Hubbard)(basis::AbstractBasis)
246-
isempty(hubbard.U) && return TermNoop()
215+
iszero(hubbard.U) && return TermNoop()
247216
projs, labs = atomic_orbital_projectors(basis)
248217
labels, projectors = extract_manifold(basis, projs, labs, hubbard.manifold)
249218
U = austrip(hubbard.U)

test/hubbard.jl

Lines changed: 35 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,38 @@
1+
@testitem "Test Wigner matrices on Silicon symmetries" setup=[TestCases] begin
2+
using DFTK
3+
using PseudoPotentialData
4+
using LinearAlgebra
5+
6+
# Identity
7+
Id = Float64[1 0 0; 0 1 0; 0 0 1]
8+
D = DFTK.wigner_d_matrix(1, Id)
9+
@test norm(D - I) < 1e-8
10+
D = DFTK.wigner_d_matrix(2, Id)
11+
@test norm(D - I) < 1e-8
12+
# This reverts all p orbitals, sends all d orbitals in themselves
13+
Inv = -Id
14+
D = DFTK.wigner_d_matrix(1, Inv)
15+
@test norm(D + I) < 1e-8
16+
D = DFTK.wigner_d_matrix(2, Inv)
17+
@test norm(D - I) < 1e-8
18+
# This keeps pz, dz2, dx2-y2 and dxy unchanged, changes sign to all others
19+
A3 = Float64[1 0 0; 0 -1 0; 0 0 -1]
20+
D3p = Float64[-1 0 0; 0 -1 0; 0 0 1]
21+
D3d = Float64[-1 0 0 0 0; 0 1 0 0 0; 0 0 1 0 0; 0 0 0 -1 0; 0 0 0 0 1]
22+
D = DFTK.wigner_d_matrix(1, A3)
23+
@test norm(D - D3p) < 1e-8
24+
D = DFTK.wigner_d_matrix(2, A3)
25+
@test norm(D - D3d) < 1e-8
26+
# This sends: px <-> py, dxz <-> dyz, dx2-y2 -> -(dx2-y2) and keeps the other fixed
27+
A3 = Float64[0 1 0; 1 0 0; 0 0 1]
28+
D3p = Float64[0 0 1; 0 1 0; 1 0 0]
29+
D3d = Float64[1 0 0 0 0; 0 0 0 1 0; 0 0 1 0 0; 0 1 0 0 0; 0 0 0 0 -1]
30+
D = DFTK.wigner_d_matrix(1, A3)
31+
@test norm(D - D3p) < 1e-8
32+
D = DFTK.wigner_d_matrix(2, A3)
33+
@test norm(D - D3d) < 1e-8
34+
end
35+
136
@testitem "Test Hubbard U term in Nickel Oxide" setup=[TestCases] begin
237
using DFTK
338
using PseudoPotentialData
@@ -50,25 +85,3 @@
5085
@test norm(n_hub .- scfres_nosym.nhubbard) < 1e-8
5186

5287
end
53-
54-
@testitem "Test Wigner matrices on Silicon symmetries" setup=[TestCases] begin
55-
using DFTK
56-
using PseudoPotentialData
57-
using LinearAlgebra
58-
59-
lattice = [[0 1 1.];
60-
[1 0 1.];
61-
[1 1 0.]]
62-
positions = [ones(3)/8, -ones(3)/8]
63-
pseudopotentials = PseudoFamily("dojo.nc.sr.pbe.v0_4_1.standard.upf")
64-
Si = ElementPsp(:Si, pseudopotentials)
65-
atoms = [Si, Si]
66-
model = model_DFT(lattice, atoms, positions; functionals=PBE())
67-
basis = PlaneWaveBasis(model; Ecut=32, kgrid=[2, 2, 2])
68-
69-
D = DFTK.Wigner_sym(1, lattice, basis.symmetries)
70-
D5 = [-1 0 0; 0 -1 0; 0 0 1]
71-
@test norm(D[:,:,1] - I) < 1e-8
72-
@test norm(D[:,:,25] + I) < 1e-8
73-
@test norm(D[:,:,5] - D5) < 1e-8
74-
end

0 commit comments

Comments
 (0)