Skip to content

Commit 3aecab1

Browse files
committed
[wien2k] pure-Python case.symqmc generator (first dmftproj port step)
triqs_dftkit.wien2k.symqmc.write_symqmc builds the correlated-shell spinor symmetry matrices (case.symqmc) from case.dmftsym + case.indmftpr + case.struct, reproducing the dmftproj Fortran construction (Wigner D, orbital time-reversal operator, angular-harmonics basis transform, spin-1/2 phase blocks) for the non-mixing spin-diagonal spin-orbit path. Verified against the Fortran output: the converter HDF5 is identical (h5diff) and the matrices match to machine precision. Adds a regression test reusing the SOC golden reference.
1 parent 830cb84 commit 3aecab1

5 files changed

Lines changed: 356 additions & 0 deletions

File tree

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
################################################################################
2+
#
3+
# TRIQS: a Toolbox for Research in Interacting Quantum Systems
4+
#
5+
# Copyright (C) 2011 by M. Aichhorn, L. Pourovskii, V. Vildosola
6+
#
7+
# TRIQS is free software: you can redistribute it and/or modify it under the
8+
# terms of the GNU General Public License as published by the Free Software
9+
# Foundation, either version 3 of the License, or (at your option) any later
10+
# version.
11+
#
12+
# TRIQS is distributed in the hope that it will be useful, but WITHOUT ANY
13+
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14+
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15+
# details.
16+
#
17+
# You should have received a copy of the GNU General Public License along with
18+
# TRIQS. If not, see <http://www.gnu.org/licenses/>.
19+
#
20+
################################################################################
21+
22+
"""Pure-Python generation of the dmftproj correlated-shell symmetry file
23+
(case.symqmc), replacing the corresponding output of the dmftproj Fortran
24+
executable.
25+
26+
Given the dmftproj symmetry input (case.dmftsym), the projector definition
27+
(case.indmftpr) and the structure (case.struct), this builds the spinor symmetry
28+
matrices and writes them in the case.symqmc format the converter reads. It
29+
reproduces the Fortran construction: the Wigner D matrix (dmat), the orbital
30+
time-reversal operator for the magnetic (SP+SO) operations, the basis transform
31+
to the chosen angular harmonics, and the spin-1/2 phase blocks.
32+
33+
Currently covers the non-mixing spin-diagonal bases (complex, cubic) with
34+
spin-orbit; this is the path used by the Wien2k SOC + spin-polarized workflow.
35+
"""
36+
37+
import math
38+
import numpy as np
39+
40+
# --- angular bases (transpose(P) = <new|m>, m = -l..l) -----------------------
41+
42+
_COMPLEX = {l: np.eye(2 * l + 1, dtype=complex) for l in range(4)}
43+
44+
# standard cubic harmonics, Wien2k convention
45+
_CUBIC = {
46+
1: np.array([[0, 1, 0], [-1j, 0, -1j], [1, 0, -1]], dtype=complex) / 1.0,
47+
2: np.array([
48+
[0, 0, 1, 0, 0],
49+
[2 ** -0.5, 0, 0, 0, 2 ** -0.5],
50+
[-(2 ** -0.5), 0, 0, 0, 2 ** -0.5],
51+
[0, 2 ** -0.5, 0, -(2 ** -0.5), 0],
52+
[0, 2 ** -0.5, 0, 2 ** -0.5, 0],
53+
], dtype=complex),
54+
}
55+
56+
57+
def _reptrans(basis, l):
58+
if basis == 'cubic' and l in _CUBIC:
59+
return _CUBIC[l]
60+
return _COMPLEX[l]
61+
62+
63+
# --- Wigner D matrix (dmftproj convention, setsym.f) -------------------------
64+
65+
def _small_d(l, m, n, b):
66+
f1 = (math.factorial(l + m) * math.factorial(l - m)) / \
67+
(math.factorial(l + n) * math.factorial(l - n))
68+
s = 0.0
69+
for t in range(0, 2 * l + 1):
70+
if (l - m - t) >= 0 and (l - n - t) >= 0 and (t + n + m) >= 0:
71+
f2 = (math.factorial(l + n) * math.factorial(l - n)) / \
72+
(math.factorial(l - m - t) * math.factorial(m + n + t) *
73+
math.factorial(l - n - t) * math.factorial(t))
74+
f3 = 1.0 if (2 * l - m - n - 2 * t) == 0 else math.sin(b / 2) ** (2 * l - m - n - 2 * t)
75+
f4 = 1.0 if (2 * t + n + m) == 0 else math.cos(b / 2) ** (2 * t + n + m)
76+
s += (-1) ** (l - m - t) * f2 * f3 * f4
77+
return math.sqrt(f1) * s
78+
79+
80+
def _dmat(l, a, b, c, det):
81+
D = np.zeros((2 * l + 1, 2 * l + 1), dtype=complex)
82+
for m in range(-l, l + 1):
83+
for n in range(-l, l + 1):
84+
v = np.exp(1j * n * a) * np.exp(1j * m * c) * _small_d(l, m, n, b)
85+
if det < -0.5:
86+
v *= (-1) ** l
87+
D[m + l, n + l] = v
88+
return D
89+
90+
91+
def _timeinv_orbital(l, mat):
92+
T = np.zeros((2 * l + 1, 2 * l + 1), dtype=complex)
93+
for m in range(-l, l + 1):
94+
T[-m + l, m + l] = (-1) ** m
95+
return T @ np.conj(mat)
96+
97+
98+
# --- input parsing -----------------------------------------------------------
99+
100+
def _read_dmftsym(path):
101+
lines = open(path).read().split('\n')
102+
nsym = int(lines[0].split()[0])
103+
perms = [[int(x) for x in lines[1 + i].split()] for i in range(nsym)]
104+
rest = lines[1 + nsym:]
105+
starts = [i for i, l in enumerate(rest) if 'Sym. op.' in l]
106+
ops = []
107+
for k, s in enumerate(starts):
108+
ang = rest[s + 1].split()
109+
a, b, c = (math.radians(float(x)) for x in ang[:3])
110+
krotm = np.array([[float(x) for x in rest[s + 2 + r].split()] for r in range(3)])
111+
ops.append(dict(perm=perms[k], a=a, b=b, c=c, krotm=krotm))
112+
return nsym, ops
113+
114+
115+
def _read_correlated_shells(indmftpr, struct):
116+
"""Return the list of correlated shells (l, basis), one entry per correlated
117+
atom, plus the SO flag, from case.indmftpr and case.struct multiplicities."""
118+
raw = [l.split('!')[0].strip() for l in open(indmftpr)]
119+
raw = [l for l in raw if l != '']
120+
nsort = int(raw[0].split()[0])
121+
mult = [int(x) for x in raw[1].split()][:nsort]
122+
i = 3
123+
so = 0
124+
shells = []
125+
for isort in range(nsort):
126+
basis = raw[i]
127+
i += 1
128+
l_inc = [int(x) for x in raw[i].split()]
129+
i += 1
130+
ireps = [int(x) for x in raw[i].split()]
131+
i += 1
132+
correlated_ls = [l for l in range(len(l_inc)) if l_inc[l] == 2]
133+
if any(n > 0 for n in ireps):
134+
i += 1 # skip the correps line
135+
if correlated_ls:
136+
so = int(raw[i].split()[0]) # SO flag follows a correlated sort
137+
i += 1
138+
for l in correlated_ls:
139+
for _ in range(mult[isort]):
140+
shells.append(dict(l=l, basis=basis))
141+
return shells, so
142+
143+
144+
def write_symqmc(case):
145+
"""Write <case>.symqmc from <case>.dmftsym, <case>.indmftpr, <case>.struct."""
146+
nsym, ops = _read_dmftsym(case + '.dmftsym')
147+
shells, so = _read_correlated_shells(case + '.indmftpr', case + '.struct')
148+
natom = len(ops[0]['perm'])
149+
150+
timeinv = []
151+
for op in ops:
152+
det2 = op['krotm'][0, 0] * op['krotm'][1, 1] - op['krotm'][0, 1] * op['krotm'][1, 0]
153+
timeinv.append(1 if (so and det2 < 0.0) else 0)
154+
155+
with open(case + '.symqmc', 'w') as f:
156+
f.write('%6d %6d\n' % (nsym, natom))
157+
for op in ops:
158+
f.write(''.join('%6d ' % p for p in op['perm']) + '\n')
159+
if so:
160+
f.write(''.join('%6d ' % t for t in timeinv) + '\n')
161+
for isym, op in enumerate(ops):
162+
for sh in shells:
163+
f.write(_format_matrix(_shell_matrix(op, sh, timeinv[isym])))
164+
165+
166+
def _shell_matrix(op, shell, ti):
167+
l, basis = shell['l'], shell['basis']
168+
a, b, c = op['a'], op['b'], op['c']
169+
det = np.linalg.det(op['krotm'])
170+
rotl = _dmat(l, a, b, c, det)
171+
if ti:
172+
rotl = _timeinv_orbital(l, rotl)
173+
P = _reptrans(basis, l)
174+
rotrep = P @ rotl @ np.conj(P.T)
175+
phase = (c - a) if ti else (a + c)
176+
e = np.exp(1j * phase / 2)
177+
d = 2 * l + 1
178+
mat = np.zeros((2 * d, 2 * d), dtype=complex)
179+
mat[:d, :d] = e * rotrep
180+
mat[d:, d:] = np.conj(e) * rotrep
181+
return mat
182+
183+
184+
def _format_matrix(mat):
185+
out = []
186+
for part in (mat.real, mat.imag):
187+
for row in part:
188+
out.append(''.join(' %.14E' % x for x in row) + '\n')
189+
return ''.join(out)

