TB2J 0.9.0.1__py3-none-any.whl → 0.9.2rc0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- TB2J/Jdownfolder.py +110 -24
- TB2J/Jtensor.py +1 -1
- TB2J/MAE.py +185 -0
- TB2J/abacus/MAE.py +320 -0
- TB2J/abacus/abacus_wrapper.py +103 -4
- TB2J/abacus/occupations.py +278 -0
- TB2J/abacus/test_density_matrix.py +38 -0
- TB2J/cut_cell.py +82 -0
- TB2J/exchange.py +99 -73
- TB2J/exchangeCL2.py +10 -5
- TB2J/green.py +14 -15
- TB2J/io_exchange/io_pickle.py +0 -0
- TB2J/manager.py +10 -4
- TB2J/mathutils/__init__.py +1 -0
- TB2J/mathutils/fermi.py +22 -0
- TB2J/mathutils/kR_convert.py +90 -0
- TB2J/mathutils/lowdin.py +12 -0
- TB2J/mathutils/rotate_spin.py +56 -0
- TB2J/patch.py +50 -0
- TB2J/pauli.py +17 -0
- TB2J/spinham/h_matrix.py +68 -0
- TB2J/spinham/obtain_J.py +79 -0
- TB2J/supercell.py +532 -0
- {TB2J-0.9.0.1.data → TB2J-0.9.2rc0.data}/scripts/TB2J_downfold.py +8 -0
- {TB2J-0.9.0.1.dist-info → TB2J-0.9.2rc0.dist-info}/METADATA +1 -1
- {TB2J-0.9.0.1.dist-info → TB2J-0.9.2rc0.dist-info}/RECORD +38 -23
- {TB2J-0.9.0.1.dist-info → TB2J-0.9.2rc0.dist-info}/WHEEL +1 -1
- {TB2J-0.9.0.1.data → TB2J-0.9.2rc0.data}/scripts/TB2J_eigen.py +0 -0
- {TB2J-0.9.0.1.data → TB2J-0.9.2rc0.data}/scripts/TB2J_magnon.py +0 -0
- {TB2J-0.9.0.1.data → TB2J-0.9.2rc0.data}/scripts/TB2J_magnon_dos.py +0 -0
- {TB2J-0.9.0.1.data → TB2J-0.9.2rc0.data}/scripts/TB2J_merge.py +0 -0
- {TB2J-0.9.0.1.data → TB2J-0.9.2rc0.data}/scripts/TB2J_rotate.py +0 -0
- {TB2J-0.9.0.1.data → TB2J-0.9.2rc0.data}/scripts/TB2J_rotateDM.py +0 -0
- {TB2J-0.9.0.1.data → TB2J-0.9.2rc0.data}/scripts/abacus2J.py +0 -0
- {TB2J-0.9.0.1.data → TB2J-0.9.2rc0.data}/scripts/siesta2J.py +0 -0
- {TB2J-0.9.0.1.data → TB2J-0.9.2rc0.data}/scripts/wann2J.py +0 -0
- {TB2J-0.9.0.1.dist-info → TB2J-0.9.2rc0.dist-info}/LICENSE +0 -0
- {TB2J-0.9.0.1.dist-info → TB2J-0.9.2rc0.dist-info}/top_level.txt +0 -0
TB2J/abacus/MAE.py
ADDED
@@ -0,0 +1,320 @@
|
|
1
|
+
import numpy as np
|
2
|
+
from TB2J.abacus.abacus_wrapper import AbacusWrapper, AbacusParser
|
3
|
+
from TB2J.mathutils.rotate_spin import rotate_Matrix_from_z_to_axis
|
4
|
+
from TB2J.kpoints import monkhorst_pack
|
5
|
+
from TB2J.mathutils.fermi import fermi
|
6
|
+
from TB2J.mathutils.kR_convert import R_to_k
|
7
|
+
from scipy.linalg import eigh
|
8
|
+
from copy import deepcopy
|
9
|
+
from scipy.spatial.transform import Rotation
|
10
|
+
import matplotlib.pyplot as plt
|
11
|
+
from pathlib import Path
|
12
|
+
from TB2J.abacus.occupations import Occupations
|
13
|
+
|
14
|
+
# TODO List:
|
15
|
+
# - [x] Add the class AbacusSplitSOCWrapper
|
16
|
+
# - [x] Add the function to rotate the XC part
|
17
|
+
# - [x] Compute the band energy at arbitrary angle
|
18
|
+
|
19
|
+
|
20
|
+
def get_occupation(evals, kweights, nel, width=0.1):
|
21
|
+
occ = Occupations(nel=nel, width=width, wk=kweights, nspin=2)
|
22
|
+
return occ.occupy(evals)
|
23
|
+
|
24
|
+
|
25
|
+
def get_density_matrix(evals=None, evecs=None, kweights=None, nel=None, width=0.1):
|
26
|
+
occ = get_occupation(evals, kweights, nel, width=width)
|
27
|
+
rho = np.einsum("kib, kb, kjb -> kij", evecs, occ, evecs.conj())
|
28
|
+
return rho
|
29
|
+
|
30
|
+
|
31
|
+
def spherical_to_cartesian(theta, phi, normalize=True):
|
32
|
+
"""
|
33
|
+
Convert spherical coordinates to cartesian
|
34
|
+
"""
|
35
|
+
x = np.sin(theta) * np.cos(phi)
|
36
|
+
y = np.sin(theta) * np.sin(phi)
|
37
|
+
z = np.cos(theta)
|
38
|
+
vec = np.array([x, y, z])
|
39
|
+
if normalize:
|
40
|
+
vec = vec / np.linalg.norm(vec)
|
41
|
+
return vec
|
42
|
+
|
43
|
+
|
44
|
+
class AbacusSplitSOCWrapper(AbacusWrapper):
|
45
|
+
"""
|
46
|
+
Abacus wrapper with Hamiltonian split to SOC and non-SOC parts
|
47
|
+
"""
|
48
|
+
|
49
|
+
def __init__(self, *args, **kwargs):
|
50
|
+
HR_soc = kwargs.pop("HR_soc", None)
|
51
|
+
# nbasis = HR_soc.shape[1]
|
52
|
+
# kwargs["nbasis"] = nbasis
|
53
|
+
super().__init__(*args, **kwargs)
|
54
|
+
self._HR_copy = deepcopy(self._HR)
|
55
|
+
self.HR_soc = HR_soc
|
56
|
+
self.soc_lambda = 1.0
|
57
|
+
self.nel = 16
|
58
|
+
self.width = 0.1
|
59
|
+
|
60
|
+
@property
|
61
|
+
def HR(self):
|
62
|
+
return self._HR + self.HR_soc * self.soc_lambda
|
63
|
+
|
64
|
+
def rotate_HR_xc(self, axis):
|
65
|
+
"""
|
66
|
+
Rotate SOC part of Hamiltonian
|
67
|
+
"""
|
68
|
+
for iR, R in enumerate(self.Rlist):
|
69
|
+
self._HR[iR] = rotate_Matrix_from_z_to_axis(self._HR_copy[iR], axis)
|
70
|
+
|
71
|
+
def rotate_Hk_xc(self, axis):
|
72
|
+
"""
|
73
|
+
Rotate SOC part of Hamiltonian
|
74
|
+
"""
|
75
|
+
for ik in range(len(self._Hk)):
|
76
|
+
self._Hk[ik] = rotate_Matrix_from_z_to_axis(self._Hk_copy[ik], axis)
|
77
|
+
|
78
|
+
def get_density_matrix(self, kpts, kweights=None):
|
79
|
+
rho = np.zeros((len(kpts), self.nbasis, self.nbasis), dtype=complex)
|
80
|
+
evals, evecs = self.solve_all(kpts)
|
81
|
+
occ = get_occupation(evals, kweights, self.nel, width=self.width)
|
82
|
+
rho = np.einsum(
|
83
|
+
"kib, kb, kjb -> kij", evecs, occ, evecs.conj()
|
84
|
+
) # should multiply S to the the real DM.
|
85
|
+
return rho
|
86
|
+
|
87
|
+
def rotate_DM(self, rho, axis):
|
88
|
+
"""
|
89
|
+
Rotate the density matrix
|
90
|
+
"""
|
91
|
+
for ik in range(len(rho)):
|
92
|
+
rho[ik] = rotate_Matrix_from_z_to_axis(rho[ik], axis)
|
93
|
+
return rho
|
94
|
+
|
95
|
+
|
96
|
+
class RotateHam:
|
97
|
+
def __init__(self, model, kmesh, gamma=True):
|
98
|
+
self.model = model
|
99
|
+
self.kpts = monkhorst_pack(kmesh, gamma_center=gamma)
|
100
|
+
self.kweights = np.ones(len(self.kpts), dtype=float) / len(self.kpts)
|
101
|
+
|
102
|
+
def get_band_energy2(self):
|
103
|
+
for ik, kpt in enumerate(self.kpts):
|
104
|
+
Hk, Sk = self.model.gen_ham(kpt)
|
105
|
+
evals, evecs = eigh(Hk, Sk)
|
106
|
+
rho = np.einsum(
|
107
|
+
"ib, b, jb -> ij",
|
108
|
+
evecs,
|
109
|
+
fermi(evals, self.model.efermi, width=0.05),
|
110
|
+
evecs.conj(),
|
111
|
+
)
|
112
|
+
eband1 = np.sum(evals * fermi(evals, self.model.efermi, width=0.05))
|
113
|
+
eband2 = np.trace(Hk @ rho)
|
114
|
+
print(eband1, eband2)
|
115
|
+
|
116
|
+
def get_band_energy(self, dm=False):
|
117
|
+
evals, evecs = self.model.solve_all(self.kpts)
|
118
|
+
occ = get_occupation(
|
119
|
+
evals, self.kweights, self.model.nel, width=self.model.width
|
120
|
+
)
|
121
|
+
eband = np.sum(evals * occ * self.kweights[:, np.newaxis])
|
122
|
+
# * fermi(evals, self.model.efermi, width=0.05)
|
123
|
+
if dm:
|
124
|
+
density_matrix = self.model.get_density_matrix(evecs)
|
125
|
+
return eband, density_matrix
|
126
|
+
else:
|
127
|
+
return eband
|
128
|
+
|
129
|
+
def calc_ref(self):
|
130
|
+
# calculate the Hk_ref, Sk_ref, Hk_soc_ref, and rho_ref
|
131
|
+
self.Sk_ref = R_to_k(self.kpts, self.model.Rlist, self.model.SR)
|
132
|
+
self.Hk_xc_ref = R_to_k(self.kpts, self.model.Rlist, self.model._HR_copy)
|
133
|
+
self.Hk_soc_ref = R_to_k(self.kpts, self.model.Rlist, self.model.HR_soc)
|
134
|
+
self.rho_ref = np.zeros(
|
135
|
+
(len(self.kpts), self.model.nbasis, self.model.nbasis), dtype=complex
|
136
|
+
)
|
137
|
+
|
138
|
+
evals = np.zeros((len(self.kpts), self.model.nbasis), dtype=float)
|
139
|
+
evecs = np.zeros(
|
140
|
+
(len(self.kpts), self.model.nbasis, self.model.nbasis), dtype=complex
|
141
|
+
)
|
142
|
+
|
143
|
+
for ik, kpt in enumerate(self.kpts):
|
144
|
+
# evals, evecs = eigh(self.Hk_xc_ref[ik]+self.Hk_soc_ref[ik], self.Sk_ref[ik])
|
145
|
+
evals[ik], evecs[ik] = eigh(self.Hk_xc_ref[ik], self.Sk_ref[ik])
|
146
|
+
occ = get_occupation(
|
147
|
+
evals, self.kweights, self.model.nel, width=self.model.width
|
148
|
+
)
|
149
|
+
# occ = fermi(evals, self.model.efermi, width=self.model.width)
|
150
|
+
self.rho_ref = np.einsum("kib, kb, kjb -> kij", evecs, occ, evecs.conj())
|
151
|
+
|
152
|
+
def get_band_energy_from_rho(self, axis):
|
153
|
+
"""
|
154
|
+
This is wrong!! Should use second order perturbation theory to get the band energy instead.
|
155
|
+
"""
|
156
|
+
eband = 0.0
|
157
|
+
for ik, k in enumerate(self.kpts):
|
158
|
+
rho = rotate_Matrix_from_z_to_axis(self.rho_ref[ik], axis)
|
159
|
+
Hk_xc = rotate_Matrix_from_z_to_axis(self.Hk_xc_ref[ik], axis)
|
160
|
+
Hk_soc = self.Hk_soc_ref[ik]
|
161
|
+
Htot = Hk_xc + Hk_soc * self.model.soc_lambda
|
162
|
+
# Sk = self.Sk_ref[ik]
|
163
|
+
# evals, evecs = eigh(Htot, Sk)
|
164
|
+
# rho2= np.einsum("ib, b, jb -> ij", evecs, fermi(evals, self.model.efermi, width=0.05), evecs.conj())
|
165
|
+
if ik == 0 and False:
|
166
|
+
pass
|
167
|
+
# print(f"{evecs[:4,0:4].real=}")
|
168
|
+
# print(f"{evals[:4]=}")
|
169
|
+
# print(f"{Hk_xc[:4,0:4].real=}")
|
170
|
+
# print(f"{Htot[:4,0:4].real=}")
|
171
|
+
# print(f"{Sk[:4,0:4].real=}")
|
172
|
+
# print(f"{rho[:4,0:4].real=}")
|
173
|
+
# print(f"{rho2[:4,0:4].real=}")
|
174
|
+
# eband1 = np.sum(evals * fermi(evals, self.model.efermi, width=0.05))
|
175
|
+
# eband2 = np.trace(Htot @ rho2).real
|
176
|
+
# eband3 = np.trace(Htot @ rho).real
|
177
|
+
# print(eband1, eband2, eband3)
|
178
|
+
e_soc = np.trace(Hk_soc @ rho) * self.kweights[ik] * self.model.soc_lambda
|
179
|
+
eband += e_soc
|
180
|
+
return eband
|
181
|
+
|
182
|
+
def get_band_energy_vs_angles(
|
183
|
+
self,
|
184
|
+
thetas,
|
185
|
+
psis,
|
186
|
+
):
|
187
|
+
es = []
|
188
|
+
# es2 = []
|
189
|
+
# e,rho = self.model.get_band_energy(dm=True)
|
190
|
+
# self.calc_ref()
|
191
|
+
# thetas = np.linspace(*angle_range, npoints)
|
192
|
+
for i, theta, phi in enumerate(zip(thetas, psis)):
|
193
|
+
axis = spherical_to_cartesian(theta, phi)
|
194
|
+
self.model.rotate_HR_xc(axis)
|
195
|
+
# self.get_band_energy2()
|
196
|
+
e = self.get_band_energy()
|
197
|
+
es.append(e)
|
198
|
+
# es2.append(e2)
|
199
|
+
return es
|
200
|
+
|
201
|
+
|
202
|
+
def get_model_energy(model, kmesh, gamma=True):
|
203
|
+
ham = RotateHam(model, kmesh, gamma=gamma)
|
204
|
+
return ham.get_band_energy()
|
205
|
+
|
206
|
+
|
207
|
+
class AbacusSplitSOCParser:
|
208
|
+
"""
|
209
|
+
Abacus parser with Hamiltonian split to SOC and non-SOC parts
|
210
|
+
"""
|
211
|
+
|
212
|
+
def __init__(self, outpath_nosoc=None, outpath_soc=None, binary=False):
|
213
|
+
self.outpath_nosoc = outpath_nosoc
|
214
|
+
self.outpath_soc = outpath_soc
|
215
|
+
self.binary = binary
|
216
|
+
self.parser_nosoc = AbacusParser(outpath=outpath_nosoc, binary=binary)
|
217
|
+
self.parser_soc = AbacusParser(outpath=outpath_soc, binary=binary)
|
218
|
+
spin1 = self.parser_nosoc.read_spin()
|
219
|
+
spin2 = self.parser_soc.read_spin()
|
220
|
+
if spin1 != "noncollinear" or spin2 != "noncollinear":
|
221
|
+
raise ValueError("Spin should be noncollinear")
|
222
|
+
|
223
|
+
def parse(self):
|
224
|
+
nbasis, Rlist, HR, SR = self.parser_nosoc.Read_HSR_noncollinear()
|
225
|
+
nbasis2, Rlist2, HR2, SR2 = self.parser_soc.Read_HSR_noncollinear()
|
226
|
+
# print(HR[0])
|
227
|
+
HR_soc = HR2 - HR
|
228
|
+
model = AbacusSplitSOCWrapper(HR, SR, Rlist, nbasis, nspin=2, HR_soc=HR_soc)
|
229
|
+
model.efermi = self.parser_soc.efermi
|
230
|
+
model.basis = self.parser_nosoc.basis
|
231
|
+
model.atoms = self.parser_nosoc.atoms
|
232
|
+
return model
|
233
|
+
|
234
|
+
|
235
|
+
def abacus_get_MAE(
|
236
|
+
path_nosoc, path_soc, kmesh, thetas, psis, gamma=True, outfile="MAE.txt"
|
237
|
+
):
|
238
|
+
"""Get MAE from Abacus with magnetic force theorem. Two calculations are needed. First we do an calculation with SOC but the soc_lambda is set to 0. Save the density. The next calculatin we start with the density from the first calculation and set the SOC prefactor to 1. With the information from the two calcualtions, we can get the band energy with magnetic moments in the direction, specified in two list, thetas, and phis."""
|
239
|
+
parser = AbacusSplitSOCParser(
|
240
|
+
outpath_nosoc=path_nosoc, outpath_soc=path_soc, binary=False
|
241
|
+
)
|
242
|
+
model = parser.parse()
|
243
|
+
ham = RotateHam(model, kmesh, gamma=gamma)
|
244
|
+
es = []
|
245
|
+
for theta, psi in zip(thetas, psis):
|
246
|
+
axis = spherical_to_cartesian(theta, psi)
|
247
|
+
model.rotate_HR_xc(axis)
|
248
|
+
e = ham.get_band_energy()
|
249
|
+
es.append(ham.get_band_energy())
|
250
|
+
if outfile:
|
251
|
+
with open(outfile, "w") as f:
|
252
|
+
f.write("theta, psi, energy\n")
|
253
|
+
for theta, psi, e in zip(thetas, psis, es):
|
254
|
+
f.write(f"{theta}, {psi}, {e}\n")
|
255
|
+
return es
|
256
|
+
|
257
|
+
|
258
|
+
def test_AbacusSplitSOCWrapper():
|
259
|
+
# path = Path("~/projects/2D_Fe").expanduser()
|
260
|
+
path = Path("~/projects/TB2Jflows/examples/2D_Fe/Fe_z").expanduser()
|
261
|
+
outpath_nosoc = f"{path}/soc0/OUT.ABACUS"
|
262
|
+
outpath_soc = f"{path}/soc1/OUT.ABACUS"
|
263
|
+
parser = AbacusSplitSOCParser(
|
264
|
+
outpath_nosoc=outpath_nosoc, outpath_soc=outpath_soc, binary=False
|
265
|
+
)
|
266
|
+
model = parser.parse()
|
267
|
+
kmesh = [6, 6, 1]
|
268
|
+
|
269
|
+
r = RotateHam(model, kmesh)
|
270
|
+
# thetas, es = r.get_band_energy_vs_theta(angle_range=(0, np.pi*2), rotation_axis="z", initial_direction=(1,0,0), npoints=21)
|
271
|
+
thetas, es, es2 = r.get_band_energy_vs_theta(
|
272
|
+
angle_range=(0, np.pi * 2),
|
273
|
+
rotation_axis="y",
|
274
|
+
initial_direction=(0, 0, 1),
|
275
|
+
npoints=11,
|
276
|
+
)
|
277
|
+
# print the table of thetas and es, es2
|
278
|
+
print("theta, e, e2")
|
279
|
+
for theta, e, e2 in zip(thetas, es, es2):
|
280
|
+
print(f"{theta=}, {e=}, {e2=}")
|
281
|
+
|
282
|
+
plt.plot(thetas / np.pi, es - es[0], marker="o")
|
283
|
+
plt.plot(thetas / np.pi, es2 - es2[0], marker=".")
|
284
|
+
plt.savefig("E_along_z_x_z.png")
|
285
|
+
plt.show()
|
286
|
+
|
287
|
+
|
288
|
+
def abacus_get_MAE_cli():
|
289
|
+
import argparse
|
290
|
+
|
291
|
+
parser = argparse.ArgumentParser(
|
292
|
+
description="Get MAE from Abacus with magnetic force theorem. Two calculations are needed. First we do an calculation with SOC but the soc_lambda is set to 0. Save the density. The next calculatin we start with the density from the first calculation and set the SOC prefactor to 1. With the information from the two calcualtions, we can get the band energy with magnetic moments in the direction, specified in two list, thetas, and phis. "
|
293
|
+
)
|
294
|
+
parser.add_argument("path_nosoc", type=str, help="Path to the calculation with ")
|
295
|
+
parser.add_argument("path_soc", type=str, help="Path to the SOC calculation")
|
296
|
+
parser.add_argument("thetas", type=float, nargs="+", help="Thetas")
|
297
|
+
parser.add_argument("psis", type=float, nargs="+", help="Phis")
|
298
|
+
parser.add_argument("kmesh", type=int, nargs=3, help="K-mesh")
|
299
|
+
parser.add_argument(
|
300
|
+
"--gamma", action="store_true", help="Use Gamma centered kpoints"
|
301
|
+
)
|
302
|
+
parser.add_argument(
|
303
|
+
"--outfile",
|
304
|
+
type=str,
|
305
|
+
help="The angles and the energey will be saved in this file.",
|
306
|
+
)
|
307
|
+
args = parser.parse_args()
|
308
|
+
abacus_get_MAE(
|
309
|
+
args.path_nosoc,
|
310
|
+
args.path_soc,
|
311
|
+
args.kmesh,
|
312
|
+
args.thetas,
|
313
|
+
args.psis,
|
314
|
+
gamma=args.gamma,
|
315
|
+
outfile=args.outfile,
|
316
|
+
)
|
317
|
+
|
318
|
+
|
319
|
+
if __name__ == "__main__":
|
320
|
+
abacus_get_MAE_cli()
|
TB2J/abacus/abacus_wrapper.py
CHANGED
@@ -7,6 +7,8 @@ from pathlib import Path
|
|
7
7
|
import os
|
8
8
|
import numpy as np
|
9
9
|
from scipy.linalg import eigh
|
10
|
+
from copy import deepcopy
|
11
|
+
from TB2J.mathutils.rotate_spin import rotate_Matrix_from_z_to_spherical
|
10
12
|
from TB2J.utils import symbol_number_list
|
11
13
|
from TB2J.myTB import AbstractTB
|
12
14
|
from TB2J.abacus.abacus_api import read_HR_SR
|
@@ -15,17 +17,55 @@ from TB2J.abacus.stru_api import read_abacus, read_abacus_out
|
|
15
17
|
|
16
18
|
|
17
19
|
class AbacusWrapper(AbstractTB):
|
18
|
-
def __init__(
|
19
|
-
self
|
20
|
+
def __init__(
|
21
|
+
self, HR, SR, Rlist, nbasis, nspin=1, HR_soc=None, HR_nosoc=None, nel=None
|
22
|
+
):
|
23
|
+
self.R2kfactor = 2j * np.pi
|
20
24
|
self.is_orthogonal = False
|
25
|
+
self.split_soc = False
|
21
26
|
self._name = "ABACUS"
|
22
|
-
self.
|
27
|
+
self._HR = HR
|
23
28
|
self.SR = SR
|
24
29
|
self.Rlist = Rlist
|
25
30
|
self.nbasis = nbasis
|
26
31
|
self.nspin = nspin
|
27
32
|
self.norb = nbasis * nspin
|
33
|
+
self.nel = nel
|
28
34
|
self._build_Rdict()
|
35
|
+
if HR_soc is not None:
|
36
|
+
self.set_HR_soc(HR_soc=HR_soc, HR_nosoc=HR_nosoc, HR_full=HR)
|
37
|
+
self.soc_rotation_angle = 0.0
|
38
|
+
|
39
|
+
def set_HR_soc(self, HR_soc=None, HR_nosoc=None, HR_full=None):
|
40
|
+
self.split_soc = True
|
41
|
+
self.HR_soc = HR_soc
|
42
|
+
if HR_nosoc is not None:
|
43
|
+
self.HR_nosoc = HR_nosoc
|
44
|
+
if HR_full is not None:
|
45
|
+
self.HR_nosoc = HR_full - HR_soc
|
46
|
+
|
47
|
+
def set_Hsoc_rotation_angle(self, angle):
|
48
|
+
"""
|
49
|
+
Set the rotation angle for SOC part of Hamiltonian
|
50
|
+
"""
|
51
|
+
self.soc_rotation_angle = angle
|
52
|
+
|
53
|
+
@property
|
54
|
+
def HR(self):
|
55
|
+
if self.split_soc:
|
56
|
+
_HR = np.zeros_like(self.HR_soc)
|
57
|
+
for iR, _ in enumerate(self.Rlist):
|
58
|
+
theta, phi = self.soc_rotation_angle
|
59
|
+
_HR[iR] = self.HR_nosoc[iR] + rotate_Matrix_from_z_to_spherical(
|
60
|
+
self.HR_soc[iR], theta, phi
|
61
|
+
)
|
62
|
+
return _HR
|
63
|
+
else:
|
64
|
+
return self._HR
|
65
|
+
|
66
|
+
@HR.setter
|
67
|
+
def set_HR(self, HR):
|
68
|
+
self._HR = HR
|
29
69
|
|
30
70
|
def _build_Rdict(self):
|
31
71
|
if hasattr(self, "Rdict"):
|
@@ -62,6 +102,8 @@ class AbacusWrapper(AbstractTB):
|
|
62
102
|
S = self.SR[iR] * phase
|
63
103
|
# Sk += S + S.conjugate().T
|
64
104
|
Sk += S
|
105
|
+
# Hk = (Hk + Hk.conj().T)/2
|
106
|
+
# Sk = (Sk + Sk.conj().T)/2
|
65
107
|
elif convention == 1:
|
66
108
|
# TODO: implement the first convention (the r convention)
|
67
109
|
raise NotImplementedError("convention 1 is not implemented yet.")
|
@@ -74,6 +116,14 @@ class AbacusWrapper(AbstractTB):
|
|
74
116
|
Hk, Sk = self.gen_ham(k, convention=convention)
|
75
117
|
return eigh(Hk, Sk)
|
76
118
|
|
119
|
+
def solve_all(self, kpts, convention=2):
|
120
|
+
nk = len(kpts)
|
121
|
+
evals = np.zeros((nk, self.nbasis), dtype=float)
|
122
|
+
evecs = np.zeros((nk, self.nbasis, self.nbasis), dtype=complex)
|
123
|
+
for ik, k in enumerate(kpts):
|
124
|
+
evals[ik], evecs[ik] = self.solve(k, convention=convention)
|
125
|
+
return evals, evecs
|
126
|
+
|
77
127
|
def HSE_k(self, kpt, convention=2):
|
78
128
|
H, S = self.gen_ham(tuple(kpt), convention=convention)
|
79
129
|
evals, evecs = eigh(H, S)
|
@@ -196,6 +246,20 @@ class AbacusParser:
|
|
196
246
|
raise ValueError(f"EFERMI not found in the {str(fname)} file.")
|
197
247
|
return efermi
|
198
248
|
|
249
|
+
def read_nel(self):
|
250
|
+
"""
|
251
|
+
Reading the number of electrons from the scf log file.
|
252
|
+
"""
|
253
|
+
fname = str(Path(self.outpath) / "running_scf.log")
|
254
|
+
nel = None
|
255
|
+
with open(fname, "r") as myfile:
|
256
|
+
for line in myfile:
|
257
|
+
if "NELECT" in line:
|
258
|
+
nel = float(line.split()[2])
|
259
|
+
if nel is None:
|
260
|
+
raise ValueError(f"NELECT not found in the {str(fname)} file.")
|
261
|
+
return nel
|
262
|
+
|
199
263
|
def get_basis(self):
|
200
264
|
slist = symbol_number_list(self.atoms)
|
201
265
|
if self.spin == "collinear":
|
@@ -213,6 +277,42 @@ class AbacusParser:
|
|
213
277
|
return basis
|
214
278
|
|
215
279
|
|
280
|
+
class AbacusSplitSOCParser:
|
281
|
+
"""
|
282
|
+
Abacus parser with Hamiltonian split to SOC and non-SOC parts
|
283
|
+
"""
|
284
|
+
|
285
|
+
def __init__(self, outpath_nosoc=None, outpath_soc=None, binary=False):
|
286
|
+
self.outpath_nosoc = outpath_nosoc
|
287
|
+
self.outpath_soc = outpath_soc
|
288
|
+
self.binary = binary
|
289
|
+
self.parser_nosoc = AbacusParser(outpath=outpath_nosoc, binary=binary)
|
290
|
+
self.parser_soc = AbacusParser(outpath=outpath_soc, binary=binary)
|
291
|
+
spin1 = self.parser_nosoc.read_spin()
|
292
|
+
spin2 = self.parser_soc.read_spin()
|
293
|
+
if spin1 != "noncollinear" or spin2 != "noncollinear":
|
294
|
+
raise ValueError("Spin should be noncollinear")
|
295
|
+
|
296
|
+
def parse(self):
|
297
|
+
nbasis, Rlist, HR_nosoc, SR = self.parser_nosoc.Read_HSR_noncollinear()
|
298
|
+
nbasis2, Rlist2, HR2, SR2 = self.parser_soc.Read_HSR_noncollinear()
|
299
|
+
# print(HR[0])
|
300
|
+
HR_soc = HR2 - HR_nosoc
|
301
|
+
model = AbacusWrapper(
|
302
|
+
HR=None,
|
303
|
+
SR=SR,
|
304
|
+
Rlist=Rlist,
|
305
|
+
nbasis=nbasis,
|
306
|
+
nspin=2,
|
307
|
+
HR_soc=HR_soc,
|
308
|
+
HR_nosoc=HR_nosoc,
|
309
|
+
)
|
310
|
+
model.efermi = self.parser_soc.efermi
|
311
|
+
model.basis = self.parser_nosoc.basis
|
312
|
+
model.atoms = self.parser_nosoc.atoms
|
313
|
+
return model
|
314
|
+
|
315
|
+
|
216
316
|
def test_abacus_wrapper_collinear():
|
217
317
|
outpath = "/Users/hexu/projects/TB2J_abacus/abacus-tb2j-master/abacus_example/case_Fe/1_no_soc/OUT.Fe"
|
218
318
|
parser = AbacusParser(outpath=outpath, spin=None, binary=False)
|
@@ -221,7 +321,6 @@ def test_abacus_wrapper_collinear():
|
|
221
321
|
# parser.read_HSR_collinear()
|
222
322
|
model_up, model_dn = parser.get_models()
|
223
323
|
H, S, E, V = model_up.HSE_k([0, 0, 0])
|
224
|
-
# print(H.shape)
|
225
324
|
# print(H.diagonal().real)
|
226
325
|
# print(model_up.get_HR0().diagonal().real)
|
227
326
|
print(parser.efermi)
|