TB2J 0.9.12.18__py3-none-any.whl → 0.9.12.23__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 +0 -2
- TB2J/exchange.py +11 -4
- TB2J/exchangeCL2.py +2 -0
- TB2J/exchange_params.py +24 -0
- TB2J/green.py +15 -3
- TB2J/interfaces/abacus/gen_exchange_abacus.py +2 -1
- TB2J/io_exchange/__init__.py +19 -1
- TB2J/io_exchange/edit.py +594 -0
- TB2J/io_exchange/io_exchange.py +243 -74
- TB2J/io_exchange/io_tomsasd.py +4 -3
- TB2J/io_exchange/io_txt.py +72 -7
- TB2J/io_exchange/io_vampire.py +1 -1
- TB2J/io_merge.py +60 -40
- TB2J/magnon/magnon3.py +27 -2
- TB2J/mathutils/rotate_spin.py +7 -7
- TB2J/mycfr.py +16 -16
- TB2J/plot.py +26 -0
- 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 +138 -7
- TB2J/tests/test_cli_remove_sublattice.py +33 -0
- TB2J/tests/test_cli_toggle_exchange.py +50 -0
- {tb2j-0.9.12.18.dist-info → tb2j-0.9.12.23.dist-info}/METADATA +7 -3
- {tb2j-0.9.12.18.dist-info → tb2j-0.9.12.23.dist-info}/RECORD +32 -35
- {tb2j-0.9.12.18.dist-info → tb2j-0.9.12.23.dist-info}/WHEEL +1 -1
- {tb2j-0.9.12.18.dist-info → tb2j-0.9.12.23.dist-info}/entry_points.txt +2 -0
- TB2J/.gitignore +0 -5
- TB2J/agent_files/debug_spinphon_fd/debug_main.py +0 -156
- TB2J/agent_files/debug_spinphon_fd/test_compute_dJdx.py +0 -272
- TB2J/agent_files/debug_spinphon_fd/test_ispin0_only.py +0 -120
- TB2J/agent_files/debug_spinphon_fd/test_no_d2j.py +0 -31
- TB2J/agent_files/debug_spinphon_fd/test_with_d2j.py +0 -28
- TB2J/interfaces/abacus/test_read_HRSR.py +0 -43
- TB2J/interfaces/abacus/test_read_stru.py +0 -32
- {tb2j-0.9.12.18.dist-info → tb2j-0.9.12.23.dist-info}/licenses/LICENSE +0 -0
- {tb2j-0.9.12.18.dist-info → tb2j-0.9.12.23.dist-info}/top_level.txt +0 -0
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} "
|
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/mycfr.py
CHANGED
|
@@ -6,20 +6,17 @@ Continued fraction representation.
|
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
8
|
import numpy as np
|
|
9
|
+
from ase.units import kB
|
|
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 / (
|
|
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.")
|
|
@@ -27,7 +24,7 @@ class CFR:
|
|
|
27
24
|
self.prepare_poles()
|
|
28
25
|
|
|
29
26
|
def prepare_poles(self):
|
|
30
|
-
##b_mat = [1 / (2.0 * np.sqrt((2 * (j + 1) - 1) * (2 * (j + 1) + 1)) / (
|
|
27
|
+
##b_mat = [1 / (2.0 * np.sqrt((2 * (j + 1) - 1) * (2 * (j + 1) + 1)) / (kB * self.#T)) for j in range(0, self.nz- 1)]
|
|
31
28
|
jmat = np.arange(0, self.nz - 1)
|
|
32
29
|
b_mat = 1 / (2.0 * np.sqrt((2 * (jmat + 1) - 1) * (2 * (jmat + 1) + 1)))
|
|
33
30
|
b_mat = np.diag(b_mat, -1) + np.diag(b_mat, 1)
|
|
@@ -42,16 +39,16 @@ class CFR:
|
|
|
42
39
|
# np.real(self.poles) > 0, 2.0j / self.beta * residules, 0.0
|
|
43
40
|
# )
|
|
44
41
|
|
|
45
|
-
# self.path = 1j / self.poles *
|
|
42
|
+
# self.path = 1j / self.poles * kB * self.T
|
|
46
43
|
|
|
47
44
|
self.path = []
|
|
48
45
|
self.weights = []
|
|
49
46
|
for p, r in zip(self.poles, residules):
|
|
50
47
|
if p > 0:
|
|
51
|
-
self.path.append(1j / p *
|
|
48
|
+
self.path.append(1j / p * kB * self.T)
|
|
52
49
|
w = 2.0j / self.beta * r
|
|
53
50
|
self.weights.append(w)
|
|
54
|
-
self.path.append(-1j / p *
|
|
51
|
+
self.path.append(-1j / p * kB * self.T)
|
|
55
52
|
self.weights.append(w)
|
|
56
53
|
|
|
57
54
|
self.path = np.array(self.path)
|
|
@@ -61,12 +58,12 @@ class CFR:
|
|
|
61
58
|
# A_mat = -1/2 *np.diag(1, -1) + np.diag(1, 1)
|
|
62
59
|
# B_mat = np.diag([2*i-1 for i in range(1, self.nz)])
|
|
63
60
|
# eigp, eigv = eig(A_mat, B_mat)
|
|
64
|
-
# zp = 1j / eigp *
|
|
61
|
+
# zp = 1j / eigp * kB * self.T
|
|
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/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="./",
|