TB2J 0.9.9rc6__py3-none-any.whl → 0.9.9rc7__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 +11 -9
- TB2J/MAEGreen.py +96 -22
- TB2J/anisotropy.py +104 -30
- TB2J/exchange.py +0 -11
- TB2J/interfaces/siesta_interface.py +2 -1
- TB2J/io_exchange/io_exchange.py +44 -5
- TB2J/io_exchange/io_matjes.py +225 -0
- TB2J/mathutils/fibonacci_sphere.py +74 -0
- TB2J/orbital_magmom.py +36 -0
- TB2J/symmetrize_J.py +23 -12
- {TB2J-0.9.9rc6.dist-info → tb2j-0.9.9rc7.dist-info}/METADATA +16 -7
- {TB2J-0.9.9rc6.dist-info → tb2j-0.9.9rc7.dist-info}/RECORD +26 -23
- {TB2J-0.9.9rc6.dist-info → tb2j-0.9.9rc7.dist-info}/WHEEL +1 -1
- {TB2J-0.9.9rc6.data → tb2j-0.9.9rc7.data}/scripts/TB2J_downfold.py +0 -0
- {TB2J-0.9.9rc6.data → tb2j-0.9.9rc7.data}/scripts/TB2J_eigen.py +0 -0
- {TB2J-0.9.9rc6.data → tb2j-0.9.9rc7.data}/scripts/TB2J_magnon.py +0 -0
- {TB2J-0.9.9rc6.data → tb2j-0.9.9rc7.data}/scripts/TB2J_magnon_dos.py +0 -0
- {TB2J-0.9.9rc6.data → tb2j-0.9.9rc7.data}/scripts/TB2J_merge.py +0 -0
- {TB2J-0.9.9rc6.data → tb2j-0.9.9rc7.data}/scripts/TB2J_rotate.py +0 -0
- {TB2J-0.9.9rc6.data → tb2j-0.9.9rc7.data}/scripts/TB2J_rotateDM.py +0 -0
- {TB2J-0.9.9rc6.data → tb2j-0.9.9rc7.data}/scripts/abacus2J.py +0 -0
- {TB2J-0.9.9rc6.data → tb2j-0.9.9rc7.data}/scripts/siesta2J.py +0 -0
- {TB2J-0.9.9rc6.data → tb2j-0.9.9rc7.data}/scripts/wann2J.py +0 -0
- {TB2J-0.9.9rc6.dist-info → tb2j-0.9.9rc7.dist-info}/entry_points.txt +0 -0
- {TB2J-0.9.9rc6.dist-info → tb2j-0.9.9rc7.dist-info/licenses}/LICENSE +0 -0
- {TB2J-0.9.9rc6.dist-info → tb2j-0.9.9rc7.dist-info}/top_level.txt +0 -0
TB2J/Jdownfolder.py
CHANGED
@@ -1,7 +1,9 @@
|
|
1
1
|
import os
|
2
2
|
from collections import defaultdict
|
3
|
+
|
3
4
|
import numpy as np
|
4
5
|
from ase.dft.kpoints import monkhorst_pack
|
6
|
+
|
5
7
|
from TB2J.io_exchange import SpinIO
|
6
8
|
from TB2J.Jtensor import decompose_J_tensor
|
7
9
|
|
@@ -73,8 +75,8 @@ class JDownfolder:
|
|
73
75
|
class PWFDownfolder:
|
74
76
|
def __init__(self, JR, Rlist, iM, iL, qmesh, atoms=None, iso_only=False, **kwargs):
|
75
77
|
from lawaf.interfaces.magnon.magnon_downfolder import (
|
76
|
-
MagnonWrapper,
|
77
78
|
MagnonDownfolder,
|
79
|
+
MagnonWrapper,
|
78
80
|
)
|
79
81
|
|
80
82
|
model = MagnonWrapper(JR, Rlist, atoms)
|
@@ -92,13 +94,13 @@ class PWFDownfolder:
|
|
92
94
|
# anchors={(0, 0, 0): (-1, -2, -3, -4)},
|
93
95
|
# anchors={(0, 0, 0): ()},
|
94
96
|
# use_proj=True,
|
95
|
-
enhance_Amn=
|
97
|
+
enhance_Amn=0.0,
|
96
98
|
)
|
97
99
|
params.update(kwargs)
|
98
100
|
wann.set_parameters(**params)
|
99
101
|
print("begin downfold")
|
100
102
|
ewf = wann.downfold()
|
101
|
-
ewf.save_hr_pickle("downfolded_JR.pickle")
|
103
|
+
# ewf.save_hr_pickle("downfolded_JR.pickle")
|
102
104
|
|
103
105
|
# Plot the band structure.
|
104
106
|
wann.plot_band_fitting(
|
@@ -135,7 +137,7 @@ class JDownfolder_pickle:
|
|
135
137
|
qmesh=[7, 7, 7],
|
136
138
|
iso_only=False,
|
137
139
|
method="pwf",
|
138
|
-
**kwargs
|
140
|
+
**kwargs,
|
139
141
|
):
|
140
142
|
self.exc = SpinIO.load_pickle(path=inpath, fname="TB2J.pickle")
|
141
143
|
|
@@ -193,7 +195,7 @@ class JDownfolder_pickle:
|
|
193
195
|
qmesh=self.qmesh,
|
194
196
|
atoms=self.atoms,
|
195
197
|
iso_only=self.iso_only,
|
196
|
-
**kwargs
|
198
|
+
**kwargs,
|
197
199
|
)
|
198
200
|
Jd, Rlist = d.get_JR()
|
199
201
|
return Jd, Rlist
|
@@ -274,10 +276,10 @@ class JDownfolder_pickle:
|
|
274
276
|
def test():
|
275
277
|
# pass
|
276
278
|
# inpath = "/home/hexu/projects/NiCl2/vasp_inputs/TB2J_results"
|
277
|
-
|
278
|
-
inpath = "/home/hexu/projects/TB2J_projects/NiCl2/TB2J_NiCl/TB2J_results"
|
279
|
-
|
280
|
-
|
279
|
+
inpath = "/home/hexu/projects/TB2J_example/CrI3/TB2J_results"
|
280
|
+
# inpath = "/home/hexu/projects/TB2J_projects/NiCl2/TB2J_NiCl/TB2J_results"
|
281
|
+
_fname = os.path.join(inpath, "TB2J.pickle")
|
282
|
+
_p = JDownfolder_pickle(
|
281
283
|
inpath=inpath, metals=["Ni"], ligands=["Cl"], outpath="TB2J_results_downfolded"
|
282
284
|
)
|
283
285
|
|
TB2J/MAEGreen.py
CHANGED
@@ -1,13 +1,17 @@
|
|
1
|
+
import gc
|
1
2
|
from pathlib import Path
|
2
3
|
|
4
|
+
import matplotlib.pyplot as plt
|
3
5
|
import numpy as np
|
4
6
|
import tqdm
|
5
7
|
from HamiltonIO.abacus.abacus_wrapper import AbacusSplitSOCParser
|
6
8
|
from HamiltonIO.model.occupations import GaussOccupations
|
7
9
|
from typing_extensions import DefaultDict
|
8
10
|
|
11
|
+
from TB2J.anisotropy import Anisotropy
|
9
12
|
from TB2J.exchange import ExchangeNCL
|
10
13
|
from TB2J.external import p_map
|
14
|
+
from TB2J.mathutils.fibonacci_sphere import fibonacci_semisphere
|
11
15
|
|
12
16
|
# from HamiltonIO.model.rotate_spin import rotate_Matrix_from_z_to_axis, rotate_Matrix_from_z_to_sperical
|
13
17
|
# from TB2J.abacus.abacus_wrapper import AbacusWrapper, AbacusParser
|
@@ -23,12 +27,21 @@ def get_occupation(evals, kweights, nel, width=0.1):
|
|
23
27
|
|
24
28
|
class MAEGreen(ExchangeNCL):
|
25
29
|
def __init__(self, angles=None, **kwargs):
|
30
|
+
"""
|
31
|
+
angles are defined as theta, phi pairs, where theta is the angle between the z-axis and the magnetization direction, and phi is the angle between the x-axis and the projection of the magnetization direction on the x-y plane.
|
32
|
+
"""
|
26
33
|
super().__init__(**kwargs)
|
27
34
|
self.natoms = len(self.atoms)
|
28
35
|
if angles is None or angles == "axis":
|
29
36
|
self.set_angles_axis()
|
30
37
|
elif angles == "scan":
|
31
38
|
self.set_angles_scan()
|
39
|
+
elif angles == "fib":
|
40
|
+
self.set_angles_fib()
|
41
|
+
elif angles == "random":
|
42
|
+
self.set_angles_random()
|
43
|
+
elif angles == "miller":
|
44
|
+
self.set_angles_miller()
|
32
45
|
else:
|
33
46
|
self.thetas = angles[0]
|
34
47
|
self.phis = angles[1]
|
@@ -39,9 +52,32 @@ class MAEGreen(ExchangeNCL):
|
|
39
52
|
self.es_atom_orb = DefaultDict(lambda: 0)
|
40
53
|
|
41
54
|
def set_angles_axis(self):
|
42
|
-
"""theta and phi are defined as the x, y, z, axis."""
|
43
|
-
self.thetas = [0, np.pi / 2, np.pi / 2, np.pi / 2, np.pi]
|
44
|
-
self.phis = [0, 0, np.pi / 2, np.pi / 4, 0]
|
55
|
+
"""theta and phi are defined as the x, y, z, xy, yz, xz, xyz, x-yz, -xyz, -x-yz axis."""
|
56
|
+
self.thetas = [0, np.pi / 2, np.pi / 2, np.pi / 2, np.pi, 0, np.pi / 2, 0, 0, 0]
|
57
|
+
self.phis = [0, 0, np.pi / 2, np.pi / 4, 0, 0, 0, np.pi]
|
58
|
+
|
59
|
+
def set_angles_miller(self, nmax=2):
|
60
|
+
"""theta and angles corresponding to the miller index. remove duplicates.
|
61
|
+
e.g. 002 and 001 are the same.
|
62
|
+
"""
|
63
|
+
thetas = []
|
64
|
+
phis = []
|
65
|
+
for k in range(0, nmax + 1):
|
66
|
+
for j in range(-nmax, nmax + 1):
|
67
|
+
for i in range(-nmax, nmax + 1):
|
68
|
+
if i == 0 and j == 0 and k == 0:
|
69
|
+
continue
|
70
|
+
thetas.append(np.arccos(k / np.sqrt(i**2 + j**2 + k**2)))
|
71
|
+
if i == 0 and j == 0:
|
72
|
+
phis.append(0)
|
73
|
+
else:
|
74
|
+
phis.append(np.arctan2(j, i))
|
75
|
+
self.thetas = thetas
|
76
|
+
self.phis = phis
|
77
|
+
self.angle_pairs = list(zip(thetas, phis))
|
78
|
+
# remove duplicates of angles using sets.
|
79
|
+
self.angle_pairs = list(set(self.angle_pairs))
|
80
|
+
self.thetas, self.phis = zip(*self.angle_pairs)
|
45
81
|
|
46
82
|
def set_angles_scan(self, step=15):
|
47
83
|
self.thetas = []
|
@@ -51,6 +87,21 @@ class MAEGreen(ExchangeNCL):
|
|
51
87
|
self.thetas.append(i * np.pi / 180)
|
52
88
|
self.phis.append(j * np.pi / 180)
|
53
89
|
|
90
|
+
def set_angles_random(self, n=16):
|
91
|
+
# n random pairs of theta, phi
|
92
|
+
self.thetas = np.random.random(n) * np.pi
|
93
|
+
self.phis = np.random.random(n) * 2 * np.pi
|
94
|
+
|
95
|
+
def set_angles_fib(self, n=35):
|
96
|
+
self.thetas, self.phis = fibonacci_semisphere(n)
|
97
|
+
# thetas and phis are np.array
|
98
|
+
# add (theta, phi): (pi/2, pi/2) and (pi/2, pi/4)
|
99
|
+
# self.thetas += [np.pi / 2, np.pi / 2]
|
100
|
+
# self.phis += [np.pi / 2, np.pi / 4]
|
101
|
+
for i in range(8):
|
102
|
+
self.thetas = np.concatenate([self.thetas, [np.pi / 2]])
|
103
|
+
self.phis = np.concatenate([self.phis, [np.pi * i / 8]])
|
104
|
+
|
54
105
|
def get_band_energy_vs_angles_from_eigen(
|
55
106
|
self,
|
56
107
|
thetas,
|
@@ -79,16 +130,12 @@ class MAEGreen(ExchangeNCL):
|
|
79
130
|
|
80
131
|
def get_perturbed(self, e, thetas, phis):
|
81
132
|
self.tbmodel.set_so_strength(0.0)
|
82
|
-
maxsoc = self.tbmodel.get_max_Hsoc_abs()
|
83
|
-
maxH0 = self.tbmodel.get_max_H0_spin_abs()
|
84
|
-
if maxsoc > maxH0 * 0.01:
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
print(f"""Warning: The SOC of the Hamiltonian has a maximum of {maxsoc} eV,
|
90
|
-
comparing to the maximum of {maxH0} eV of the spin part of the Hamiltonian.
|
91
|
-
The SOC is too strong, the perturbation theory may not be valid.""")
|
133
|
+
# maxsoc = self.tbmodel.get_max_Hsoc_abs()
|
134
|
+
# maxH0 = self.tbmodel.get_max_H0_spin_abs()
|
135
|
+
# if maxsoc > maxH0 * 0.01:
|
136
|
+
# print(f"""Warning: The SOC of the Hamiltonian has a maximum of {maxsoc} eV,
|
137
|
+
# comparing to the maximum of {maxH0} eV of the spin part of the Hamiltonian.
|
138
|
+
# The SOC is too strong, the perturbation theory may not be valid.""")
|
92
139
|
|
93
140
|
G0K = self.G.get_Gk_all(e)
|
94
141
|
Hsoc_k = self.tbmodel.get_Hk_soc(self.G.kpts)
|
@@ -104,12 +151,12 @@ class MAEGreen(ExchangeNCL):
|
|
104
151
|
GdH = G0K[ik] @ dHi
|
105
152
|
# dE += np.trace(GdH @ G0K[i].T.conj() @ dHi) * self.kweights[i]
|
106
153
|
# diagonal of second order perturbation.
|
107
|
-
dG2diag = np.diag(GdH @ GdH)
|
154
|
+
# dG2diag = np.diag(GdH @ GdH)
|
108
155
|
# dG2 = np.einsum("ij,ji->ij", GdH, GdH)
|
109
156
|
dG2 = GdH * GdH.T
|
110
157
|
dG2sum = np.sum(dG2)
|
111
158
|
# print(f"dG2sum-sum: {dG2sum}")
|
112
|
-
dG2sum = np.sum(dG2diag)
|
159
|
+
# dG2sum = np.sum(dG2diag)
|
113
160
|
|
114
161
|
# dG2sum = np.trace(GdH @ GdH)
|
115
162
|
# print(f"dG2sum-Tr: {dG2sum}")
|
@@ -130,13 +177,11 @@ class MAEGreen(ExchangeNCL):
|
|
130
177
|
+ dE_atom_orb[1::2, ::2]
|
131
178
|
+ dE_atom_orb[::2, 1::2]
|
132
179
|
)
|
180
|
+
dE_atom = np.sum(dE_atom_orb)
|
133
181
|
mmat = self.mmats[iatom]
|
134
182
|
dE_atom_orb = mmat.T @ dE_atom_orb @ mmat
|
135
|
-
|
136
183
|
dE_angle_atom_orb[(iangle, iatom)] += dE_atom_orb
|
137
|
-
|
138
|
-
dE_atom = np.sum(dG2diag[iorb]) * self.G.kweights[ik]
|
139
|
-
# dE_atom = np.sum(dE_atom_orb)
|
184
|
+
# dE_atom = np.sum(dG2diag[iorb]) * self.G.kweights[ik]
|
140
185
|
dE_angle_atom[iangle, iatom] += dE_atom
|
141
186
|
return dE_angle, dE_angle_atom, dE_angle_atom_orb
|
142
187
|
|
@@ -189,10 +234,25 @@ class MAEGreen(ExchangeNCL):
|
|
189
234
|
for key, value in self.es_atom_orb.items():
|
190
235
|
self.es_atom_orb[key] = -np.imag(value) / (2 * np.pi)
|
191
236
|
|
192
|
-
def
|
237
|
+
def fit_anisotropy_tensor(self):
|
238
|
+
self.ani = Anisotropy.fit_from_data(self.thetas, self.phis, self.es)
|
239
|
+
return self.ani
|
240
|
+
|
241
|
+
def output(
|
242
|
+
self,
|
243
|
+
output_path="TB2J_anisotropy",
|
244
|
+
with_eigen=True,
|
245
|
+
figure3d="MAE_3d.png",
|
246
|
+
figure_contourf="MAE_contourf.png",
|
247
|
+
):
|
193
248
|
Path(output_path).mkdir(exist_ok=True)
|
194
249
|
fname = f"{output_path}/MAE.dat"
|
195
250
|
fname_orb = f"{output_path}/MAE_orb.dat"
|
251
|
+
fname_tensor = f"{output_path}/MAE_tensor.dat"
|
252
|
+
if figure3d is not None:
|
253
|
+
fname_fig3d = f"{output_path}/{figure3d}"
|
254
|
+
if figure_contourf is not None:
|
255
|
+
fname_figcontourf = f"{output_path}/{figure_contourf}"
|
196
256
|
|
197
257
|
# ouput with eigenvalues.
|
198
258
|
if with_eigen:
|
@@ -208,15 +268,29 @@ class MAEGreen(ExchangeNCL):
|
|
208
268
|
f.write("\n")
|
209
269
|
|
210
270
|
with open(fname, "w") as f:
|
211
|
-
f.write("# theta, phi, MAE(total), MAE(atom-wise) Unit: meV\n")
|
271
|
+
f.write("# theta (rad), phi(rad), MAE(total), MAE(atom-wise) Unit: meV\n")
|
212
272
|
for i, (theta, phi, e, es) in enumerate(
|
213
273
|
zip(self.thetas, self.phis, self.es, self.es_atom)
|
214
274
|
):
|
215
|
-
f.write(f"{theta:.5f} {phi:.5f} {e*1e3:.8f} ")
|
275
|
+
f.write(f"{theta%np.pi:.5f} {phi%(2*np.pi):.5f} {e*1e3:.8f} ")
|
216
276
|
for ea in es:
|
217
277
|
f.write(f"{ea*1e3:.8f} ")
|
218
278
|
f.write("\n")
|
219
279
|
|
280
|
+
self.ani = self.fit_anisotropy_tensor()
|
281
|
+
with open(fname_tensor, "w") as f:
|
282
|
+
f.write("# Anisotropy tensor in meV\n")
|
283
|
+
f.write(f"{self.ani.tensor_strings(include_isotropic=False)}\n")
|
284
|
+
|
285
|
+
if figure3d is not None:
|
286
|
+
self.ani.plot_3d(figname=fname_fig3d, show=False)
|
287
|
+
|
288
|
+
if figure_contourf is not None:
|
289
|
+
self.ani.plot_contourf(figname=fname_figcontourf, show=False)
|
290
|
+
|
291
|
+
plt.close()
|
292
|
+
gc.collect()
|
293
|
+
|
220
294
|
with open(fname_orb, "w") as f:
|
221
295
|
f.write("=" * 80 + "\n")
|
222
296
|
f.write("Orbitals for each atom: \n")
|
TB2J/anisotropy.py
CHANGED
@@ -1,11 +1,12 @@
|
|
1
1
|
import random
|
2
|
-
|
2
|
+
from dataclasses import dataclass
|
3
|
+
from pathlib import Path
|
4
|
+
|
3
5
|
import matplotlib.pyplot as plt
|
6
|
+
import numpy as np
|
7
|
+
from numpy.linalg import matrix_rank
|
4
8
|
from scipy.interpolate import LinearNDInterpolator
|
5
9
|
from scipy.optimize import curve_fit
|
6
|
-
from numpy.linalg import matrix_rank
|
7
|
-
from dataclasses import dataclass
|
8
|
-
from pathlib import Path
|
9
10
|
|
10
11
|
|
11
12
|
@dataclass
|
@@ -18,8 +19,9 @@ class Anisotropy:
|
|
18
19
|
@classmethod
|
19
20
|
def from_T6(cls, T6):
|
20
21
|
T = T6_to_T(T6)
|
21
|
-
|
22
|
-
|
22
|
+
isotropic_part = np.trace(T) / 3
|
23
|
+
T -= isotropic_part * np.eye(3)
|
24
|
+
direction, amplitude = anisotropy_tensor_to_vector(T)
|
23
25
|
return cls(
|
24
26
|
direction=direction, amplitude=amplitude, isotropic_part=isotropic_part, T=T
|
25
27
|
)
|
@@ -33,9 +35,9 @@ class Anisotropy:
|
|
33
35
|
|
34
36
|
@classmethod
|
35
37
|
def from_tensor(cls, T):
|
36
|
-
isotropic_part = np.trace(T) / 3
|
38
|
+
isotropic_part = np.trace(T) / 3 * np.eye(3)
|
37
39
|
T -= np.trace(T) / 3 * np.eye(3)
|
38
|
-
direction, amplitude =
|
40
|
+
direction, amplitude = anisotropy_tensor_to_vector(T)
|
39
41
|
return cls(
|
40
42
|
T=T, direction=direction, amplitude=amplitude, isotropic_part=isotropic_part
|
41
43
|
)
|
@@ -44,13 +46,14 @@ class Anisotropy:
|
|
44
46
|
if self.isotropic_part is None:
|
45
47
|
self.isotropic_part = 0
|
46
48
|
if self.T is None:
|
47
|
-
self.T =
|
48
|
-
self.direction, self.amplitude
|
49
|
-
|
49
|
+
self.T = (
|
50
|
+
anisotropy_vector_to_tensor(self.direction, self.amplitude)
|
51
|
+
+ self.isotropic_part
|
52
|
+
)
|
50
53
|
elif self.direction is None or self.amplitude is None:
|
51
|
-
self.isotropic_part = np.trace(self.T) / 3
|
52
|
-
# print(f'
|
53
|
-
self.direction, self.amplitude =
|
54
|
+
self.isotropic_part = np.trace(self.T) / 3 * np.eye(3)
|
55
|
+
# print(f'anisotropic tensor = {self.anisotropic_part}')
|
56
|
+
self.direction, self.amplitude = anisotropy_tensor_to_vector(
|
54
57
|
self.anisotropic_part
|
55
58
|
)
|
56
59
|
self.isotropic_part = np.trace(self.T) / 3
|
@@ -113,7 +116,6 @@ class Anisotropy:
|
|
113
116
|
E = -self.amplitude * (S @ self.direction) ** 2
|
114
117
|
if include_isotropic:
|
115
118
|
E = E + self.isotropic_part
|
116
|
-
print(f"E shape = {E.shape}")
|
117
119
|
return E
|
118
120
|
|
119
121
|
def energy_tensor_form(self, S=None, angle=None, include_isotropic=False):
|
@@ -126,7 +128,7 @@ class Anisotropy:
|
|
126
128
|
return -S.T @ self.anisotropic_part @ S
|
127
129
|
|
128
130
|
@classmethod
|
129
|
-
def fit_from_data(cls, thetas, phis, values, test=False):
|
131
|
+
def fit_from_data(cls, thetas, phis, values, test=False, units="rad"):
|
130
132
|
"""
|
131
133
|
Fit the anisotropic tensor to the data
|
132
134
|
parameters:
|
@@ -137,14 +139,16 @@ class Anisotropy:
|
|
137
139
|
the anisotropic object fitted from the data
|
138
140
|
"""
|
139
141
|
angles = np.vstack([thetas, phis])
|
142
|
+
if units.lower().startswith("deg"):
|
143
|
+
angles = np.deg2rad(angles)
|
140
144
|
params, cov = curve_fit(anisotropy_energy, angles, values)
|
141
145
|
fitted_values = anisotropy_energy(angles, *params)
|
142
146
|
|
143
147
|
delta = fitted_values - values
|
144
148
|
|
145
149
|
# print(f'Max value = {np.max(values)}, Min value = {np.min(values)}')
|
146
|
-
if np.abs(delta).max() > 1e-
|
147
|
-
print(
|
150
|
+
if np.abs(delta).max() > 1e-4:
|
151
|
+
print("Warning: The fitting is not consistent with the data.")
|
148
152
|
print(f"Max-min = {np.max(values) - np.min(values)}")
|
149
153
|
print(f"delta = {np.max(np.abs(delta))}")
|
150
154
|
T = T6_to_T(params)
|
@@ -157,7 +161,7 @@ class Anisotropy:
|
|
157
161
|
angle=[thetas[i], phis[i]], include_isotropic=True
|
158
162
|
)
|
159
163
|
values2.append(E)
|
160
|
-
delta2 = np.array(values2) - values
|
164
|
+
# delta2 = np.array(values2) - values
|
161
165
|
# print(delta2)
|
162
166
|
|
163
167
|
ax = plot_3D_scatter(angles, values - np.min(values), color="r")
|
@@ -183,7 +187,7 @@ class Anisotropy:
|
|
183
187
|
delta = fitted_values - values
|
184
188
|
print(f"Max value = {np.max(values)}, Min value = {np.min(values)}")
|
185
189
|
print(f"Max-min = {np.max(values) - np.min(values)}")
|
186
|
-
|
190
|
+
print(f"delta = {delta}")
|
187
191
|
theta_a, phi_a, amplitude, isotropic_part = params
|
188
192
|
direction = sphere_to_cartesian([theta_a, phi_a])
|
189
193
|
return cls.from_direction_amplitude(
|
@@ -208,6 +212,25 @@ class Anisotropy:
|
|
208
212
|
else:
|
209
213
|
raise ValueError(f"Unknown method {method}")
|
210
214
|
|
215
|
+
@classmethod
|
216
|
+
def fit_from_xyz_data_file(cls, fname, method="tensor"):
|
217
|
+
"""
|
218
|
+
Fit the anisotropic tensor to the data with x y z val form
|
219
|
+
parameters:
|
220
|
+
fname: the file name of the data
|
221
|
+
Return:
|
222
|
+
anisotropy: the anisotropic object
|
223
|
+
"""
|
224
|
+
data = np.loadtxt(fname)
|
225
|
+
xyz, value = data[:, 0:3], data[:, 3]
|
226
|
+
theta, phi = np.array([cartesian_to_sphere(t) for t in xyz]).T
|
227
|
+
if method == "tensor":
|
228
|
+
return cls.fit_from_data(theta, phi, value)
|
229
|
+
elif method == "vector":
|
230
|
+
return cls.fit_from_data_vector_form(theta, phi, value)
|
231
|
+
else:
|
232
|
+
raise ValueError(f"Unknown method {method}")
|
233
|
+
|
211
234
|
def plot_3d(self, ax=None, figname=None, show=True, surface=True):
|
212
235
|
"""
|
213
236
|
plot the anisotropic energy in all directions in 3D
|
@@ -297,6 +320,55 @@ class Anisotropy:
|
|
297
320
|
if show:
|
298
321
|
plt.show()
|
299
322
|
|
323
|
+
def plot_contourf(self, ax=None, figname=None, show=False):
|
324
|
+
if ax is None:
|
325
|
+
fig, ax = plt.subplots()
|
326
|
+
X, Y = np.meshgrid(np.arange(0, 180, 1), np.arange(-180, 180, 1))
|
327
|
+
Z = np.zeros(X.shape)
|
328
|
+
ntheta, nphi = X.shape
|
329
|
+
for i in range(ntheta):
|
330
|
+
for j in range(nphi):
|
331
|
+
E = self.energy_tensor_form(angle=[X[i, j], Y[i, j]])
|
332
|
+
Z[i, j] = E
|
333
|
+
# find the X, Y for min and max of Z
|
334
|
+
X_max, Y_max = np.unravel_index(np.argmax(Z), Z.shape)
|
335
|
+
X_min, Y_min = np.unravel_index(np.argmin(Z), Z.shape)
|
336
|
+
X_max, Y_max = X[X_max, Y_max], Y[X_max, Y_max]
|
337
|
+
X_min, Y_min = X[X_min, Y_min], Y[X_min, Y_min]
|
338
|
+
c = ax.contourf(X, Y, Z, cmap="viridis", levels=200)
|
339
|
+
# print(X_max, Y_max, X_min, Y_min)
|
340
|
+
# ax.scatter(X_max, Y_max, color="r", marker="o")
|
341
|
+
# ax.scatter(X_min, Y_min, color="b", marker="o")
|
342
|
+
ax.set_xlabel("$\theta$ (degree)")
|
343
|
+
ax.set_ylabel("$\phi$ degree")
|
344
|
+
# ax.scatter(X_max, Y_max, color="r", marker="o")
|
345
|
+
# ax.scatter(X_min, Y_min, color="r", marker="o")
|
346
|
+
|
347
|
+
# colorbar
|
348
|
+
_cbar = plt.colorbar(c, ax=ax)
|
349
|
+
|
350
|
+
if figname is not None:
|
351
|
+
plt.savefig(figname)
|
352
|
+
plt.close()
|
353
|
+
if show:
|
354
|
+
print(f"Max = {X_max}, {Y_max}, Min = {X_min}, {Y_min}")
|
355
|
+
plt.show()
|
356
|
+
return ax
|
357
|
+
|
358
|
+
def tensor_strings(self, include_isotropic=False, multiplier=1):
|
359
|
+
"""
|
360
|
+
convert the energy tensor to strings for easy printing
|
361
|
+
parameters:
|
362
|
+
include_isotropic: if include the isotropic part
|
363
|
+
multiplier: the multiplier for the tensor. Use for scaling the tensor by units.
|
364
|
+
"""
|
365
|
+
if include_isotropic:
|
366
|
+
T = self.T
|
367
|
+
else:
|
368
|
+
T = self.T
|
369
|
+
strings = np.array2string(T * multiplier, precision=5, separator=" ")
|
370
|
+
return strings
|
371
|
+
|
300
372
|
|
301
373
|
def plot_3D_scatter(angles, values, ax=None, **kwargs):
|
302
374
|
if ax is None:
|
@@ -389,7 +461,7 @@ def sphere_to_cartesian(angles, r=1):
|
|
389
461
|
return np.array([x, y, z])
|
390
462
|
|
391
463
|
|
392
|
-
def cartesian_to_sphere(xyz):
|
464
|
+
def cartesian_to_sphere(xyz, unit="deg"):
|
393
465
|
"""
|
394
466
|
Transform the cartesian coordinates to the spherical coordinates
|
395
467
|
parameters:
|
@@ -401,8 +473,13 @@ def cartesian_to_sphere(xyz):
|
|
401
473
|
r = np.linalg.norm(xyz)
|
402
474
|
theta = np.arccos(z / r)
|
403
475
|
phi = np.arctan2(y, x)
|
404
|
-
|
405
|
-
|
476
|
+
if unit.lower().startswith("deg"):
|
477
|
+
theta = theta * 180 / np.pi
|
478
|
+
phi = phi * 180 / np.pi
|
479
|
+
elif unit.lower.startswith("rad"):
|
480
|
+
pass
|
481
|
+
else:
|
482
|
+
raise ValueError("unit must be 'deg' or 'rad'")
|
406
483
|
return np.array([theta, phi])
|
407
484
|
|
408
485
|
|
@@ -470,7 +547,7 @@ def fit_anisotropy(thetas, phis, values):
|
|
470
547
|
params, cov = curve_fit(anisotropy_energy, angles, values)
|
471
548
|
# check if the fitting is consistent with the data
|
472
549
|
T = T6_to_T(params)
|
473
|
-
direction, amp =
|
550
|
+
direction, amp = anisotropy_tensor_to_vector(T)
|
474
551
|
return direction, amp
|
475
552
|
|
476
553
|
|
@@ -518,7 +595,7 @@ def anisotropy_vector_to_tensor(direction, amplitude):
|
|
518
595
|
return T
|
519
596
|
|
520
597
|
|
521
|
-
def
|
598
|
+
def anisotropy_tensor_to_vector(T):
|
522
599
|
"""
|
523
600
|
Transform the anisotropic tensor to the anisotropic vector
|
524
601
|
parameters:
|
@@ -553,7 +630,7 @@ def test_anisotorpy_vector_to_tensor():
|
|
553
630
|
assert np.abs(diff) < 1e-10
|
554
631
|
|
555
632
|
# test if the inverse transformation get the direction and amplitude back
|
556
|
-
dir2, amp2 =
|
633
|
+
dir2, amp2 = anisotropy_tensor_to_vector(T)
|
557
634
|
|
558
635
|
# set the first element of the direction to be positive
|
559
636
|
if direction[0] * dir2[0] < 0:
|
@@ -569,7 +646,7 @@ def test_anisotropy_tensor_to_vector():
|
|
569
646
|
T = np.random.rand(3, 3)
|
570
647
|
T = T + T.T
|
571
648
|
T = T - np.trace(T) / 3
|
572
|
-
direction, amplitude =
|
649
|
+
direction, amplitude = anisotropy_tensor_to_vector(T)
|
573
650
|
T2 = anisotropy_vector_to_tensor(direction, amplitude)
|
574
651
|
print(f"T = {T}")
|
575
652
|
print(f"T2 = {T2}")
|
@@ -597,9 +674,6 @@ def test_fit_anisotropy():
|
|
597
674
|
f"theta = {theta[i]}, phi = {phi[i]}, value = {value[i]}, fitted value = {fitted_values[i]}, delta = {delta[i]}"
|
598
675
|
)
|
599
676
|
|
600
|
-
easy_axis = easy_axis_from_tensor(T6)
|
601
|
-
print(f"easy_axis = {easy_axis}")
|
602
|
-
|
603
677
|
|
604
678
|
def view_anisotropy_strain():
|
605
679
|
strains1 = [(x, 0.0) for x in np.arange(0.000, 0.021, 0.002)]
|
TB2J/exchange.py
CHANGED
@@ -566,23 +566,12 @@ class ExchangeNCL(Exchange):
|
|
566
566
|
AijRs: a list of AijR,
|
567
567
|
wherer AijR: array of ((nR, n, n, 4,4), dtype=complex)
|
568
568
|
"""
|
569
|
-
# if method == "trapezoidal":
|
570
|
-
# integrate = trapezoidal_nonuniform
|
571
|
-
# elif method == "simpson":
|
572
|
-
# integrate = simpson_nonuniform
|
573
|
-
#
|
574
|
-
|
575
|
-
# self.rho = integrate(self.contour.path, rhoRs)
|
576
569
|
for iR, R in enumerate(self.R_ijatom_dict):
|
577
570
|
for iatom, jatom in self.R_ijatom_dict[R]:
|
578
571
|
f = AijRs[(R, iatom, jatom)]
|
579
|
-
# self.A_ijR[(R, iatom, jatom)] = integrate(self.contour.path, f)
|
580
572
|
self.A_ijR[(R, iatom, jatom)] = self.contour.integrate_values(f)
|
581
573
|
|
582
574
|
if self.orb_decomposition:
|
583
|
-
# self.A_ijR_orb[(R, iatom, jatom)] = integrate(
|
584
|
-
# self.contour.path, AijRs_orb[(R, iatom, jatom)]
|
585
|
-
# )
|
586
575
|
self.contour.integrate_values(AijRs_orb[(R, iatom, jatom)])
|
587
576
|
|
588
577
|
def get_quantities_per_e(self, e):
|
@@ -156,6 +156,7 @@ Warning: The DMI component parallel to the spin orientation, the Jani which has
|
|
156
156
|
atoms=model.atoms,
|
157
157
|
basis=basis,
|
158
158
|
efermi=None,
|
159
|
+
angles="miller",
|
159
160
|
# magnetic_elements=magnetic_elements,
|
160
161
|
# include_orbs=include_orbs,
|
161
162
|
**exargs,
|
@@ -163,7 +164,7 @@ Warning: The DMI component parallel to the spin orientation, the Jani which has
|
|
163
164
|
# thetas = [0, np.pi / 2, np.pi, 3 * np.pi / 2]
|
164
165
|
# phis = [0, 0, 0, 0]
|
165
166
|
# MAE.set_angles(thetas=thetas, phis=phis)
|
166
|
-
MAE.run(output_path=f"{output_path}_anisotropy", with_eigen=
|
167
|
+
MAE.run(output_path=f"{output_path}_anisotropy", with_eigen=False)
|
167
168
|
print(
|
168
169
|
f"MAE calculation finished. The results are in {output_path} directory."
|
169
170
|
)
|
TB2J/io_exchange/io_exchange.py
CHANGED
@@ -13,6 +13,11 @@ import pickle
|
|
13
13
|
from collections.abc import Iterable
|
14
14
|
from datetime import datetime
|
15
15
|
|
16
|
+
import matplotlib
|
17
|
+
|
18
|
+
matplotlib.use("Agg")
|
19
|
+
import gc
|
20
|
+
|
16
21
|
import matplotlib.pyplot as plt
|
17
22
|
import numpy as np
|
18
23
|
|
@@ -320,7 +325,11 @@ Generation time: {now.strftime("%y/%m/%d %H:%M:%S")}
|
|
320
325
|
i = self.i_spin(i)
|
321
326
|
j = self.i_spin(j)
|
322
327
|
if iso_only:
|
323
|
-
|
328
|
+
J = self.get_Jiso(i, j, R)
|
329
|
+
if J is not None:
|
330
|
+
Jtensor = np.eye(3) * self.get_J(i, j, R)
|
331
|
+
else:
|
332
|
+
Jtensor = np.eye(3) * 0
|
324
333
|
else:
|
325
334
|
Jtensor = combine_J_tensor(
|
326
335
|
Jiso=self.get_J(i, j, R),
|
@@ -329,7 +338,13 @@ Generation time: {now.strftime("%y/%m/%d %H:%M:%S")}
|
|
329
338
|
)
|
330
339
|
return Jtensor
|
331
340
|
|
332
|
-
def
|
341
|
+
def get_J_tensor_dict(self):
|
342
|
+
Jdict = {}
|
343
|
+
for i, j, R in self.ijR_list():
|
344
|
+
Jdict[(i, j, R)] = self.get_J_tensor(i, j, R)
|
345
|
+
return Jdict
|
346
|
+
|
347
|
+
def get_full_Jtensor_for_one_R(self, R, iso_only=False):
|
333
348
|
"""
|
334
349
|
Return the full exchange tensor of all i and j for cell R.
|
335
350
|
param R (tuple of integers): cell index R
|
@@ -340,15 +355,17 @@ Generation time: {now.strftime("%y/%m/%d %H:%M:%S")}
|
|
340
355
|
Jmat = np.zeros((n3, n3), dtype=float)
|
341
356
|
for i in range(self.nspin):
|
342
357
|
for j in range(self.nspin):
|
343
|
-
Jmat[i * 3 : i * 3 + 3, j * 3 : j * 3 + 3] = self.get_J_tensor(
|
358
|
+
Jmat[i * 3 : i * 3 + 3, j * 3 : j * 3 + 3] = self.get_J_tensor(
|
359
|
+
i, j, R, iso_only=iso_only
|
360
|
+
)
|
344
361
|
return Jmat
|
345
362
|
|
346
|
-
def get_full_Jtensor_for_Rlist(self, asr=False, iso_only=
|
363
|
+
def get_full_Jtensor_for_Rlist(self, asr=False, iso_only=True):
|
347
364
|
n3 = self.nspin * 3
|
348
365
|
nR = len(self.Rlist)
|
349
366
|
Jmat = np.zeros((nR, n3, n3), dtype=float)
|
350
367
|
for iR, R in enumerate(self.Rlist):
|
351
|
-
Jmat[iR] = self.get_full_Jtensor_for_one_R(R)
|
368
|
+
Jmat[iR] = self.get_full_Jtensor_for_one_R(R, iso_only=iso_only)
|
352
369
|
if asr:
|
353
370
|
iR0 = np.argmin(np.linalg.norm(self.Rlist, axis=1))
|
354
371
|
assert np.linalg.norm(self.Rlist[iR0]) == 0
|
@@ -394,6 +411,7 @@ Generation time: {now.strftime("%y/%m/%d %H:%M:%S")}
|
|
394
411
|
self.write_multibinit(path=os.path.join(path, "Multibinit"))
|
395
412
|
self.write_tom_format(path=os.path.join(path, "TomASD"))
|
396
413
|
self.write_vampire(path=os.path.join(path, "Vampire"))
|
414
|
+
self.write_matjes(path=os.path.join(path, "Matjes"))
|
397
415
|
|
398
416
|
self.plot_all(savefile=os.path.join(path, "JvsR.pdf"))
|
399
417
|
# self.write_Jq(kmesh=[9, 9, 9], path=path)
|
@@ -544,6 +562,7 @@ Generation time: {now.strftime("%y/%m/%d %H:%M:%S")}
|
|
544
562
|
plt.show()
|
545
563
|
plt.clf()
|
546
564
|
plt.close()
|
565
|
+
gc.collect() # This is to fix the tk error if multiprocess is used.
|
547
566
|
return fig, axes
|
548
567
|
|
549
568
|
def write_tom_format(self, path):
|
@@ -561,6 +580,11 @@ Generation time: {now.strftime("%y/%m/%d %H:%M:%S")}
|
|
561
580
|
|
562
581
|
write_uppasd(self, path=path)
|
563
582
|
|
583
|
+
def write_matjes(self, path):
|
584
|
+
from TB2J.io_exchange.io_matjes import write_matjes
|
585
|
+
|
586
|
+
write_matjes(self, path=path)
|
587
|
+
|
564
588
|
|
565
589
|
def gen_distance_dict(ind_mag_atoms, atoms, Rlist):
|
566
590
|
distance_dict = {}
|
@@ -576,6 +600,21 @@ def gen_distance_dict(ind_mag_atoms, atoms, Rlist):
|
|
576
600
|
return distance_dict
|
577
601
|
|
578
602
|
|
603
|
+
def get_ind_shell(distance_dict, symprec=1e-5):
|
604
|
+
"""
|
605
|
+
return a dictionary of shell index for each pair of atoms.
|
606
|
+
The index of shell is the ith shortest distances between all magnetic atom pairs.
|
607
|
+
"""
|
608
|
+
shell_dict = {}
|
609
|
+
distances = np.array([x[1] for x in distance_dict.values()])
|
610
|
+
distances_int = np.round(distances / symprec).astype(int)
|
611
|
+
dint = sorted(np.unique(distances_int))
|
612
|
+
dintdict = dict(zip(dint, range(len(dint))))
|
613
|
+
for key, val in enumerate(distances_int):
|
614
|
+
shell_dict[key] = dintdict[val]
|
615
|
+
return shell_dict
|
616
|
+
|
617
|
+
|
579
618
|
def test_spin_io():
|
580
619
|
import numpy as np
|
581
620
|
from ase import Atoms
|
@@ -0,0 +1,225 @@
|
|
1
|
+
import os
|
2
|
+
|
3
|
+
import numpy as np
|
4
|
+
|
5
|
+
from TB2J.utils import symbol_number
|
6
|
+
from ase.units import Bohr, nm
|
7
|
+
|
8
|
+
|
9
|
+
def get_ind_shell(distance_dict, symprec=1e-5):
|
10
|
+
"""
|
11
|
+
return a dictionary of shell index for each pair of atoms.
|
12
|
+
The index of shell is the ith shortest distances between all magnetic atom pairs.
|
13
|
+
"""
|
14
|
+
shell_dict = {}
|
15
|
+
distances = np.array([x[1] for x in distance_dict.values()])
|
16
|
+
distances_int = np.round(distances / symprec).astype(int)
|
17
|
+
dint = sorted(np.unique(distances_int))
|
18
|
+
dintdict = dict(zip(dint, range(len(dint))))
|
19
|
+
for key, val in distance_dict.items():
|
20
|
+
di = np.round(val[1] / symprec).astype(int)
|
21
|
+
shell_dict[key] = dintdict[di]
|
22
|
+
return shell_dict
|
23
|
+
|
24
|
+
|
25
|
+
def _write_symmetry(cls, path, fname="symmetry.in", symmetry=True):
|
26
|
+
fname = os.path.join(path, fname)
|
27
|
+
if not symmetry:
|
28
|
+
with open(fname, "w") as myfile:
|
29
|
+
myfile.write("1 \n")
|
30
|
+
myfile.write("\n")
|
31
|
+
myfile.write("! identity operation\n")
|
32
|
+
myfile.write("1.0000000000 0.0000000000 0.0000000000\n")
|
33
|
+
myfile.write("0.0000000000 1.0000000000 0.0000000000\n")
|
34
|
+
myfile.write("0.0000000000 0.0000000000 1.0000000000\n")
|
35
|
+
myfile.write("0.0000000000 0.0000000000 0.0000000000\n")
|
36
|
+
else:
|
37
|
+
raise NotImplementedError("Symmetry not implemented yet")
|
38
|
+
|
39
|
+
|
40
|
+
def write_matjes(cls, path="TB2J_results/Matjes", symmetry=False):
|
41
|
+
if not os.path.exists(path):
|
42
|
+
os.makedirs(path)
|
43
|
+
inputfname = os.path.join(path, "input")
|
44
|
+
with open(inputfname, "w") as myfile:
|
45
|
+
_write_lattice_supercell(cls, myfile)
|
46
|
+
_write_atoms(cls, myfile)
|
47
|
+
_write_magnetic_interactions(cls, myfile)
|
48
|
+
#_write_isotropic_exchange(cls, myfile, symmetry=symmetry)
|
49
|
+
_write_magnetic_anisotropy(cls, myfile)
|
50
|
+
# _write_dmi(cls, myfile)
|
51
|
+
_write_exchange_tensor(cls, myfile, symmetry=symmetry)
|
52
|
+
_write_parameters(cls, myfile, symmetry=symmetry)
|
53
|
+
print(f"writting symmetries ")
|
54
|
+
_write_symmetry(cls, path, fname="symmetries.in", symmetry=False)
|
55
|
+
|
56
|
+
def _write_parameters(cls, myfile, symmetry=False):
|
57
|
+
if symmetry:
|
58
|
+
myfile.write("cal_sym T\n")
|
59
|
+
myfile.write("sym_mode 2\n")
|
60
|
+
else:
|
61
|
+
myfile.write("cal_sym F \n")
|
62
|
+
myfile.write("sym_mode 0\n")
|
63
|
+
|
64
|
+
def _write_lattice_supercell(cls, myfile):
|
65
|
+
myfile.write("# Lattice and supercell\n")
|
66
|
+
myfile.write(
|
67
|
+
"Periodic_log .T. .T. .T. # periodic boundary conditions along vector 1, 2 and 3\n"
|
68
|
+
)
|
69
|
+
myfile.write("Nsize 8 8 8 # size of the supercell\n")
|
70
|
+
try:
|
71
|
+
unitcell = cls.atoms.get_cell().reshape((3, 3))
|
72
|
+
except Exception:
|
73
|
+
unitcell = cls.atoms.get_cell().array.reshape((3, 3))
|
74
|
+
uc_lengths = np.linalg.norm(unitcell, axis=1)
|
75
|
+
unitcell /= uc_lengths[:, None]
|
76
|
+
myfile.write(f"alat {uc_lengths[0]/nm} {uc_lengths[1]/nm} {uc_lengths[2]/nm} #lattice constant lengths\n")
|
77
|
+
myfile.write(
|
78
|
+
"lattice #lattice vector should be orthogonal or expressed in cartesian\n")
|
79
|
+
myfile.write(
|
80
|
+
f"{unitcell[0][0]} {unitcell[0][1]} {unitcell[0][2]} # a_11 a_12 a_1 first lattice vector in line (does not need to be normalize)\n"
|
81
|
+
)
|
82
|
+
myfile.write(
|
83
|
+
f"{unitcell[1][0]} {unitcell[1][1]} {unitcell[1][2]} # a_21 a_22 a_23 second lattice vector in line (does not need to be normalize)\n"
|
84
|
+
)
|
85
|
+
myfile.write(
|
86
|
+
f"{unitcell[2][0]} {unitcell[2][1]} {unitcell[2][2]} # a_31 a_32 a_33 third lattice vector in line\n"
|
87
|
+
)
|
88
|
+
|
89
|
+
|
90
|
+
def get_atoms_info(atoms, spinat, symmetry=False):
|
91
|
+
if symmetry:
|
92
|
+
raise NotImplementedError("Symmetry not implemented yet")
|
93
|
+
else:
|
94
|
+
symnum = symbol_number(atoms)
|
95
|
+
atom_types = list(symnum.keys())
|
96
|
+
magmoms = np.linalg.norm(spinat, axis=1)
|
97
|
+
masses = atoms.get_masses()
|
98
|
+
tags = [i for i in range(len(atom_types))]
|
99
|
+
return atom_types, magmoms, masses, tags
|
100
|
+
|
101
|
+
|
102
|
+
def _write_atoms(cls, myfile, symmetry=False):
|
103
|
+
myfile.write("\n")
|
104
|
+
myfile.write("# Atoms\n")
|
105
|
+
atom_types, magmoms, masses, tags = get_atoms_info(
|
106
|
+
cls.atoms, cls.spinat, symmetry=symmetry
|
107
|
+
)
|
108
|
+
|
109
|
+
myfile.write(f"atomtypes {len(atom_types)} #Number of types atom\n")
|
110
|
+
for i, atom_type in enumerate(atom_types):
|
111
|
+
m = magmoms[i]
|
112
|
+
mass = masses[i]
|
113
|
+
charge = 0
|
114
|
+
myfile.write(
|
115
|
+
f"{atom_type} {m} {mass} 0.0 F 0 # atom type: (name, mag. moment, mass, charge, displacement, number TB-orb.)\n"
|
116
|
+
)
|
117
|
+
|
118
|
+
myfile.write(
|
119
|
+
f"\natoms {len(cls.atoms)} positions of the atom in the unit cell\n"
|
120
|
+
)
|
121
|
+
for i, atom in enumerate(cls.atoms):
|
122
|
+
spos = cls.atoms.get_scaled_positions()[i]
|
123
|
+
myfile.write(f"{atom_types[tags[i]]} {spos[0]}, {spos[1]}, {spos[2]} \n")
|
124
|
+
|
125
|
+
|
126
|
+
def _write_magnetic_interactions(cls, myfile, symmetry=False):
|
127
|
+
myfile.write("\nThe Hamiltonian\n")
|
128
|
+
myfile.write("# Magnetic interactions\n")
|
129
|
+
|
130
|
+
|
131
|
+
def _write_isotropic_exchange(cls, myfile, symmetry=False):
|
132
|
+
myfile.write("\nmagnetic_J\n")
|
133
|
+
shell_dict = get_ind_shell(cls.distance_dict)
|
134
|
+
if symmetry:
|
135
|
+
Jdict = cls.reduced_exchange_Jdict
|
136
|
+
else:
|
137
|
+
Jdict = cls.exchange_Jdict
|
138
|
+
for key, val in Jdict.items():
|
139
|
+
R, i, j = key
|
140
|
+
ishell = shell_dict[(R, i, j)]
|
141
|
+
myfile.write(
|
142
|
+
f"{i+1} {j+1} {ishell} {val} # between atoms type {i+1} and {j+1}, shell {R}, amplitude in eV \n"
|
143
|
+
)
|
144
|
+
myfile.write(
|
145
|
+
"\nc_H_J -1 apply 1/2 in front of the sum of the exchange energy - default is -1\n"
|
146
|
+
)
|
147
|
+
|
148
|
+
|
149
|
+
def _write_magnetic_anisotropy(cls, myfile):
|
150
|
+
if cls.k1 is None:
|
151
|
+
return
|
152
|
+
else:
|
153
|
+
myfile.write("\nmagnetic_anisotropy \n")
|
154
|
+
for i, k1 in enumerate(cls.k1):
|
155
|
+
myfile.write(
|
156
|
+
f"{i+1} {cls.k1dir[i][0]} {cls.k1dir[i][1]} {cls.k1dir[i][2]} {k1} anisotropy of atoms type {i+1}, direction {cls.k1dir[i][0]} {cls.k1dir[i][1]} {cls.k1dir[i][2]} and amplitude in eV\n"
|
157
|
+
)
|
158
|
+
myfile.write("\nc_H_ani 1.0\n")
|
159
|
+
|
160
|
+
|
161
|
+
def _write_dmi(cls, myfile):
|
162
|
+
if cls.has_dmi:
|
163
|
+
myfile.write("\nmagnetic_D\n")
|
164
|
+
for key, val in cls.dmi_ddict.items():
|
165
|
+
R, i, j = key
|
166
|
+
myfile.write(
|
167
|
+
f"{i+1} {j+1} {R[0]} {R[1]} {R[2]} {val} #between atoms type {i+1} and {j+1}, mediated by atom type {R}, shell {R}, amplitude in eV\n"
|
168
|
+
)
|
169
|
+
myfile.write(
|
170
|
+
"\nc_H_D -1.0 # coefficients to put in from of the sum - default is -1\n"
|
171
|
+
)
|
172
|
+
|
173
|
+
|
174
|
+
def _write_exchange_tensor(cls, myfile, symmetry=False):
|
175
|
+
myfile.write(
|
176
|
+
"\nmagnetic_r2_tensor #Exchange tensor elements, middle 9 entries: xx, xy, xz, yx, etc. (in units of eV) and direction in which it should be applied\n"
|
177
|
+
)
|
178
|
+
|
179
|
+
Jtensor = cls.get_J_tensor_dict()
|
180
|
+
shelldict = get_ind_shell(cls.distance_dict)
|
181
|
+
unitcell = cls.atoms.get_cell().array.reshape((3, 3))
|
182
|
+
uc_lengths = np.linalg.norm(unitcell, axis=1)
|
183
|
+
if Jtensor is not None:
|
184
|
+
for key, val in Jtensor.items():
|
185
|
+
i, j, R = key
|
186
|
+
# distance vector
|
187
|
+
dvec = cls.distance_dict[(R, i, j)][0]/nm/uc_lengths
|
188
|
+
dscalar = np.linalg.norm(dvec)
|
189
|
+
val = np.real(val)
|
190
|
+
ishell = shelldict[(R, i, j)]
|
191
|
+
myfile.write(
|
192
|
+
f"{i+1} {j+1} {ishell} {' '.join([str(x) for x in val.flatten()])} {dvec[0]} {dvec[1]} {dvec[2]} \n"
|
193
|
+
#f"{i+1} {j+1} {dscalar} {' '.join([str(x) for x in val.flatten()])} {dvec[0]} {dvec[1]} {dvec[2]} \n"
|
194
|
+
)
|
195
|
+
myfile.write(
|
196
|
+
"\nc_H_Exchten -1 apply 1/2 in front of the sum of the exchange tensor energy - default is -1\n"
|
197
|
+
)
|
198
|
+
|
199
|
+
def rattle_atoms_and_cell(atoms, stdev=0.001, cell_stdev=0.001):
|
200
|
+
"""
|
201
|
+
Rattle both atomic positions and cell parameters.
|
202
|
+
|
203
|
+
Parameters:
|
204
|
+
-----------
|
205
|
+
atoms: ASE atoms object
|
206
|
+
The atoms to be rattled
|
207
|
+
stdev: float
|
208
|
+
Standard deviation for atomic displacement in Angstrom
|
209
|
+
cell_stdev: float
|
210
|
+
Standard deviation for cell parameter variation (fractional)
|
211
|
+
|
212
|
+
Returns:
|
213
|
+
--------
|
214
|
+
None
|
215
|
+
The atoms object is modified in-place
|
216
|
+
"""
|
217
|
+
# Rattle atomic positions
|
218
|
+
positions = atoms.get_positions()
|
219
|
+
displacement = np.random.normal(0, stdev, positions.shape)
|
220
|
+
atoms.set_positions(positions + displacement)
|
221
|
+
|
222
|
+
# Rattle cell parameters
|
223
|
+
cell = atoms.get_cell()
|
224
|
+
cell_noise = np.random.normal(0, cell_stdev, cell.shape)
|
225
|
+
atoms.set_cell(cell * (1 + cell_noise), scale_atoms=True)
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
"""
|
3
|
+
scan the spheres in the space of (theta, phi), so that the points are uniformly distributed on a sphere.
|
4
|
+
The algorithm is based on fibonacci spiral sampling method.
|
5
|
+
Reference:
|
6
|
+
https://extremelearning.com.au/how-to-evenly-distribute-points-on-a-sphere-more-effectively-than-the-canonical-fibonacci-lattice/
|
7
|
+
|
8
|
+
But note that the convention of theta and phi are different from the one in the reference.
|
9
|
+
Here, cos(theta) = z, and phi is the azimuthal angle in the x-y plane.
|
10
|
+
In the reference, theta is the azimuthal angle in the x-y plane, and phi is the polar angle.
|
11
|
+
"""
|
12
|
+
|
13
|
+
import numpy as np
|
14
|
+
from numpy import pi
|
15
|
+
|
16
|
+
|
17
|
+
def fibonacci_sphere(samples=100):
|
18
|
+
"""
|
19
|
+
Fibonacci Sphere Sampling Method
|
20
|
+
Parameters:
|
21
|
+
samples (int): number of points to sample on the sphere
|
22
|
+
Returns:
|
23
|
+
theta and phi: numpy arrays with shape (samples,) containing the angles of the sampled points.
|
24
|
+
"""
|
25
|
+
# Initialize the golden ratio and angles
|
26
|
+
goldenRatio = (1 + np.sqrt(5)) / 2
|
27
|
+
phi = np.arange(samples) * (2 * pi / goldenRatio)
|
28
|
+
theta = np.arccos(1 - 2 * np.arange(samples) / samples)
|
29
|
+
return theta, phi
|
30
|
+
|
31
|
+
|
32
|
+
def fibonacci_semisphere(samples=100):
|
33
|
+
"""
|
34
|
+
Fibonacci Sphere Sampling Method for the upper hemisphere of a sphere.
|
35
|
+
|
36
|
+
Parameters:
|
37
|
+
samples (int): number of points to sample on the sphere
|
38
|
+
|
39
|
+
Returns:
|
40
|
+
theta and phi: numpy arrays with shape (samples,) containing the angles of the sampled points.
|
41
|
+
"""
|
42
|
+
# Initialize the golden ratio and angles
|
43
|
+
goldenRatio = (1 + np.sqrt(5)) / 2
|
44
|
+
phi = np.arange(samples) * (2 * pi / goldenRatio)
|
45
|
+
theta = np.arccos(np.linspace(0, 1, samples))
|
46
|
+
return theta, phi
|
47
|
+
|
48
|
+
|
49
|
+
def test_fibonacci_sphere():
|
50
|
+
import matplotlib.pyplot as plt
|
51
|
+
|
52
|
+
# Generate points on the sphere
|
53
|
+
samples = 20000
|
54
|
+
theta, phi = fibonacci_sphere(samples)
|
55
|
+
# theta, phi = fibonacci_semisphere(samples)
|
56
|
+
|
57
|
+
# Convert spherical coordinates to Cartesian coordinates
|
58
|
+
x = np.cos(phi) * np.sin(theta)
|
59
|
+
y = np.sin(phi) * np.sin(theta)
|
60
|
+
z = np.cos(theta)
|
61
|
+
|
62
|
+
fig = plt.figure()
|
63
|
+
ax = fig.add_subplot(111, projection="3d")
|
64
|
+
ax.scatter(x, y, z, s=13.5)
|
65
|
+
# ax.plot(x, y, z)
|
66
|
+
|
67
|
+
# Set aspect to 'equal' for equal scaling in all directions
|
68
|
+
ax.set_aspect("equal")
|
69
|
+
|
70
|
+
plt.show()
|
71
|
+
|
72
|
+
|
73
|
+
if __name__ == "__main__":
|
74
|
+
test_fibonacci_sphere()
|
TB2J/orbital_magmom.py
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
"""
|
2
|
+
utilities to handel orbital magnetic moments
|
3
|
+
"""
|
4
|
+
|
5
|
+
import numpy as np
|
6
|
+
|
7
|
+
|
8
|
+
def complex_spherical_harmonic_to_real_spherical_harmonic(l=1):
|
9
|
+
"""
|
10
|
+
matrix to convert the complex spherical harmonics to real spherical harmonics
|
11
|
+
"""
|
12
|
+
MCR = np.zeros((2 * l + 1, 2 * l + 1), dtype=np.complex128)
|
13
|
+
MCR[0 + l, 0 + l] = 1.0
|
14
|
+
for m in range(1, l + 1):
|
15
|
+
mi = m + l
|
16
|
+
mpi = -m + l
|
17
|
+
MCR[mi, mi] = (-1) ** m / 2
|
18
|
+
MCR[mi, mpi] = 1.0 / 2
|
19
|
+
MCR[mpi, mi] = -1j * (-1) ** m / 2
|
20
|
+
MCR[mpi, mpi] = 1j / 2
|
21
|
+
|
22
|
+
return MCR
|
23
|
+
|
24
|
+
|
25
|
+
def test_complex_spherical_harmonic_to_real_spherical_harmonic():
|
26
|
+
"""
|
27
|
+
test the conversion matrix
|
28
|
+
"""
|
29
|
+
l = 3
|
30
|
+
MCR = complex_spherical_harmonic_to_real_spherical_harmonic(l)
|
31
|
+
# print(MCR*np.sqrt(2))
|
32
|
+
print(MCR * 2)
|
33
|
+
|
34
|
+
|
35
|
+
if __name__ == "__main__":
|
36
|
+
test_complex_spherical_harmonic_to_real_spherical_harmonic()
|
TB2J/symmetrize_J.py
CHANGED
@@ -9,7 +9,7 @@ from TB2J.versioninfo import print_license
|
|
9
9
|
|
10
10
|
|
11
11
|
class TB2JSymmetrizer:
|
12
|
-
def __init__(self, exc, symprec=1e-8, verbose=True, Jonly=
|
12
|
+
def __init__(self, exc, symprec=1e-8, verbose=True, Jonly=True):
|
13
13
|
# list of pairs with the index of atoms
|
14
14
|
ijRs = exc.ijR_list_index_atom()
|
15
15
|
finder = SymmetryPairFinder(atoms=exc.atoms, pairs=ijRs, symprec=symprec)
|
@@ -25,6 +25,12 @@ class TB2JSymmetrizer:
|
|
25
25
|
)
|
26
26
|
print("-" * 30)
|
27
27
|
if exc.has_dmi:
|
28
|
+
# raise NotImplementedError(
|
29
|
+
# "Symmetrization of DMI is not yet implemented."
|
30
|
+
# )
|
31
|
+
# raise Warning(
|
32
|
+
# "WARNING: Symmetrization of DMI is not yet implemented."
|
33
|
+
# )
|
28
34
|
print(
|
29
35
|
"WARNING: Currently only the isotropic exchange is symmetrized. Symmetrization of DMI and anisotropic exchange are not yet implemented."
|
30
36
|
)
|
@@ -33,7 +39,7 @@ class TB2JSymmetrizer:
|
|
33
39
|
print("Symmetry found:")
|
34
40
|
print(finder.spacegroup)
|
35
41
|
print("-" * 30)
|
36
|
-
self.
|
42
|
+
self.pldict = finder.get_symmetry_pair_list_dict()
|
37
43
|
self.exc = exc
|
38
44
|
self.new_exc = copy.deepcopy(exc)
|
39
45
|
self.Jonly = Jonly
|
@@ -46,21 +52,25 @@ class TB2JSymmetrizer:
|
|
46
52
|
Symmetrize the exchange parameters J.
|
47
53
|
"""
|
48
54
|
symJdict = {}
|
55
|
+
reduced_symJdict = {}
|
49
56
|
# Jdict = self.exc.exchange_Jdict
|
50
|
-
|
51
|
-
|
52
|
-
ijRs = pairgroup.get_all_ijR()
|
57
|
+
for ishell, pairlist in enumerate(self.pldict.pairlists):
|
58
|
+
ijRs = pairlist.get_all_ijR()
|
53
59
|
ijRs_spin = [self.exc.ijR_index_atom_to_spin(*ijR) for ijR in ijRs]
|
54
60
|
Js = [self.exc.get_J(*ijR_spin) for ijR_spin in ijRs_spin]
|
55
61
|
Javg = np.average(Js)
|
56
62
|
for i, j, R in ijRs_spin:
|
57
63
|
symJdict[(R, i, j)] = Javg
|
64
|
+
ijRs_ref = ijRs_spin[0]
|
65
|
+
i, j, R = ijRs_ref
|
66
|
+
reduced_symJdict[(R, i, j)] = Javg
|
58
67
|
self.new_exc.exchange_Jdict = symJdict
|
59
68
|
if self.Jonly:
|
60
69
|
self.new_exc.has_dmi = False
|
61
70
|
self.new_exc.dmi_ddict = None
|
62
71
|
self.new_exc.has_bilinear = False
|
63
72
|
self.new_exc.Jani_dict = None
|
73
|
+
self.new_exc.reduced_exchange_Jdict = reduced_symJdict
|
64
74
|
self.has_uniaxial_anisotropy = False
|
65
75
|
self.k1 = None
|
66
76
|
self.k1dir = None
|
@@ -124,12 +134,12 @@ def symmetrize_J_cli():
|
|
124
134
|
help="precision for symmetry detection. default is 1e-5 Angstrom",
|
125
135
|
)
|
126
136
|
|
127
|
-
parser.add_argument(
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
)
|
137
|
+
# parser.add_argument(
|
138
|
+
# "--Jonly",
|
139
|
+
# action="store_true",
|
140
|
+
# help="symmetrize only the exchange parameters and discard the DMI and anisotropic exchange",
|
141
|
+
# default=True,
|
142
|
+
# )
|
133
143
|
|
134
144
|
args = parser.parse_args()
|
135
145
|
if args.inpath is None:
|
@@ -139,7 +149,8 @@ def symmetrize_J_cli():
|
|
139
149
|
path=args.inpath,
|
140
150
|
output_path=args.outpath,
|
141
151
|
symprec=args.symprec,
|
142
|
-
Jonly=args.Jonly,
|
152
|
+
# Jonly=args.Jonly,
|
153
|
+
Jonly=True,
|
143
154
|
)
|
144
155
|
|
145
156
|
|
@@ -1,6 +1,6 @@
|
|
1
|
-
Metadata-Version: 2.
|
1
|
+
Metadata-Version: 2.4
|
2
2
|
Name: TB2J
|
3
|
-
Version: 0.9.
|
3
|
+
Version: 0.9.9rc7
|
4
4
|
Summary: TB2J: First principle to Heisenberg exchange J using tight-binding Green function method
|
5
5
|
Author: Xu He
|
6
6
|
Author-email: mailhexu@gmail.com
|
@@ -14,15 +14,24 @@ Classifier: Topic :: Scientific/Engineering :: Physics
|
|
14
14
|
Classifier: License :: OSI Approved :: BSD License
|
15
15
|
Requires-Python: >=3.6
|
16
16
|
License-File: LICENSE
|
17
|
-
Requires-Dist: numpy
|
17
|
+
Requires-Dist: numpy<2.0
|
18
18
|
Requires-Dist: scipy
|
19
19
|
Requires-Dist: matplotlib
|
20
|
-
Requires-Dist: ase
|
20
|
+
Requires-Dist: ase>=3.19
|
21
21
|
Requires-Dist: tqdm
|
22
22
|
Requires-Dist: pathos
|
23
|
-
Requires-Dist: packaging
|
24
|
-
Requires-Dist: HamiltonIO
|
23
|
+
Requires-Dist: packaging>=20.0
|
24
|
+
Requires-Dist: HamiltonIO>=0.1.9
|
25
25
|
Requires-Dist: pre-commit
|
26
|
-
Requires-Dist: sympair
|
26
|
+
Requires-Dist: sympair>0.1.0
|
27
|
+
Dynamic: author
|
28
|
+
Dynamic: author-email
|
29
|
+
Dynamic: classifier
|
30
|
+
Dynamic: description
|
31
|
+
Dynamic: license
|
32
|
+
Dynamic: license-file
|
33
|
+
Dynamic: requires-dist
|
34
|
+
Dynamic: requires-python
|
35
|
+
Dynamic: summary
|
27
36
|
|
28
37
|
TB2J is a Python package aimed to compute automatically the magnetic interactions (superexchange and Dzyaloshinskii-Moriya) between atoms of magnetic crystals from DFT Hamiltonian based on Wannier functions or Linear combination of atomic orbitals. It uses the Green's function method and take the local rigid spin rotation as a perturbation. The package can take the output from Wannier90, which is interfaced with many density functional theory codes or from codes based on localised orbitals. A minimal user input is needed, which allows for an easily integration into a high-throughput workflows.
|
@@ -1,17 +1,17 @@
|
|
1
|
-
TB2J/Jdownfolder.py,sha256=
|
1
|
+
TB2J/Jdownfolder.py,sha256=xs0gmcKwOA3ejXqk3JPPgnYVu8CvkK8cteuvHuu1oSk,9603
|
2
2
|
TB2J/Jtensor.py,sha256=t6OsqrSlYW6Im4H7ykVAW8Al_pFXN4C5yj2UEsV6r7g,3181
|
3
3
|
TB2J/MAE.py,sha256=fM8U-Dgp9HcQOEeC_kyZV1oVrygBvcux9BraUXVouvY,10994
|
4
|
-
TB2J/MAEGreen.py,sha256=
|
4
|
+
TB2J/MAEGreen.py,sha256=pCX12GDNaOz8XgMlui8NjAERM43Ux3HyOKfiD80czXI,15306
|
5
5
|
TB2J/Oiju.py,sha256=cNGv8N5uH_swGq7cnAt2OyiDfqtjLlLrwseGu0E4iaM,3383
|
6
6
|
TB2J/Oiju_epc.py,sha256=oytM3NYW7nWmklrGgNlqwIpI_JYv_hb7ZnR4o9nYNog,6809
|
7
7
|
TB2J/__init__.py,sha256=hcEWkag_UvLm1ZSbjsgcTWkGVlR3Bwmzg1QYAwsvf-g,24
|
8
|
-
TB2J/anisotropy.py,sha256=
|
8
|
+
TB2J/anisotropy.py,sha256=0zmvXkmDmakbBOwGYLa3IIkv5cE99SHLAQJsGoZz7JQ,25463
|
9
9
|
TB2J/basis.py,sha256=DFo6_QUwjBwisP6zGxvoO0lpGTMDPAOkiL9giNCjOjA,1558
|
10
10
|
TB2J/citation.py,sha256=gcQeyJZaT1Qrtsl8Y3s4neOH3-vvgmIcCvXeV2o3vj0,2891
|
11
11
|
TB2J/contour.py,sha256=zLHQZZ3hhgLkLFPATCraLOJyLJKLC0fba_L_5sRz23o,3246
|
12
12
|
TB2J/density_matrix.py,sha256=D5k8Oe21OCiLVORNYbo4TZOFG0slrQSbj91kJ3TMFjs,1514
|
13
13
|
TB2J/epc.py,sha256=zLbtqZJhDr8DnnGN6YENcXwrMb3Qxu6KB08mLy9Pw20,3474
|
14
|
-
TB2J/exchange.py,sha256=
|
14
|
+
TB2J/exchange.py,sha256=742vEE8DQJuWBTlq45RQoPy0-_n_TT6BZOb_DoTpDKI,25739
|
15
15
|
TB2J/exchangeCL2.py,sha256=P7bklMXVYX_tn9DbjEPqeTir1SeZyfPBIP1fhWUzLmY,11419
|
16
16
|
TB2J/exchange_params.py,sha256=AcGYYky27DXSF3yDZWVjksr_3rt6im6qeIzpOwvqssk,7141
|
17
17
|
TB2J/exchange_pert.py,sha256=jmFMtQbYa_uczM4VAeS6TijkIHRFIqEzZJswzE9Wfuo,8523
|
@@ -23,6 +23,7 @@ TB2J/io_merge.py,sha256=E1_GfAB2HGpW-ipaO2lqU9SvaslwkiLxssn4DqJpMT8,6899
|
|
23
23
|
TB2J/kpoints.py,sha256=9L7tBarFBHoIhpuc9zuwA6HdnlgH834SQrPek4yRoWk,3191
|
24
24
|
TB2J/myTB.py,sha256=ok_B4my29bOIghMSZfx0Es6G8FaXaIiLP4gPxTdSj00,17659
|
25
25
|
TB2J/mycfr.py,sha256=Wgj6PpA-oVuxm8Hh32FVw_vthozVBrDJhRV1hA1ku44,3752
|
26
|
+
TB2J/orbital_magmom.py,sha256=JTwO9ZDgRRQndqR9aFIua4eTvwLMoGsTiY_HaIPMZ2I,889
|
26
27
|
TB2J/orbmap.py,sha256=XLQjKMxCy2eADaM5eb2F_zG08V7lzpXJxp5uEtTeVYI,7194
|
27
28
|
TB2J/pauli.py,sha256=ESpAhk6LG5ugzuW1YFUTqiDxcg-pQ7wNnzR2FtUnvKM,5295
|
28
29
|
TB2J/pert.py,sha256=RaCJfewl0doht4cjAnzzGKe-uj2le4aqe0iPKFrq9fo,1192
|
@@ -30,7 +31,7 @@ TB2J/plot.py,sha256=AnFIFWE2vlmj7Z6f_7-dX_O1stJN-qbuiurPj43dUCM,4104
|
|
30
31
|
TB2J/rotate_atoms.py,sha256=Dwptn-wdDW4zYzjYb95yxTzuZOe9WPuLjh3d3-YcSs0,3277
|
31
32
|
TB2J/rotate_siestaDM.py,sha256=eR97rspdrRaK9YTwQwUKfobI0S9UnEcbEZ2f5IgR7Tk,1070
|
32
33
|
TB2J/sisl_wrapper.py,sha256=A5x1-tt8efUSPeGY5wM5m6-pJYQFXTCzQHVqD6RBa2g,14792
|
33
|
-
TB2J/symmetrize_J.py,sha256=
|
34
|
+
TB2J/symmetrize_J.py,sha256=gN6Y8zV4IrO5rPh0SG8KHkekrJjMQzNY4GGe2ZBtwbc,4993
|
34
35
|
TB2J/tensor_rotate.py,sha256=4-DfT_Mg5e40fbd74M5W0D5DqmUq-kVOOLDkkkI834A,8083
|
35
36
|
TB2J/utest.py,sha256=z_ahi7tpHQF9WlHNQihcQ7qzfezRJQXQt28eB1X_z64,3897
|
36
37
|
TB2J/utils.py,sha256=DHkc7BK0KUGesfoAv1OxMgIw_iZzcFXh--3ybsFSd_c,12535
|
@@ -41,7 +42,7 @@ TB2J/interfaces/__init__.py,sha256=4tiLoXQ73Nlyi9L4j8jJXOYzXluVNPxQZkwfkQZEGHg,3
|
|
41
42
|
TB2J/interfaces/gpaw_interface.py,sha256=GCDlJ-hRWfChvWwsgBDYSmVqO4sH9HAuGZTV9GqgN6c,1504
|
42
43
|
TB2J/interfaces/lawaf_interface.py,sha256=PieLnmppdafOYsgeHznqOou1g9L1sam5jOm3KaObdqo,4408
|
43
44
|
TB2J/interfaces/manager.py,sha256=PQMLEfMCT5GnDWSl2nI4JOgRPm_fysyR-6Y6l97xWcw,860
|
44
|
-
TB2J/interfaces/siesta_interface.py,sha256=
|
45
|
+
TB2J/interfaces/siesta_interface.py,sha256=olvo2xdBOSNk3zn67nuKrxbW--EKPoWwEzKRBwJbrVY,7366
|
45
46
|
TB2J/interfaces/wannier90_interface.py,sha256=qzRgXUBb7t1Aiegrl_RV51BB8csdtVM0EP0Z4pjmTcs,4467
|
46
47
|
TB2J/interfaces/abacus/__init__.py,sha256=leas71oCvM_HxrF4gnO5A_VKcJmDAgsI1BUctLU3OBw,177
|
47
48
|
TB2J/interfaces/abacus/abacus_api.py,sha256=lNV4LNkLcKw7Zux4MQYM9wnh3eFTlcSqbf4Pb7pqhrk,7243
|
@@ -53,7 +54,8 @@ TB2J/interfaces/abacus/test_density_matrix.py,sha256=bMWWJYtDS57SpPZ-eZXZ9Hr_UK4
|
|
53
54
|
TB2J/interfaces/abacus/test_read_HRSR.py,sha256=W1oO_yigT50Yb5_u-KB_IfTpM7kArGkBuMSMs0H4CTs,1235
|
54
55
|
TB2J/interfaces/abacus/test_read_stru.py,sha256=hoKPHVco8vwzC7Gao4bOPCdAPhh29x-9DTJJqRr7AYM,788
|
55
56
|
TB2J/io_exchange/__init__.py,sha256=KfGHum7B8E4G_KKfillqw0lErtoyKEuFUUttHLs-mg4,32
|
56
|
-
TB2J/io_exchange/io_exchange.py,sha256=
|
57
|
+
TB2J/io_exchange/io_exchange.py,sha256=7-Xt7_K5FKVrFkFvBrHhJGCRrL2Sw7zY-93GRBBv2GM,21050
|
58
|
+
TB2J/io_exchange/io_matjes.py,sha256=klM5Z_t2kk0y_1IoqIILLnhUgjHQmXXmsXiono2LeMU,8594
|
57
59
|
TB2J/io_exchange/io_multibinit.py,sha256=8PDmWxzGuv-GwJosj2ZTmiyNY_duFVWJ4ekCuSqGdd8,6739
|
58
60
|
TB2J/io_exchange/io_tomsasd.py,sha256=NqkAC1Fl-CUnFA21eBzSy_S5F_oeQFJysw4UukQbN8o,4173
|
59
61
|
TB2J/io_exchange/io_txt.py,sha256=BMr1eSILlKpgtjvDx7uw2VMAkEKSvGEPNxpaT_zev0I,10547
|
@@ -61,6 +63,7 @@ TB2J/io_exchange/io_uppasd.py,sha256=bI4iPEgnK4TvCZNvb6x2xYXgjW7pEehCqmcizy2pqFU
|
|
61
63
|
TB2J/io_exchange/io_vampire.py,sha256=UllC4twf06_q2vBCnAYFzEDGvS8mSefwQXDquBuyc0M,5583
|
62
64
|
TB2J/mathutils/__init__.py,sha256=tQLBfHkZqdVfVxPOahy42qMUkFYnFFFhM-uc4QsYFxI,27
|
63
65
|
TB2J/mathutils/fermi.py,sha256=72tZ5CptGmYaBUD0xLWltuH7LBXcrMUwODyW6-WqlzI,638
|
66
|
+
TB2J/mathutils/fibonacci_sphere.py,sha256=l0USn25ZCAWF6l4UleyWaeLthsj9TThV9iWmfi6DbaM,2344
|
64
67
|
TB2J/mathutils/kR_convert.py,sha256=p_9XWJVNanTzTK2rI6KRjTkbSq42la6N448-zJOsMwY,2671
|
65
68
|
TB2J/mathutils/lowdin.py,sha256=RYbm9OcnFnjcZFdC5YcNUsI9cOJmoDLsWSSCaP0GqKQ,499
|
66
69
|
TB2J/mathutils/rotate_spin.py,sha256=lbGzve_36FyNjanXqdxYDb102kA4_5OycRlBcm-tH-g,8360
|
@@ -77,19 +80,19 @@ TB2J/spinham/supercell.py,sha256=y17uUC6r3gQb278FhxIW4CABihfLTvKFj6flyXrCPR8,122
|
|
77
80
|
TB2J/wannier/__init__.py,sha256=7ojCbM84PYv1X1Tbo4NHI-d3gWmQsZB_xiYqbfxVV1E,80
|
78
81
|
TB2J/wannier/w90_parser.py,sha256=dbd63LuKyv2DVUzqRINGsbDzEsOxsQyE8_Ear_LQIRg,4620
|
79
82
|
TB2J/wannier/w90_tb_parser.py,sha256=qt8pnuprmPp9iIAYwPkPbmEzk6ZPgMq2xognoQp7vwc,4610
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
83
|
+
tb2j-0.9.9rc7.data/scripts/TB2J_downfold.py,sha256=i4BVqnpDdgrX_amookVWeLGefGBn-qeAutWiwuY9SfQ,2099
|
84
|
+
tb2j-0.9.9rc7.data/scripts/TB2J_eigen.py,sha256=Qs9v2hnMm2Tpfoa4h53muUKty2dZjwx8948MBoQooNg,1128
|
85
|
+
tb2j-0.9.9rc7.data/scripts/TB2J_magnon.py,sha256=q7UwAmorRcFNk4tfE7gl_ny05l6p7pbD9Wm_LkIpKEw,3101
|
86
|
+
tb2j-0.9.9rc7.data/scripts/TB2J_magnon_dos.py,sha256=TMXQvD2dIbO5FZ4tUMmxJgCgH2O2hDAPUNfEKO4z-x4,110
|
87
|
+
tb2j-0.9.9rc7.data/scripts/TB2J_merge.py,sha256=y834SF4rIRn1L1ptkhczvavQpC-8Px6DTmDOOSaq_DE,1854
|
88
|
+
tb2j-0.9.9rc7.data/scripts/TB2J_rotate.py,sha256=zgiDFuYZNmzKK0rwDmTaYD2OpRlmKA_VGeBx83w2Xwc,873
|
89
|
+
tb2j-0.9.9rc7.data/scripts/TB2J_rotateDM.py,sha256=kCvF7gotuqAX1VnJ06cwfVm7RrhrdtiV5v7d9P2Pn_E,567
|
90
|
+
tb2j-0.9.9rc7.data/scripts/abacus2J.py,sha256=0HLXoJhAkiZ-ZM1cs26lncccxE8-TzC8JiDTba1h1uM,4163
|
91
|
+
tb2j-0.9.9rc7.data/scripts/siesta2J.py,sha256=gp31LioqpPkDmMY0y_5gXIjOBPNnf080P37pRo0yjw8,4886
|
92
|
+
tb2j-0.9.9rc7.data/scripts/wann2J.py,sha256=pTFDf6h72I_LN_NX5UivyCoJPgwvyAyHW175nSAJvLo,2987
|
93
|
+
tb2j-0.9.9rc7.dist-info/licenses/LICENSE,sha256=CbZI-jyRTjiqIcWa244cRSHJdjjtUNqGR4HeJkgEwJw,1332
|
94
|
+
tb2j-0.9.9rc7.dist-info/METADATA,sha256=H5g8ApE237zxKx8_mx0Fr1fUCz2m97VkgDY5LBFYzlg,1660
|
95
|
+
tb2j-0.9.9rc7.dist-info/WHEEL,sha256=1tXe9gY0PYatrMPMDd6jXqjfpz_B-Wqm32CPfRC58XU,91
|
96
|
+
tb2j-0.9.9rc7.dist-info/entry_points.txt,sha256=Hdz1WC9waUzyFVmowKnbbZ6j-J4adHh_Ko6JpxGYAtE,131
|
97
|
+
tb2j-0.9.9rc7.dist-info/top_level.txt,sha256=whYa5ByLYhl5XnTPBHSWr-IGD6VWmr5Ql2bye2qwV_s,5
|
98
|
+
tb2j-0.9.9rc7.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|