test/python/wien2k/CMakeLists.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,12 @@ add_test(NAME Py_wien2k_soc_convert
2323
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
2424
set_property(TEST Py_wien2k_soc_convert APPEND PROPERTY ENVIRONMENT
2525
PYTHONPATH=${PROJECT_BINARY_DIR}/python:$ENV{PYTHONPATH} ${SANITIZER_RT_PRELOAD})
26+
27+
# Pure-Python case.symqmc generator vs the dmftproj Fortran output (reuses the
28+
# SOC reference h5; adds only the small text inputs the generator reads).
29+
file(COPY CaOs2.dmftsym CaOs2.indmftpr DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
30+
add_test(NAME Py_wien2k_symqmc_python
31+
COMMAND ${TRIQS_PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/wien2k_symqmc_python.py
32+
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
33+
set_property(TEST Py_wien2k_symqmc_python APPEND PROPERTY ENVIRONMENT
34+
PYTHONPATH=${PROJECT_BINARY_DIR}/python:$ENV{PYTHONPATH} ${SANITIZER_RT_PRELOAD})

test/python/wien2k/CaOs2.dmftsym

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
16
2+
1 3 2
3+
1 2 3
4+
1 2 3
5+
1 3 2
6+
1 2 3
7+
1 3 2
8+
1 3 2
9+
1 2 3
10+
1 2 3
11+
1 3 2
12+
1 3 2
13+
1 2 3
14+
1 3 2
15+
1 2 3
16+
1 2 3
17+
1 3 2
18+
19+
Sym. op. : 1
20+
0.0 0.0 0.0 -1 - euler angles: a,b,c; iprop
21+
1.00000000000000 0.000000000000000E+000 0.000000000000000E+000
22+
0.000000000000000E+000 1.00000000000000 0.000000000000000E+000
23+
0.000000000000000E+000 0.000000000000000E+000 1.00000000000000
24+
25+
Sym. op. : 2
26+
270.0 0.0 0.0 -1 - euler angles: a,b,c; iprop
27+
0.000000000000000E+000 -1.00000000000000 0.000000000000000E+000
28+
1.00000000000000 0.000000000000000E+000 0.000000000000000E+000
29+
0.000000000000000E+000 0.000000000000000E+000 1.00000000000000
30+
31+
Sym. op. : 3
32+
90.0 0.0 0.0 -1 - euler angles: a,b,c; iprop
33+
0.000000000000000E+000 1.00000000000000 0.000000000000000E+000
34+
-1.00000000000000 0.000000000000000E+000 0.000000000000000E+000
35+
0.000000000000000E+000 0.000000000000000E+000 1.00000000000000
36+
37+
Sym. op. : 4
38+
180.0 0.0 0.0 -1 - euler angles: a,b,c; iprop
39+
-1.00000000000000 0.000000000000000E+000 0.000000000000000E+000
40+
0.000000000000000E+000 -1.00000000000000 0.000000000000000E+000
41+
0.000000000000000E+000 0.000000000000000E+000 1.00000000000000
42+
43+
Sym. op. : 5
44+
180.0 0.0 0.0 1 - euler angles: a,b,c; iprop
45+
-1.00000000000000 0.000000000000000E+000 0.000000000000000E+000
46+
0.000000000000000E+000 -1.00000000000000 0.000000000000000E+000
47+
0.000000000000000E+000 0.000000000000000E+000 1.00000000000000
48+
49+
Sym. op. : 6
50+
90.0 0.0 0.0 1 - euler angles: a,b,c; iprop
51+
0.000000000000000E+000 1.00000000000000 0.000000000000000E+000
52+
-1.00000000000000 0.000000000000000E+000 0.000000000000000E+000
53+
0.000000000000000E+000 0.000000000000000E+000 1.00000000000000
54+
55+
Sym. op. : 7
56+
270.0 0.0 0.0 1 - euler angles: a,b,c; iprop
57+
0.000000000000000E+000 -1.00000000000000 0.000000000000000E+000
58+
1.00000000000000 0.000000000000000E+000 0.000000000000000E+000
59+
0.000000000000000E+000 0.000000000000000E+000 1.00000000000000
60+
61+
Sym. op. : 8
62+
0.0 0.0 0.0 1 - euler angles: a,b,c; iprop
63+
1.00000000000000 0.000000000000000E+000 0.000000000000000E+000
64+
0.000000000000000E+000 1.00000000000000 0.000000000000000E+000
65+
0.000000000000000E+000 0.000000000000000E+000 1.00000000000000
66+
67+
Sym. op. : 9
68+
0.0 180.0 180.0 1 - euler angles: a,b,c; iprop
69+
1.00000000000000 0.000000000000000E+000 0.000000000000000E+000
70+
0.000000000000000E+000 -1.00000000000000 0.000000000000000E+000
71+
0.000000000000000E+000 0.000000000000000E+000 -1.00000000000000
72+
73+
Sym. op. : 10
74+
0.0 180.0 90.0 1 - euler angles: a,b,c; iprop
75+
0.000000000000000E+000 1.00000000000000 0.000000000000000E+000
76+
1.00000000000000 0.000000000000000E+000 0.000000000000000E+000
77+
0.000000000000000E+000 0.000000000000000E+000 -1.00000000000000
78+
79+
Sym. op. : 11
80+
0.0 180.0 270.0 1 - euler angles: a,b,c; iprop
81+
0.000000000000000E+000 -1.00000000000000 0.000000000000000E+000
82+
-1.00000000000000 0.000000000000000E+000 0.000000000000000E+000
83+
0.000000000000000E+000 0.000000000000000E+000 -1.00000000000000
84+
85+
Sym. op. : 12
86+
0.0 180.0 0.0 1 - euler angles: a,b,c; iprop
87+
-1.00000000000000 0.000000000000000E+000 0.000000000000000E+000
88+
0.000000000000000E+000 1.00000000000000 0.000000000000000E+000
89+
0.000000000000000E+000 0.000000000000000E+000 -1.00000000000000
90+
91+
Sym. op. : 13
92+
0.0 180.0 0.0 -1 - euler angles: a,b,c; iprop
93+
-1.00000000000000 0.000000000000000E+000 0.000000000000000E+000
94+
0.000000000000000E+000 1.00000000000000 0.000000000000000E+000
95+
0.000000000000000E+000 0.000000000000000E+000 -1.00000000000000
96+
97+
Sym. op. : 14
98+
0.0 180.0 270.0 -1 - euler angles: a,b,c; iprop
99+
0.000000000000000E+000 -1.00000000000000 0.000000000000000E+000
100+
-1.00000000000000 0.000000000000000E+000 0.000000000000000E+000
101+
0.000000000000000E+000 0.000000000000000E+000 -1.00000000000000
102+
103+
Sym. op. : 15
104+
0.0 180.0 90.0 -1 - euler angles: a,b,c; iprop
105+
0.000000000000000E+000 1.00000000000000 0.000000000000000E+000
106+
1.00000000000000 0.000000000000000E+000 0.000000000000000E+000
107+
0.000000000000000E+000 0.000000000000000E+000 -1.00000000000000
108+
109+
Sym. op. : 16
110+
0.0 180.0 180.0 -1 - euler angles: a,b,c; iprop
111+
1.00000000000000 0.000000000000000E+000 0.000000000000000E+000
112+
0.000000000000000E+000 -1.00000000000000 0.000000000000000E+000
113+
0.000000000000000E+000 0.000000000000000E+000 -1.00000000000000
114+
Global->local coordinates rotation matrices
115+
1
116+
1.00000000000000 0.000000000000000E+000 0.000000000000000E+000
117+
0.000000000000000E+000 1.00000000000000 0.000000000000000E+000
118+
0.000000000000000E+000 0.000000000000000E+000 1.00000000000000
119+
0.0 0.0 0.0 1 - euler angles: a,b,c; iprop
120+
2
121+
1.00000000000000 0.000000000000000E+000 0.000000000000000E+000
122+
0.000000000000000E+000 1.00000000000000 0.000000000000000E+000
123+
0.000000000000000E+000 0.000000000000000E+000 1.00000000000000
124+
0.0 0.0 0.0 1 - euler angles: a,b,c; iprop

test/python/wien2k/CaOs2.indmftpr

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
2
2+
1 2
3+
3
4+
complex
5+
0 0 0 0
6+
0 0 0 0
7+
cubic
8+
0 0 2 0
9+
0 0 0 0
10+
1
11+
-0.15 0.30
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
################################################################################
2+
# TRIQS: a Toolbox for Research in Interacting Quantum Systems
3+
# (GPL-3.0-or-later)
4+
################################################################################
5+
6+
# Verify the pure-Python case.symqmc generator (triqs_dftkit.wien2k.symqmc)
7+
# reproduces the dmftproj Fortran output: regenerate case.symqmc in Python from
8+
# case.dmftsym + case.indmftpr + case.struct, convert, and compare the resulting
9+
# HDF5 to the reference produced from the Fortran dmftproj symqmc.
10+
11+
from triqs_dftkit.wien2k.symqmc import write_symqmc
12+
from triqs_dftkit.wien2k import Converter
13+
from triqs.utility.h5diff import h5diff
14+
import triqs.utility.mpi as mpi
15+
16+
write_symqmc('CaOs2')
17+
18+
Converter = Converter(filename='CaOs2')
19+
Converter.hdf_file = 'wien2k_symqmc_python.out.h5'
20+
Converter.convert_dft_input()
21+
22+
if mpi.is_master_node():
23+
h5diff('wien2k_symqmc_python.out.h5', 'wien2k_soc_convert.ref.h5')

0 commit comments

Comments
 (0)