TB2J 0.9.12.9__py3-none-any.whl → 0.9.12.22__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/MAE.py +8 -1
- TB2J/MAEGreen.py +78 -61
- TB2J/contour.py +3 -2
- TB2J/exchange.py +346 -51
- TB2J/exchangeCL2.py +285 -47
- TB2J/exchange_params.py +48 -0
- TB2J/green.py +73 -36
- TB2J/interfaces/abacus/gen_exchange_abacus.py +2 -1
- TB2J/interfaces/wannier90_interface.py +4 -4
- TB2J/io_exchange/__init__.py +19 -1
- TB2J/io_exchange/edit.py +594 -0
- TB2J/io_exchange/io_espins.py +276 -0
- TB2J/io_exchange/io_exchange.py +248 -76
- TB2J/io_exchange/io_tomsasd.py +4 -3
- TB2J/io_exchange/io_txt.py +72 -7
- TB2J/io_exchange/io_vampire.py +4 -2
- TB2J/io_merge.py +60 -40
- TB2J/magnon/magnon3.py +27 -2
- TB2J/mathutils/rotate_spin.py +7 -7
- TB2J/myTB.py +11 -11
- TB2J/mycfr.py +11 -11
- TB2J/pauli.py +32 -2
- TB2J/plot.py +26 -0
- TB2J/rotate_atoms.py +9 -6
- TB2J/scripts/TB2J_edit.py +403 -0
- TB2J/scripts/TB2J_plot_exchange.py +48 -0
- TB2J/spinham/hamiltonian.py +156 -13
- TB2J/spinham/hamiltonian_terms.py +40 -1
- TB2J/spinham/spin_xml.py +40 -8
- TB2J/symmetrize_J.py +140 -9
- TB2J/tests/test_cli_remove_sublattice.py +33 -0
- TB2J/tests/test_cli_toggle_exchange.py +50 -0
- {tb2j-0.9.12.9.dist-info → tb2j-0.9.12.22.dist-info}/METADATA +10 -7
- {tb2j-0.9.12.9.dist-info → tb2j-0.9.12.22.dist-info}/RECORD +38 -34
- {tb2j-0.9.12.9.dist-info → tb2j-0.9.12.22.dist-info}/WHEEL +1 -1
- {tb2j-0.9.12.9.dist-info → tb2j-0.9.12.22.dist-info}/entry_points.txt +2 -0
- TB2J/interfaces/abacus/test_read_HRSR.py +0 -43
- TB2J/interfaces/abacus/test_read_stru.py +0 -32
- {tb2j-0.9.12.9.dist-info → tb2j-0.9.12.22.dist-info}/licenses/LICENSE +0 -0
- {tb2j-0.9.12.9.dist-info → tb2j-0.9.12.22.dist-info}/top_level.txt +0 -0
TB2J/io_exchange/io_txt.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import os
|
|
2
|
+
import sys
|
|
2
3
|
|
|
3
4
|
import numpy as np
|
|
4
5
|
from numpy import array_str
|
|
@@ -9,6 +10,7 @@ from TB2J.utils import symbol_number
|
|
|
9
10
|
def write_info_section(cls, myfile):
|
|
10
11
|
myfile.write("=" * 90 + "\n")
|
|
11
12
|
myfile.write("Information: \n")
|
|
13
|
+
myfile.write(f"Executed: \n{' '.join(sys.argv)}\n")
|
|
12
14
|
# myfile.write("Exchange parameters generated by TB2J %s\n" % (__version__))
|
|
13
15
|
# myfile.write("Generation time: %s\n" % (now.strftime("%y/%m/%d %H:%M:%S")))
|
|
14
16
|
myfile.write(cls.description)
|
|
@@ -82,6 +84,24 @@ def write_atom_section(cls, myfile):
|
|
|
82
84
|
)
|
|
83
85
|
)
|
|
84
86
|
|
|
87
|
+
# write single ion anisotropy
|
|
88
|
+
if cls.k1 is not None and cls.k1dir is not None:
|
|
89
|
+
myfile.write("\n")
|
|
90
|
+
myfile.write("-" * 90 + "\n")
|
|
91
|
+
myfile.write("Single Ion Anisotropy (meV): \n")
|
|
92
|
+
myfile.write("(k1 is the anisotropy constant, k1dir is the direction vector)\n")
|
|
93
|
+
myfile.write("{:^12s} {:^12s} {:^24s}\n".format("Atom number", "k1", "k1dir"))
|
|
94
|
+
for i, s in enumerate(symnum):
|
|
95
|
+
ispin = cls.index_spin[i]
|
|
96
|
+
if ispin >= 0:
|
|
97
|
+
k1 = cls.k1[ispin] * 1e3
|
|
98
|
+
k1dir = cls.k1dir[ispin]
|
|
99
|
+
myfile.write(
|
|
100
|
+
"{:<12s} {:12.4f} ({:7.4f}, {:7.4f}, {:7.4f})\n".format(
|
|
101
|
+
s, k1, k1dir[0], k1dir[1], k1dir[2]
|
|
102
|
+
)
|
|
103
|
+
)
|
|
104
|
+
|
|
85
105
|
myfile.write("\n")
|
|
86
106
|
|
|
87
107
|
|
|
@@ -159,17 +179,17 @@ def write_exchange_section(
|
|
|
159
179
|
|
|
160
180
|
if cls.has_biquadratic and write_experimental:
|
|
161
181
|
Jprime, B = cls.biquadratic_Jdict[ll]
|
|
162
|
-
myfile.write(f"[Testing!] Jprime: {Jprime*1e3:.3f}, B: {B*1e3:.3f}\n")
|
|
182
|
+
myfile.write(f"[Testing!] Jprime: {Jprime * 1e3:.3f}, B: {B * 1e3:.3f}\n")
|
|
163
183
|
|
|
164
184
|
if cls.dJdx is not None:
|
|
165
185
|
dJdx = cls.dJdx[ll]
|
|
166
|
-
myfile.write(f"dJ/dx: {dJdx*1e3:.3f}\n")
|
|
186
|
+
myfile.write(f"dJ/dx: {dJdx * 1e3:.3f}\n")
|
|
167
187
|
|
|
168
188
|
if cls.dJdx2 is not None:
|
|
169
189
|
dJdx2 = cls.dJdx2[ll]
|
|
170
|
-
myfile.write(f"d2J/dx2: {dJdx2*1e3:.3f}\n")
|
|
190
|
+
myfile.write(f"d2J/dx2: {dJdx2 * 1e3:.3f}\n")
|
|
171
191
|
|
|
172
|
-
if cls.dmi_ddict is not None:
|
|
192
|
+
if cls.dmi_ddict is not None and ll in cls.dmi_ddict:
|
|
173
193
|
DMI = cls.dmi_ddict[ll] * 1e3
|
|
174
194
|
myfile.write(
|
|
175
195
|
"[Testing!] DMI: ({:7.4f} {:7.4f} {:7.4f})\n".format(
|
|
@@ -188,13 +208,13 @@ def write_exchange_section(
|
|
|
188
208
|
except Exception as e:
|
|
189
209
|
myfile.write(f"[Debug!] DMI2 not available: {e}\n")
|
|
190
210
|
|
|
191
|
-
if cls.Jani_dict is not None:
|
|
211
|
+
if cls.Jani_dict is not None and ll in cls.Jani_dict:
|
|
192
212
|
J = cls.Jani_dict[ll] * 1e3
|
|
193
213
|
myfile.write(
|
|
194
214
|
f"[Testing!]J_ani:\n{array_str(J, precision=3, suppress_small=True)}\n"
|
|
195
215
|
)
|
|
196
216
|
|
|
197
|
-
if cls.NJT_ddict is not None:
|
|
217
|
+
if cls.NJT_ddict is not None and ll in cls.NJT_ddict:
|
|
198
218
|
DMI = cls.NJT_ddict[ll] * 1e3
|
|
199
219
|
myfile.write(
|
|
200
220
|
"[Experimental!] DMI_NJt: ({:7.4f} {:7.4f} {:7.4f})\n".format(
|
|
@@ -202,7 +222,7 @@ def write_exchange_section(
|
|
|
202
222
|
)
|
|
203
223
|
)
|
|
204
224
|
|
|
205
|
-
if cls.NJT_Jdict is not None:
|
|
225
|
+
if cls.NJT_Jdict is not None and ll in cls.NJT_Jdict:
|
|
206
226
|
J = cls.NJT_Jdict[ll] * 1e3
|
|
207
227
|
myfile.write(
|
|
208
228
|
"[Testing!] Jani_NJt: ({:7.4f} {:7.4f} {:7.4f})\n".format(
|
|
@@ -305,6 +325,50 @@ def write_Jq_info(cls, kpts, evals, evecs, myfile, special_kpoints={}):
|
|
|
305
325
|
print(f"{sns[cls.ind_atoms[i]]}, {v[0]: 8.3f}, {v[2]: 8.3f}, {v[2]: 8.3f}\n")
|
|
306
326
|
|
|
307
327
|
|
|
328
|
+
def write_sia_section(cls, myfile):
|
|
329
|
+
"""
|
|
330
|
+
Write single ion anisotropy tensor section.
|
|
331
|
+
"""
|
|
332
|
+
if cls.has_sia_tensor and cls.sia_tensor is not None:
|
|
333
|
+
myfile.write("\n")
|
|
334
|
+
myfile.write("=" * 90 + "\n")
|
|
335
|
+
myfile.write("Single Ion Anisotropy: \n")
|
|
336
|
+
symnum = symbol_number(cls.atoms)
|
|
337
|
+
sns = list(symnum.keys())
|
|
338
|
+
myfile.write(
|
|
339
|
+
"{:^12s} {:^13s} {:^13s} {:^13s}\n".format(
|
|
340
|
+
"Atom_number", "A_xx", "A_xy", "A_xz"
|
|
341
|
+
)
|
|
342
|
+
)
|
|
343
|
+
myfile.write(
|
|
344
|
+
"{:^12s} {:^13s} {:^13s} {:^13s}\n".format("", "A_yx", "A_yy", "A_yz")
|
|
345
|
+
)
|
|
346
|
+
myfile.write(
|
|
347
|
+
"{:^12s} {:^13s} {:^13s} {:^13s}\n".format("", "A_zx", "A_zy", "A_zz")
|
|
348
|
+
)
|
|
349
|
+
for i, tensor in cls.sia_tensor.items():
|
|
350
|
+
iatom = cls.ind_atoms[i]
|
|
351
|
+
myfile.write(
|
|
352
|
+
"{:<12s} {:13.8f} {:13.8f} {:13.8f}\n".format(
|
|
353
|
+
sns[iatom],
|
|
354
|
+
tensor[0, 0] * 1e3,
|
|
355
|
+
tensor[0, 1] * 1e3,
|
|
356
|
+
tensor[0, 2] * 1e3,
|
|
357
|
+
)
|
|
358
|
+
)
|
|
359
|
+
myfile.write(
|
|
360
|
+
"{:<12s} {:13.8f} {:13.8f} {:13.8f}\n".format(
|
|
361
|
+
"", tensor[1, 0] * 1e3, tensor[1, 1] * 1e3, tensor[1, 2] * 1e3
|
|
362
|
+
)
|
|
363
|
+
)
|
|
364
|
+
myfile.write(
|
|
365
|
+
"{:<12s} {:13.8f} {:13.8f} {:13.8f}\n".format(
|
|
366
|
+
"", tensor[2, 0] * 1e3, tensor[2, 1] * 1e3, tensor[2, 2] * 1e3
|
|
367
|
+
)
|
|
368
|
+
)
|
|
369
|
+
myfile.write("-" * 88 + "\n")
|
|
370
|
+
|
|
371
|
+
|
|
308
372
|
def write_txt(
|
|
309
373
|
cls,
|
|
310
374
|
path="TB2J_results",
|
|
@@ -321,6 +385,7 @@ def write_txt(
|
|
|
321
385
|
write_info_section(cls, myfile)
|
|
322
386
|
write_atom_section(cls, myfile)
|
|
323
387
|
write_orbital_section(cls, myfile)
|
|
388
|
+
write_sia_section(cls, myfile)
|
|
324
389
|
write_exchange_section(
|
|
325
390
|
cls,
|
|
326
391
|
myfile,
|
TB2J/io_exchange/io_vampire.py
CHANGED
|
@@ -50,7 +50,7 @@ def write_vampire_unitcell_file(cls, fname):
|
|
|
50
50
|
counter = -1
|
|
51
51
|
for key in cls.exchange_Jdict:
|
|
52
52
|
R, ispin, jspin = key
|
|
53
|
-
Jtensor = cls.get_J_tensor(ispin, jspin, R)
|
|
53
|
+
Jtensor = cls.get_J_tensor(ispin, jspin, R, Jani=True, DMI=True)
|
|
54
54
|
counter += 1 # starts at 0
|
|
55
55
|
myfile.write(
|
|
56
56
|
f"{counter:5d} {ispin:3d} {jspin:3d} {R[0]:3d} {R[1]:3d} {R[2]:3d} "
|
|
@@ -66,7 +66,7 @@ def write_vampire_unitcell_file(cls, fname):
|
|
|
66
66
|
|
|
67
67
|
def write_vampire_mat_file(cls, fname):
|
|
68
68
|
mat_tmpl = """#---------------------------------------------------
|
|
69
|
-
# Material {id}
|
|
69
|
+
# Material {id}
|
|
70
70
|
#---------------------------------------------------
|
|
71
71
|
material[{id}]:material-name={name}
|
|
72
72
|
material[{id}]:damping-constant={damping}
|
|
@@ -75,6 +75,8 @@ material[{id}]:uniaxial-anisotropy-constant={k1}
|
|
|
75
75
|
material[{id}]:material-element={name}
|
|
76
76
|
material[{id}]:initial-spin-direction = {spinat}
|
|
77
77
|
material[{id}]:uniaxial-anisotropy-direction = {k1dir}
|
|
78
|
+
# The following line is required for vampire 5 and later.
|
|
79
|
+
material[{id}]:unit-cell-category = {id}
|
|
78
80
|
#---------------------------------------------------
|
|
79
81
|
"""
|
|
80
82
|
with open(fname, "w") as myfile:
|
TB2J/io_merge.py
CHANGED
|
@@ -107,22 +107,23 @@ class Merger:
|
|
|
107
107
|
projv = {}
|
|
108
108
|
coeff_matrix = {}
|
|
109
109
|
projectors = {}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
110
|
+
if self.main_dat.projv is not None:
|
|
111
|
+
for key in self.main_dat.projv.keys():
|
|
112
|
+
vectors = [obj.projv[key] for obj in self.dat]
|
|
113
|
+
coefficients, u, v = zip(
|
|
114
|
+
*[get_Jani_coefficients(vectors[i], R=R[i]) for i in indices]
|
|
115
|
+
)
|
|
116
|
+
projectors[key] = np.vstack([u[i] @ R[i].T for i in indices])
|
|
117
|
+
coeff_matrix[key] = np.vstack(coefficients)
|
|
118
|
+
proju[key] = np.stack(u)
|
|
119
|
+
projv[key] = np.stack(v)
|
|
120
|
+
if np.linalg.matrix_rank(coeff_matrix[key], tol=1e-2) < 6:
|
|
121
|
+
warnings.warn("""
|
|
122
|
+
WARNING: The matrix of equations to reconstruct the exchange tensors is
|
|
123
|
+
close to being singular. This happens when the magnetic moments between
|
|
124
|
+
different configurations are cloes to being parallel. You need to consider
|
|
125
|
+
more rotated spin configurations, otherwise the results might have a large
|
|
126
|
+
error.""")
|
|
126
127
|
|
|
127
128
|
self.proju = proju
|
|
128
129
|
self.projv = projv
|
|
@@ -134,13 +135,23 @@ class Merger:
|
|
|
134
135
|
proju = self.proju
|
|
135
136
|
projv = self.projv
|
|
136
137
|
coeff_matrix = self.coeff_matrix
|
|
137
|
-
for key in self.main_dat.
|
|
138
|
+
for key in self.main_dat.exchange_Jdict.keys():
|
|
138
139
|
try:
|
|
139
140
|
R, i, j = key
|
|
140
141
|
u = proju[i, j]
|
|
141
142
|
v = projv[i, j]
|
|
142
|
-
|
|
143
|
-
|
|
143
|
+
projections = []
|
|
144
|
+
for idx, sio in enumerate(self.dat):
|
|
145
|
+
if sio.has_bilinear and key in sio.Jani_dict:
|
|
146
|
+
Jmat = sio.get_J_tensor(
|
|
147
|
+
i, j, R, Jiso=False, Jani=True, DMI=False
|
|
148
|
+
)
|
|
149
|
+
else:
|
|
150
|
+
#J_scalar = sio.exchange_Jdict.get(key, 0.0)
|
|
151
|
+
Jmat = np.eye(3) * 0.0
|
|
152
|
+
proj = np.einsum("mi,ij,mj->m", u[idx], Jmat, v[idx])
|
|
153
|
+
projections.append(proj)
|
|
154
|
+
projections = np.concatenate(projections)
|
|
144
155
|
except KeyError as err:
|
|
145
156
|
raise KeyError(
|
|
146
157
|
"Can not find key: %s, Please make sure the three calculations use the same k-mesh and same Rcut."
|
|
@@ -158,28 +169,36 @@ class Merger:
|
|
|
158
169
|
|
|
159
170
|
def merge_Jiso(self):
|
|
160
171
|
Jdict = {}
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
172
|
+
if self.main_dat.exchange_Jdict is not None:
|
|
173
|
+
for key in self.main_dat.exchange_Jdict.keys():
|
|
174
|
+
try:
|
|
175
|
+
J = np.mean([obj.exchange_Jdict[key] for obj in self.dat])
|
|
176
|
+
except KeyError as err:
|
|
177
|
+
raise KeyError(
|
|
178
|
+
"Can not find key: %s, Please make sure the three calculations use the same k-mesh and same Rcut."
|
|
179
|
+
% err
|
|
180
|
+
)
|
|
181
|
+
Jdict[key] = J
|
|
182
|
+
self.main_dat.exchange_Jdict = Jdict
|
|
171
183
|
|
|
172
184
|
def merge_DMI(self):
|
|
173
185
|
dmi_ddict = {}
|
|
174
|
-
if
|
|
186
|
+
if any(obj.has_dmi for obj in self.dat):
|
|
175
187
|
projectors = self.projectors
|
|
176
188
|
proju = self.proju
|
|
177
|
-
for key in self.main_dat.
|
|
189
|
+
for key in self.main_dat.exchange_Jdict.keys():
|
|
178
190
|
try:
|
|
179
191
|
R, i, j = key
|
|
180
192
|
u = proju[i, j]
|
|
181
|
-
|
|
182
|
-
|
|
193
|
+
projections = []
|
|
194
|
+
for idx, sio in enumerate(self.dat):
|
|
195
|
+
if sio.has_dmi and key in sio.dmi_ddict:
|
|
196
|
+
DMI = sio.dmi_ddict[key]
|
|
197
|
+
else:
|
|
198
|
+
DMI = np.zeros(3)
|
|
199
|
+
proj = np.einsum("mi,i->m", u[idx], DMI)
|
|
200
|
+
projections.append(proj)
|
|
201
|
+
projections = np.concatenate(projections)
|
|
183
202
|
except KeyError as err:
|
|
184
203
|
raise KeyError(
|
|
185
204
|
"Can not find key: %s, Please make sure the three calculations use the same k-mesh and same Rcut."
|
|
@@ -193,13 +212,14 @@ class Merger:
|
|
|
193
212
|
# make sure that the Jani has the trace of zero
|
|
194
213
|
Jdict = self.main_dat.exchange_Jdict
|
|
195
214
|
Jani_dict = self.main_dat.Jani_dict
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
215
|
+
if Jani_dict is not None:
|
|
216
|
+
for key in Jani_dict.keys():
|
|
217
|
+
Jani = Jani_dict[key]
|
|
218
|
+
shift = np.trace(Jani) / 3.0
|
|
219
|
+
Jani_dict[key] -= shift * np.eye(3)
|
|
220
|
+
Jdict[key] += shift
|
|
221
|
+
self.main_dat.Jani_dict = Jani_dict
|
|
222
|
+
self.main_dat.exchange_Jdict = Jdict
|
|
203
223
|
|
|
204
224
|
|
|
205
225
|
def merge(*paths, main_path=None, save=True, write_path="TB2J_results"):
|
TB2J/magnon/magnon3.py
CHANGED
|
@@ -28,6 +28,7 @@ class MagnonParameters:
|
|
|
28
28
|
Jiso: bool = True
|
|
29
29
|
Jani: bool = False
|
|
30
30
|
DMI: bool = False
|
|
31
|
+
SIA: bool = True
|
|
31
32
|
Q: Optional[List[float]] = None
|
|
32
33
|
uz_file: Optional[str] = None
|
|
33
34
|
n: Optional[List[float]] = None
|
|
@@ -398,11 +399,18 @@ class Magnon:
|
|
|
398
399
|
cell = exc.atoms.get_cell()
|
|
399
400
|
pbc = exc.atoms.get_pbc()
|
|
400
401
|
|
|
402
|
+
# Extract SIA from kwargs, default True if not present
|
|
403
|
+
include_SIA = kwargs.pop("SIA", True)
|
|
404
|
+
|
|
405
|
+
JR = exc.get_full_Jtensor_for_Rlist(
|
|
406
|
+
order="ij33", asr=False, SIA=include_SIA, **kwargs
|
|
407
|
+
)
|
|
408
|
+
|
|
401
409
|
return cls(
|
|
402
410
|
nspin=exc.nspin,
|
|
403
411
|
magmom=magmoms,
|
|
404
412
|
Rlist=exc.Rlist,
|
|
405
|
-
JR=
|
|
413
|
+
JR=JR,
|
|
406
414
|
cell=cell,
|
|
407
415
|
_Q=np.zeros(3), # Default propagation vector
|
|
408
416
|
_uz=np.array([[0.0, 0.0, 1.0]]), # Default quantization axis
|
|
@@ -654,7 +662,11 @@ def plot_magnon_bands_from_TB2J(
|
|
|
654
662
|
# Load magnon calculator from TB2J results
|
|
655
663
|
print(f"Loading exchange parameters from {params.path}...")
|
|
656
664
|
magnon = Magnon.from_TB2J_results(
|
|
657
|
-
path=params.path,
|
|
665
|
+
path=params.path,
|
|
666
|
+
Jiso=params.Jiso,
|
|
667
|
+
Jani=params.Jani,
|
|
668
|
+
DMI=params.DMI,
|
|
669
|
+
SIA=params.SIA,
|
|
658
670
|
)
|
|
659
671
|
|
|
660
672
|
# Set reference vectors if provided
|
|
@@ -815,6 +827,18 @@ def plot_magnon_bands_cli():
|
|
|
815
827
|
default=False,
|
|
816
828
|
help="Include anisotropic exchange interactions (default: False)",
|
|
817
829
|
)
|
|
830
|
+
parser.add_argument(
|
|
831
|
+
"--SIA",
|
|
832
|
+
action="store_true",
|
|
833
|
+
default=True,
|
|
834
|
+
help="Include single ion anisotropy (default: True)",
|
|
835
|
+
)
|
|
836
|
+
parser.add_argument(
|
|
837
|
+
"--no-SIA",
|
|
838
|
+
action="store_false",
|
|
839
|
+
dest="SIA",
|
|
840
|
+
help="Exclude single ion anisotropy",
|
|
841
|
+
)
|
|
818
842
|
parser.add_argument(
|
|
819
843
|
"-d",
|
|
820
844
|
"--DMI",
|
|
@@ -881,6 +905,7 @@ def plot_magnon_bands_cli():
|
|
|
881
905
|
filename=args.output,
|
|
882
906
|
Jiso=args.Jiso,
|
|
883
907
|
Jani=args.Jani,
|
|
908
|
+
SIA=args.SIA,
|
|
884
909
|
DMI=args.DMI,
|
|
885
910
|
Q=args.Q if args.Q is not None else None,
|
|
886
911
|
uz_file=args.uz_file,
|
TB2J/mathutils/rotate_spin.py
CHANGED
|
@@ -92,7 +92,7 @@ def rotate_spinor_matrix_einsum(M, theta, phi):
|
|
|
92
92
|
Rotate the spinor matrix M by theta and phi,
|
|
93
93
|
"""
|
|
94
94
|
shape = M.shape
|
|
95
|
-
n1 = np.
|
|
95
|
+
n1 = np.prod(shape[:-1]) // 2
|
|
96
96
|
n2 = M.shape[-1] // 2
|
|
97
97
|
Mnew = np.reshape(M, (n1, 2, n2, 2)) # .swapaxes(1, 2)
|
|
98
98
|
U = rotation_matrix(theta, phi)
|
|
@@ -203,15 +203,15 @@ def test_rotate_spinor_M():
|
|
|
203
203
|
Mrot4 = rotate_spinor_matrix_kron(M, np.pi / 2, np.pi / 2)
|
|
204
204
|
Mrot5 = rotate_spinor_matrix_spkron(M, np.pi / 2, np.pi / 2)
|
|
205
205
|
print(f"Rotated M with jit:\n {Mrot1}")
|
|
206
|
-
print(f"Rotated M with einsum:\n {Mrot2-Mrot1}")
|
|
207
|
-
print(f"Rotated M with reshape:\n {Mrot3-Mrot1}")
|
|
208
|
-
print(f"Rotated M with kron:\n {Mrot4-Mrot1}")
|
|
209
|
-
print(f"Rotated M with spkron:\n {Mrot5-Mrot1}")
|
|
206
|
+
print(f"Rotated M with einsum:\n {Mrot2 - Mrot1}")
|
|
207
|
+
print(f"Rotated M with reshape:\n {Mrot3 - Mrot1}")
|
|
208
|
+
print(f"Rotated M with kron:\n {Mrot4 - Mrot1}")
|
|
209
|
+
print(f"Rotated M with spkron:\n {Mrot5 - Mrot1}")
|
|
210
210
|
|
|
211
211
|
M_rot00 = rotate_spinor_matrix(M, 0, 0)
|
|
212
212
|
M_rot00_sph = rotate_Matrix_from_z_to_spherical(M, 0, 0)
|
|
213
|
-
print(f"Rotated M with theta=0, phi=0 compared with M:\n {M_rot00-M}")
|
|
214
|
-
print(f"Rotated M with theta=0, phi=0 compared with M:\n {M_rot00_sph-M}")
|
|
213
|
+
print(f"Rotated M with theta=0, phi=0 compared with M:\n {M_rot00 - M}")
|
|
214
|
+
print(f"Rotated M with theta=0, phi=0 compared with M:\n {M_rot00_sph - M}")
|
|
215
215
|
|
|
216
216
|
|
|
217
217
|
def test_rotate_spinor_oneblock():
|
TB2J/myTB.py
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
|
-
import os
|
|
2
|
-
import numpy as np
|
|
3
1
|
import copy
|
|
4
|
-
|
|
5
|
-
from scipy.sparse import csr_matrix
|
|
6
|
-
from scipy.io import netcdf_file
|
|
2
|
+
import os
|
|
7
3
|
from collections import defaultdict
|
|
8
4
|
|
|
5
|
+
import numpy as np
|
|
6
|
+
|
|
9
7
|
# from tbmodels import Model
|
|
10
8
|
from ase.atoms import Atoms
|
|
9
|
+
from scipy.io import netcdf_file
|
|
10
|
+
from scipy.linalg import eigh
|
|
11
|
+
from scipy.sparse import csr_matrix
|
|
12
|
+
|
|
11
13
|
from TB2J.utils import auto_assign_basis_name
|
|
12
|
-
from TB2J.wannier import
|
|
14
|
+
from TB2J.wannier import parse_atoms, parse_ham, parse_tb, parse_xyz
|
|
13
15
|
|
|
14
16
|
|
|
15
17
|
class AbstractTB:
|
|
@@ -187,9 +189,7 @@ class MyTB(AbstractTB):
|
|
|
187
189
|
if os.path.exists(tb_fname):
|
|
188
190
|
xcart, nbasis, data, R_degens = parse_tb(fname=tb_fname)
|
|
189
191
|
else:
|
|
190
|
-
nbasis, data, R_degens = parse_ham(
|
|
191
|
-
fname=os.path.join(path, prefix + "_hr.dat")
|
|
192
|
-
)
|
|
192
|
+
nbasis, data, R_degens = parse_ham(fname=hr_fname)
|
|
193
193
|
xcart, _, _ = parse_xyz(fname=os.path.join(path, prefix + "_centres.xyz"))
|
|
194
194
|
|
|
195
195
|
if atoms is None:
|
|
@@ -237,7 +237,7 @@ class MyTB(AbstractTB):
|
|
|
237
237
|
return m
|
|
238
238
|
|
|
239
239
|
def gen_ham(self, k, convention=2):
|
|
240
|
-
"""
|
|
240
|
+
r"""
|
|
241
241
|
generate hamiltonian matrix at k point.
|
|
242
242
|
H_k( i, j)=\sum_R H_R(i, j)^phase.
|
|
243
243
|
There are two conventions,
|
|
@@ -451,7 +451,7 @@ class MyTB(AbstractTB):
|
|
|
451
451
|
nbasis = root.dimensions["nbasis"]
|
|
452
452
|
nspin = root.dimensions["nspin"]
|
|
453
453
|
ndim = root.dimensions["ndim"]
|
|
454
|
-
|
|
454
|
+
_natom = root.dimensions["natom"]
|
|
455
455
|
Rlist = root.variables["R"][:]
|
|
456
456
|
mdata_real = root.variables["data_real"][:]
|
|
457
457
|
mdata_imag = root.variables["data_imag"][:]
|
TB2J/mycfr.py
CHANGED
|
@@ -5,21 +5,18 @@
|
|
|
5
5
|
Continued fraction representation.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
+
import ase.units.kB as kb
|
|
8
9
|
import numpy as np
|
|
9
10
|
from scipy.linalg import eig
|
|
10
11
|
|
|
11
|
-
kb = 8.61733e-5 # Boltzmann constant in eV
|
|
12
|
-
|
|
13
12
|
|
|
14
13
|
class CFR:
|
|
15
|
-
"""
|
|
16
|
-
Integration with the continued fraction representation.
|
|
17
|
-
"""
|
|
14
|
+
"""Integration with the continued fraction representation."""
|
|
18
15
|
|
|
19
|
-
def __init__(self, nz=50, T=
|
|
16
|
+
def __init__(self, nz: int = 50, T: float = 200.0):
|
|
20
17
|
self.nz = nz
|
|
21
|
-
self.T =
|
|
22
|
-
self.beta = 1 / (kb * self.T)
|
|
18
|
+
self.T = float(T)
|
|
19
|
+
self.beta = 1.0 / (kb * self.T)
|
|
23
20
|
self.Rinf = 1e10
|
|
24
21
|
if nz <= 0:
|
|
25
22
|
raise ValueError("nz should be larger than 0.")
|
|
@@ -65,8 +62,8 @@ class CFR:
|
|
|
65
62
|
# Rp = 0.25 * np.diag(eigv)**2 * zp **2
|
|
66
63
|
|
|
67
64
|
# print the poles and the weights
|
|
68
|
-
for i in range(len(self.poles)):
|
|
69
|
-
|
|
65
|
+
# for i in range(len(self.poles)):
|
|
66
|
+
# print("Pole: ", self.poles[i], "Weight: ", self.weights[i])
|
|
70
67
|
|
|
71
68
|
# add a point to the poles: 1e10j
|
|
72
69
|
self.path = np.concatenate((self.path, [self.Rinf * 1j]))
|
|
@@ -110,7 +107,10 @@ def test_cfr():
|
|
|
110
107
|
|
|
111
108
|
r = cfr.integrate_func(test_gf, ef=2)
|
|
112
109
|
r = -np.imag(r) / np.pi * 2
|
|
113
|
-
|
|
110
|
+
if not np.close(r, -1.0):
|
|
111
|
+
raise AssertionError(
|
|
112
|
+
f"Test failed, the integration should be close to -1.0 but get {r}"
|
|
113
|
+
)
|
|
114
114
|
return r
|
|
115
115
|
|
|
116
116
|
|
TB2J/pauli.py
CHANGED
|
@@ -59,7 +59,7 @@ def pauli_decomp2(M):
|
|
|
59
59
|
)
|
|
60
60
|
|
|
61
61
|
|
|
62
|
-
def
|
|
62
|
+
def pauli_sigma_norm_old(M):
|
|
63
63
|
MI, Mx, My, Mz = pauli_decomp2(M)
|
|
64
64
|
return np.linalg.norm([Mx, My, Mz])
|
|
65
65
|
|
|
@@ -134,7 +134,7 @@ def pauli_block(M, idim):
|
|
|
134
134
|
return tmp
|
|
135
135
|
|
|
136
136
|
|
|
137
|
-
def
|
|
137
|
+
def pauli_block_all_old(M):
|
|
138
138
|
MI = (M[::2, ::2] + M[1::2, 1::2]) / 2.0
|
|
139
139
|
Mx = (M[::2, 1::2] + M[1::2, ::2]) / 2.0
|
|
140
140
|
# Note that this is not element wise product with sigma_y but dot product
|
|
@@ -143,6 +143,28 @@ def pauli_block_all(M):
|
|
|
143
143
|
return MI, Mx, My, Mz
|
|
144
144
|
|
|
145
145
|
|
|
146
|
+
def pauli_block_all(array):
|
|
147
|
+
A00 = array[..., ::2, ::2]
|
|
148
|
+
A01 = array[..., ::2, 1::2]
|
|
149
|
+
A10 = array[..., 1::2, ::2]
|
|
150
|
+
A11 = array[..., 1::2, 1::2]
|
|
151
|
+
n2 = array.shape[-1] // 2
|
|
152
|
+
|
|
153
|
+
out_dtype = np.result_type(array.dtype, np.complex64)
|
|
154
|
+
block = np.empty((*array.shape[:-2], 4, n2, n2), dtype=out_dtype)
|
|
155
|
+
|
|
156
|
+
np.add(A00, A11, out=block[..., 0, :, :])
|
|
157
|
+
block[..., 0, :, :] *= 0.5
|
|
158
|
+
np.add(A01, A10, out=block[..., 1, :, :])
|
|
159
|
+
block[..., 1, :, :] *= 0.5
|
|
160
|
+
np.subtract(A01, A10, out=block[..., 2, :, :])
|
|
161
|
+
block[..., 2, :, :] *= 0.5j
|
|
162
|
+
np.subtract(A00, A11, out=block[..., 3, :, :])
|
|
163
|
+
block[..., 3, :, :] *= 0.5
|
|
164
|
+
|
|
165
|
+
return block
|
|
166
|
+
|
|
167
|
+
|
|
146
168
|
def gather_pauli_blocks(MI, Mx, My, Mz, coeffs=[1.0, 1.0, 1.0, 1.0]):
|
|
147
169
|
"""
|
|
148
170
|
Gather the I, x, y, z component of a matrix.
|
|
@@ -212,3 +234,11 @@ def pauli_block_sigma_norm(M):
|
|
|
212
234
|
evec = np.array((ex, ey, ez))
|
|
213
235
|
evec = evec / np.linalg.norm(evec)
|
|
214
236
|
return Mx * evec[0] + My * evec[1] + Mz * evec[2]
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def pauli_sigma_norm(array):
|
|
240
|
+
block = pauli_block_all(array)[..., 1:, :, :]
|
|
241
|
+
E = np.trace(block, axis1=-2, axis2=-1)
|
|
242
|
+
E /= np.linalg.norm(E, axis=-1, keepdims=True)
|
|
243
|
+
np.multiply(block, E[..., None, None], out=block)
|
|
244
|
+
return block.sum(axis=-3)
|
TB2J/plot.py
CHANGED
|
@@ -14,6 +14,32 @@ def write_eigen(qmesh, gamma=True, path="./", output_fname="EigenJq.txt", **kwar
|
|
|
14
14
|
m.write_Jq(kmesh=qmesh, path=path, gamma=gamma, output_fname=output_fname, **kwargs)
|
|
15
15
|
|
|
16
16
|
|
|
17
|
+
def plot_exchange(
|
|
18
|
+
input_data,
|
|
19
|
+
ax=None,
|
|
20
|
+
marker="o",
|
|
21
|
+
fname=None,
|
|
22
|
+
show=False,
|
|
23
|
+
by_species=True,
|
|
24
|
+
**kwargs,
|
|
25
|
+
):
|
|
26
|
+
"""Plot exchange parameters vs distance grouped by species.
|
|
27
|
+
input_data: SpinIO object or path to the pickle file/directory.
|
|
28
|
+
"""
|
|
29
|
+
if isinstance(input_data, str):
|
|
30
|
+
if os.path.isdir(input_data):
|
|
31
|
+
m = SpinIO.load_pickle(input_data)
|
|
32
|
+
else:
|
|
33
|
+
m = SpinIO.load_pickle(
|
|
34
|
+
os.path.dirname(input_data), os.path.basename(input_data)
|
|
35
|
+
)
|
|
36
|
+
else:
|
|
37
|
+
m = input_data
|
|
38
|
+
return m.plot_JvsR(
|
|
39
|
+
ax=ax, marker=marker, fname=fname, show=show, by_species=by_species, **kwargs
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
|
|
17
43
|
def plot_magnon_band(
|
|
18
44
|
fname="exchange.xml",
|
|
19
45
|
path="./",
|
TB2J/rotate_atoms.py
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
2
|
import copy
|
|
3
|
-
|
|
3
|
+
|
|
4
4
|
import numpy as np
|
|
5
|
+
from ase.io import read, write
|
|
5
6
|
from TB2J.tensor_rotate import Rzx, Rzy, Rzz
|
|
6
7
|
|
|
7
8
|
|
|
@@ -15,14 +16,16 @@ def rotate_atom_xyz(atoms, noncollinear=False):
|
|
|
15
16
|
will be generated.
|
|
16
17
|
"""
|
|
17
18
|
|
|
19
|
+
yield atoms
|
|
18
20
|
rotation_axes = [(1, 0, 0), (0, 1, 0)]
|
|
19
21
|
if noncollinear:
|
|
20
22
|
rotation_axes += [(1, 1, 0), (1, 0, 1), (0, 1, 1)]
|
|
21
|
-
|
|
23
|
+
|
|
22
24
|
for axis in rotation_axes:
|
|
23
25
|
rotated_atoms = copy.deepcopy(atoms)
|
|
24
26
|
rotated_atoms.rotate(90, axis, rotate_cell=True)
|
|
25
27
|
yield rotated_atoms
|
|
28
|
+
yield atoms
|
|
26
29
|
|
|
27
30
|
|
|
28
31
|
def rotate_atom_spin_one_rotation(atoms, Rotation):
|
|
@@ -109,7 +112,7 @@ def rotate_xyz(fname, ftype="xyz", noncollinear=False):
|
|
|
109
112
|
rotated = rotate_atom_xyz(atoms, noncollinear=noncollinear)
|
|
110
113
|
|
|
111
114
|
for i, rotated_atoms in enumerate(rotated):
|
|
112
|
-
write(f"atoms_{i
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
115
|
+
write(f"atoms_{i}.{ftype}", rotated_atoms)
|
|
116
|
+
print(
|
|
117
|
+
f"The output has been written to the atoms_i.{ftype} files. atoms_0.{ftype} contains the reference structure."
|
|
118
|
+
)
|