TB2J 0.9.0.3__py3-none-any.whl → 0.9.0.5__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 +3 -3
- TB2J/MAE.py +188 -0
- TB2J/cut_cell.py +82 -0
- TB2J/io_exchange/io_exchange.py +12 -0
- TB2J/io_exchange/io_pickle.py +0 -0
- TB2J/lawaf/__init__.py +0 -0
- TB2J/lawaf/lawaf_interface.py +433 -0
- TB2J/manager.py +26 -3
- TB2J/patch.py +50 -0
- TB2J/spinham/h_matrix.py +68 -0
- TB2J/spinham/obtain_J.py +79 -0
- TB2J/supercell.py +532 -0
- TB2J/symmetrize_J.py +121 -0
- {TB2J-0.9.0.3.data → TB2J-0.9.0.5.data}/scripts/TB2J_downfold.py +2 -2
- {TB2J-0.9.0.3.dist-info → TB2J-0.9.0.5.dist-info}/METADATA +2 -1
- {TB2J-0.9.0.3.dist-info → TB2J-0.9.0.5.dist-info}/RECORD +29 -18
- {TB2J-0.9.0.3.dist-info → TB2J-0.9.0.5.dist-info}/WHEEL +1 -1
- TB2J-0.9.0.5.dist-info/entry_points.txt +2 -0
- {TB2J-0.9.0.3.data → TB2J-0.9.0.5.data}/scripts/TB2J_eigen.py +0 -0
- {TB2J-0.9.0.3.data → TB2J-0.9.0.5.data}/scripts/TB2J_magnon.py +0 -0
- {TB2J-0.9.0.3.data → TB2J-0.9.0.5.data}/scripts/TB2J_magnon_dos.py +0 -0
- {TB2J-0.9.0.3.data → TB2J-0.9.0.5.data}/scripts/TB2J_merge.py +0 -0
- {TB2J-0.9.0.3.data → TB2J-0.9.0.5.data}/scripts/TB2J_rotate.py +0 -0
- {TB2J-0.9.0.3.data → TB2J-0.9.0.5.data}/scripts/TB2J_rotateDM.py +0 -0
- {TB2J-0.9.0.3.data → TB2J-0.9.0.5.data}/scripts/abacus2J.py +0 -0
- {TB2J-0.9.0.3.data → TB2J-0.9.0.5.data}/scripts/siesta2J.py +0 -0
- {TB2J-0.9.0.3.data → TB2J-0.9.0.5.data}/scripts/wann2J.py +0 -0
- {TB2J-0.9.0.3.dist-info → TB2J-0.9.0.5.dist-info}/LICENSE +0 -0
- {TB2J-0.9.0.3.dist-info → TB2J-0.9.0.5.dist-info}/top_level.txt +0 -0
TB2J/Jdownfolder.py
CHANGED
@@ -92,7 +92,7 @@ class PWFDownfolder:
|
|
92
92
|
# anchors={(0, 0, 0): (-1, -2, -3, -4)},
|
93
93
|
# anchors={(0, 0, 0): ()},
|
94
94
|
# use_proj=True,
|
95
|
-
enhance_Amn=2.0,
|
95
|
+
# enhance_Amn=2.0,
|
96
96
|
)
|
97
97
|
params.update(kwargs)
|
98
98
|
wann.set_parameters(**params)
|
@@ -134,7 +134,7 @@ class JDownfolder_pickle:
|
|
134
134
|
outpath,
|
135
135
|
qmesh=[7, 7, 7],
|
136
136
|
iso_only=False,
|
137
|
-
method="
|
137
|
+
method="lowdin",
|
138
138
|
**kwargs
|
139
139
|
):
|
140
140
|
self.exc = SpinIO.load_pickle(path=inpath, fname="TB2J.pickle")
|
@@ -174,7 +174,7 @@ class JDownfolder_pickle:
|
|
174
174
|
|
175
175
|
def _downfold(self, **kwargs):
|
176
176
|
JR2 = self.exc.get_full_Jtensor_for_Rlist(asr=True)
|
177
|
-
if self.method == "lowdin":
|
177
|
+
if self.method.strip().lower() == "lowdin":
|
178
178
|
d = JDownfolder(
|
179
179
|
JR2,
|
180
180
|
self.exc.Rlist,
|
TB2J/MAE.py
ADDED
@@ -0,0 +1,188 @@
|
|
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.mathutils.rotate_spin import spherical_to_cartesian
|
13
|
+
from HamiltonIO.model.occupations import Occupations
|
14
|
+
#from TB2J.abacus.abacus_wrapper import AbacusSplitSOCParser
|
15
|
+
from HamiltonIO.abacus.abacus_wrapper import AbacusSplitSOCParser
|
16
|
+
from HamiltonIO.siesta import SislParser, SiestaHamiltonian
|
17
|
+
import tqdm
|
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
|
+
class MAE:
|
32
|
+
def __init__(self, model, kmesh, gamma=True, width=0.1, nel=None):
|
33
|
+
self.model = model
|
34
|
+
if nel is not None:
|
35
|
+
self.model.nel = nel
|
36
|
+
self.kpts = monkhorst_pack(kmesh, gamma_center=gamma)
|
37
|
+
self.kweights = np.ones(len(self.kpts), dtype=float) / len(self.kpts)
|
38
|
+
self.width = width
|
39
|
+
|
40
|
+
def get_band_energy(self):
|
41
|
+
evals, evecs = self.model.solve_all(self.kpts)
|
42
|
+
occ = get_occupation(evals, self.kweights, self.model.nel, width=self.width)
|
43
|
+
eband = np.sum(evals * occ * self.kweights[:, np.newaxis])
|
44
|
+
return eband
|
45
|
+
|
46
|
+
def calc_ref(self):
|
47
|
+
# calculate the Hk_ref, Sk_ref, Hk_soc_ref, and rho_ref
|
48
|
+
self.Sk_ref = R_to_k(self.kpts, self.model.Rlist, self.model.SR)
|
49
|
+
self.Hk_xc_ref = R_to_k(self.kpts, self.model.Rlist, self.model._HR_copy)
|
50
|
+
self.Hk_soc_ref = R_to_k(self.kpts, self.model.Rlist, self.model.HR_soc)
|
51
|
+
self.rho_ref = np.zeros(
|
52
|
+
(len(self.kpts), self.model.nbasis, self.model.nbasis), dtype=complex
|
53
|
+
)
|
54
|
+
|
55
|
+
evals = np.zeros((len(self.kpts), self.model.nbasis), dtype=float)
|
56
|
+
evecs = np.zeros(
|
57
|
+
(len(self.kpts), self.model.nbasis, self.model.nbasis), dtype=complex
|
58
|
+
)
|
59
|
+
|
60
|
+
for ik, kpt in enumerate(self.kpts):
|
61
|
+
# evals, evecs = eigh(self.Hk_xc_ref[ik]+self.Hk_soc_ref[ik], self.Sk_ref[ik])
|
62
|
+
evals[ik], evecs[ik] = eigh(self.Hk_xc_ref[ik], self.Sk_ref[ik])
|
63
|
+
occ = get_occupation(
|
64
|
+
evals, self.kweights, self.model.nel, width=self.model.width
|
65
|
+
)
|
66
|
+
# occ = fermi(evals, self.model.efermi, width=self.model.width)
|
67
|
+
self.rho_ref = np.einsum("kib, kb, kjb -> kij", evecs, occ, evecs.conj())
|
68
|
+
|
69
|
+
def get_band_energy_vs_angles(
|
70
|
+
self,
|
71
|
+
thetas,
|
72
|
+
phis,
|
73
|
+
):
|
74
|
+
es = []
|
75
|
+
# es2 = []
|
76
|
+
# e,rho = self.model.get_band_energy(dm=True)
|
77
|
+
# self.calc_ref()
|
78
|
+
# thetas = np.linspace(*angle_range, npoints)
|
79
|
+
nangles = len(thetas)
|
80
|
+
for i in tqdm.trange(nangles):
|
81
|
+
theta = thetas[i]
|
82
|
+
phi = phis[i]
|
83
|
+
self.model.set_Hsoc_rotation_angle([theta, phi])
|
84
|
+
e = self.get_band_energy()
|
85
|
+
es.append(e)
|
86
|
+
# es2.append(e2)
|
87
|
+
return es
|
88
|
+
|
89
|
+
|
90
|
+
def get_model_energy(model, kmesh, gamma=True):
|
91
|
+
ham = MAE(model, kmesh, gamma=gamma)
|
92
|
+
return ham.get_band_energy()
|
93
|
+
|
94
|
+
|
95
|
+
def abacus_get_MAE(
|
96
|
+
path_nosoc, path_soc, kmesh, thetas, psis, gamma=True, outfile="MAE.txt", nel=None
|
97
|
+
):
|
98
|
+
"""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."""
|
99
|
+
parser = AbacusSplitSOCParser(
|
100
|
+
outpath_nosoc=path_nosoc, outpath_soc=path_soc, binary=False
|
101
|
+
)
|
102
|
+
model = parser.parse()
|
103
|
+
ham = MAE(model, kmesh, gamma=gamma)
|
104
|
+
es = ham.get_band_energy_vs_angles(thetas, psis)
|
105
|
+
if outfile:
|
106
|
+
with open(outfile, "w") as f:
|
107
|
+
f.write("#theta, psi, energy\n")
|
108
|
+
for theta, psi, e in zip(thetas, psis, es):
|
109
|
+
f.write(f"{theta:5.3f}, {psi:5.3f}, {e:10.9f}\n")
|
110
|
+
return es
|
111
|
+
|
112
|
+
|
113
|
+
def siesta_get_MAE(fdf_fname, kmesh, thetas, phis, gamma=True, outfile="MAE.txt"):
|
114
|
+
""" """
|
115
|
+
model= SislParser(fdf_fname=fdf_fname, read_H_soc=True).get_model()
|
116
|
+
ham = MAE(model, kmesh, gamma=gamma)
|
117
|
+
es = ham.get_band_energy_vs_angles(thetas, phis)
|
118
|
+
if outfile:
|
119
|
+
with open(outfile, "w") as f:
|
120
|
+
f.write("#theta, psi, energy\n")
|
121
|
+
for theta, psi, e in zip(thetas, phis, es):
|
122
|
+
#f.write(f"{theta}, {psi}, {e}\n")
|
123
|
+
f.write(f"{theta:5.3f}, {psi:5.3f}, {e:10.9f}\n")
|
124
|
+
return es
|
125
|
+
|
126
|
+
|
127
|
+
def test_AbacusSplitSOCWrapper():
|
128
|
+
# path = Path("~/projects/2D_Fe").expanduser()
|
129
|
+
path = Path("~/projects/TB2Jflows/examples/2D_Fe/Fe_z").expanduser()
|
130
|
+
outpath_nosoc = f"{path}/soc0/OUT.ABACUS"
|
131
|
+
outpath_soc = f"{path}/soc1/OUT.ABACUS"
|
132
|
+
parser = AbacusSplitSOCParser(
|
133
|
+
outpath_nosoc=outpath_nosoc, outpath_soc=outpath_soc, binary=False
|
134
|
+
)
|
135
|
+
model = parser.parse()
|
136
|
+
kmesh = [6, 6, 1]
|
137
|
+
|
138
|
+
r = MAE(model, kmesh, gamma=True)
|
139
|
+
# thetas, es = r.get_band_energy_vs_theta(angle_range=(0, np.pi*2), rotation_axis="z", initial_direction=(1,0,0), npoints=21)
|
140
|
+
thetas, es, es2 = r.get_band_energy_vs_theta(
|
141
|
+
angle_range=(0, np.pi),
|
142
|
+
rotation_axis="y",
|
143
|
+
initial_direction=(0, 0, 1),
|
144
|
+
npoints=11,
|
145
|
+
)
|
146
|
+
# print the table of thetas and es, es2
|
147
|
+
for theta, e, e2 in zip(thetas, es, es2):
|
148
|
+
print(f"{theta=}, {e=}, {e2=}")
|
149
|
+
|
150
|
+
plt.plot(thetas / np.pi, es - es[0], marker="o")
|
151
|
+
plt.plot(thetas / np.pi, es2 - es2[0], marker=".")
|
152
|
+
plt.savefig("E_along_z_x_z.png")
|
153
|
+
plt.show()
|
154
|
+
|
155
|
+
|
156
|
+
def abacus_get_MAE_cli():
|
157
|
+
import argparse
|
158
|
+
|
159
|
+
parser = argparse.ArgumentParser(
|
160
|
+
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. "
|
161
|
+
)
|
162
|
+
parser.add_argument("path_nosoc", type=str, help="Path to the calculation with ")
|
163
|
+
parser.add_argument("path_soc", type=str, help="Path to the SOC calculation")
|
164
|
+
parser.add_argument("thetas", type=float, nargs="+", help="Thetas")
|
165
|
+
parser.add_argument("psis", type=float, nargs="+", help="Phis")
|
166
|
+
parser.add_argument("kmesh", type=int, nargs=3, help="K-mesh")
|
167
|
+
parser.add_argument(
|
168
|
+
"--gamma", action="store_true", help="Use Gamma centered kpoints"
|
169
|
+
)
|
170
|
+
parser.add_argument(
|
171
|
+
"--outfile",
|
172
|
+
type=str,
|
173
|
+
help="The angles and the energey will be saved in this file.",
|
174
|
+
)
|
175
|
+
args = parser.parse_args()
|
176
|
+
abacus_get_MAE(
|
177
|
+
args.path_nosoc,
|
178
|
+
args.path_soc,
|
179
|
+
args.kmesh,
|
180
|
+
args.thetas,
|
181
|
+
args.psis,
|
182
|
+
gamma=args.gamma,
|
183
|
+
outfile=args.outfile,
|
184
|
+
)
|
185
|
+
|
186
|
+
|
187
|
+
if __name__ == "__main__":
|
188
|
+
abacus_get_MAE_cli()
|
TB2J/cut_cell.py
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
import numpy as np
|
2
|
+
from TB2J.supercell import find_primitive_cell, map_to_primitive
|
3
|
+
from TB2J.io_exchange.io_exchange import SpinIO, gen_distance_dict
|
4
|
+
|
5
|
+
|
6
|
+
def cut_cell(path, output_path, sc_matrix, origin_atom_id, thr=1e-5):
|
7
|
+
"""
|
8
|
+
Cut the exchange parameters
|
9
|
+
:param path: the original TB2J_results path
|
10
|
+
:param output_path: the output path.
|
11
|
+
:param sc_matrix: the matrix which maps the primitive cell to supercell.
|
12
|
+
:param origin: the origin of the primitive cell.
|
13
|
+
:param thr: the atoms which the reduced position is within -thr to 1.0+thr are considered as inside the primitive atoms
|
14
|
+
:returns:
|
15
|
+
"""
|
16
|
+
sc_matrix = np.asarray(sc_matrix, dtype=int)
|
17
|
+
sc_excparams = SpinIO.load_pickle(path=path, fname='TB2J.pickle')
|
18
|
+
sc_atoms = sc_excparams.atoms
|
19
|
+
uc_atoms, ids = find_primitive_cell(sc_atoms,
|
20
|
+
sc_matrix,
|
21
|
+
origin_atom_id=origin_atom_id,
|
22
|
+
thr=thr,
|
23
|
+
perfect=False)
|
24
|
+
uc_charges = sc_excparams.charges[ids]
|
25
|
+
uc_spinat = sc_excparams.spinat[ids]
|
26
|
+
indmap, Rmap = map_to_primitive(sc_atoms, uc_atoms)
|
27
|
+
|
28
|
+
# TODO index_spin: {iatom: ispin}
|
29
|
+
|
30
|
+
# list of iatom for each spin index.
|
31
|
+
uc_index_spin = [sc_excparams.index_spin[i] for i in ids]
|
32
|
+
|
33
|
+
uc_ind_atoms = {}
|
34
|
+
for iatom, ispin in enumerate(uc_index_spin):
|
35
|
+
if ispin >= 0:
|
36
|
+
uc_ind_atoms[ispin] = iatom
|
37
|
+
|
38
|
+
uc_Jdict = {}
|
39
|
+
uc_Rset = set()
|
40
|
+
for key, val in sc_excparams.exchange_Jdict.items():
|
41
|
+
R, ispin, jspin = key
|
42
|
+
iatom = sc_excparams.ind_atoms[ispin]
|
43
|
+
jatom = sc_excparams.ind_atoms[jspin]
|
44
|
+
uc_ispin = uc_index_spin[indmap[iatom]]
|
45
|
+
uc_jspin = uc_index_spin[indmap[jatom]]
|
46
|
+
uc_R = R @ sc_matrix + Rmap[jatom] - Rmap[iatom]
|
47
|
+
uc_R = tuple(uc_R)
|
48
|
+
if iatom in ids:
|
49
|
+
#print(f"{iatom=}, {indmap[iatom]=},{uc_ispin=}, {uc_jspin=}, {uc_R=} ")
|
50
|
+
uc_Jdict[(uc_R, uc_ispin, uc_jspin)] = val
|
51
|
+
uc_Rset.add(uc_R)
|
52
|
+
|
53
|
+
uc_distance_dict = gen_distance_dict(uc_ind_atoms, uc_atoms, list(uc_Rset))
|
54
|
+
|
55
|
+
assert sc_excparams.colinear, "Cut supercell for non-collinear spin is not yet implemented."
|
56
|
+
|
57
|
+
uc_exc = SpinIO(uc_atoms,
|
58
|
+
spinat=uc_spinat,
|
59
|
+
charges=uc_charges,
|
60
|
+
index_spin=uc_index_spin,
|
61
|
+
colinear=sc_excparams.colinear,
|
62
|
+
distance_dict=uc_distance_dict,
|
63
|
+
exchange_Jdict=uc_Jdict,
|
64
|
+
description="Cutted")
|
65
|
+
|
66
|
+
uc_exc.write_all(output_path)
|
67
|
+
|
68
|
+
|
69
|
+
def run_cut_cell(
|
70
|
+
path="TB2J_results",
|
71
|
+
output_path="./TB2J_cutted",
|
72
|
+
sc_matrix=np.array([[1, 1, 0], [-1, 1, 0], [0, 0, 2]]),
|
73
|
+
):
|
74
|
+
cut_cell(path,
|
75
|
+
output_path,
|
76
|
+
np.array(sc_matrix),
|
77
|
+
origin_atom_id=0,
|
78
|
+
thr=1e-19)
|
79
|
+
|
80
|
+
|
81
|
+
if __name__ == "__main__":
|
82
|
+
run_cut_cell()
|
TB2J/io_exchange/io_exchange.py
CHANGED
@@ -238,6 +238,18 @@ Generation time: {now.strftime("%y/%m/%d %H:%M:%S")}
|
|
238
238
|
def get_charge_iatom(self, iatom):
|
239
239
|
return self.charges[iatom]
|
240
240
|
|
241
|
+
def ijR_index_spin_to_atom(self, i, j, R):
|
242
|
+
return (self.iatom(i), self.iatom(j), R)
|
243
|
+
|
244
|
+
def ijR_index_atom_to_spin(self, iatom, jatom, R):
|
245
|
+
return (self.index_spin[iatom], self.index_spin[jatom], R)
|
246
|
+
|
247
|
+
def ijR_list(self):
|
248
|
+
return [(i, j, R) for R, i, j in self.exchange_Jdict]
|
249
|
+
|
250
|
+
def ijR_list_index_atom(self):
|
251
|
+
return [self.ijR_index_spin_to_atom(i, j, R) for R, i, j in self.exchange_Jdict]
|
252
|
+
|
241
253
|
def get_J(self, i, j, R, default=None):
|
242
254
|
i = self.i_spin(i)
|
243
255
|
j = self.i_spin(j)
|
File without changes
|
TB2J/lawaf/__init__.py
ADDED
File without changes
|