TB2J 0.9.9.13__py3-none-any.whl → 0.9.9.15__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/MAEGreen.py +1 -2
- TB2J/__init__.py +1 -1
- TB2J/exchange.py +1 -1
- TB2J/interfaces/abacus/stru_api.py +10 -17
- TB2J/interfaces/siesta_interface.py +1 -1
- TB2J/io_exchange/io_exchange.py +4 -5
- TB2J/io_exchange/io_vampire.py +1 -1
- TB2J/io_merge.py +60 -45
- TB2J/magnon/magnon3.py +105 -11
- TB2J/magnon/magnon_band.py +3 -3
- TB2J/magnon/magnon_dos.py +308 -0
- TB2J/magnon/magnon_math.py +1 -1
- TB2J/magnon/plot_magnon_dos_cli.py +99 -0
- {tb2j-0.9.9.13.data → tb2j-0.9.9.15.data}/scripts/TB2J_merge.py +9 -5
- tb2j-0.9.9.15.data/scripts/TB2J_plot_magnon_bands.py +27 -0
- {tb2j-0.9.9.13.dist-info → tb2j-0.9.9.15.dist-info}/METADATA +1 -1
- {tb2j-0.9.9.13.dist-info → tb2j-0.9.9.15.dist-info}/RECORD +31 -29
- {tb2j-0.9.9.13.dist-info → tb2j-0.9.9.15.dist-info}/entry_points.txt +1 -0
- tb2j-0.9.9.13.data/scripts/TB2J_plot_magnon_bands.py +0 -7
- {tb2j-0.9.9.13.data → tb2j-0.9.9.15.data}/scripts/TB2J_downfold.py +0 -0
- {tb2j-0.9.9.13.data → tb2j-0.9.9.15.data}/scripts/TB2J_eigen.py +0 -0
- {tb2j-0.9.9.13.data → tb2j-0.9.9.15.data}/scripts/TB2J_magnon.py +0 -0
- {tb2j-0.9.9.13.data → tb2j-0.9.9.15.data}/scripts/TB2J_magnon2.py +0 -0
- {tb2j-0.9.9.13.data → tb2j-0.9.9.15.data}/scripts/TB2J_magnon_dos.py +0 -0
- {tb2j-0.9.9.13.data → tb2j-0.9.9.15.data}/scripts/TB2J_rotate.py +0 -0
- {tb2j-0.9.9.13.data → tb2j-0.9.9.15.data}/scripts/TB2J_rotateDM.py +0 -0
- {tb2j-0.9.9.13.data → tb2j-0.9.9.15.data}/scripts/abacus2J.py +0 -0
- {tb2j-0.9.9.13.data → tb2j-0.9.9.15.data}/scripts/siesta2J.py +0 -0
- {tb2j-0.9.9.13.data → tb2j-0.9.9.15.data}/scripts/wann2J.py +0 -0
- {tb2j-0.9.9.13.dist-info → tb2j-0.9.9.15.dist-info}/WHEEL +0 -0
- {tb2j-0.9.9.13.dist-info → tb2j-0.9.9.15.dist-info}/licenses/LICENSE +0 -0
- {tb2j-0.9.9.13.dist-info → tb2j-0.9.9.15.dist-info}/top_level.txt +0 -0
TB2J/MAEGreen.py
CHANGED
@@ -94,12 +94,11 @@ class MAEGreen(ExchangeNCL):
|
|
94
94
|
self.thetas.append(i * np.pi / 180)
|
95
95
|
self.phis.append(j * np.pi / 180)
|
96
96
|
|
97
|
-
def set_angels_ztox(self, n=16):
|
97
|
+
def set_angels_ztox(self, n=16):
|
98
98
|
"""Set angles for a scan from z to x"""
|
99
99
|
self.thetas = np.linspace(0, np.pi, n)
|
100
100
|
self.phis = np.zeros(n)
|
101
101
|
|
102
|
-
|
103
102
|
def set_angles_random(self, n=16):
|
104
103
|
# n random pairs of theta, phi
|
105
104
|
self.thetas = np.random.random(n) * np.pi
|
TB2J/__init__.py
CHANGED
@@ -1 +1 @@
|
|
1
|
-
__version__ = "0.9.9.
|
1
|
+
__version__ = "0.9.9.14"
|
TB2J/exchange.py
CHANGED
@@ -48,7 +48,7 @@ class Exchange(ExchangeParams):
|
|
48
48
|
self.emin = self.G.find_energy_ingap(rbound=self.efermi - 15.0) - self.efermi
|
49
49
|
# self.emin = self.G.find_energy_ingap(rbound=self.efermi - 15.0) - self.efermi
|
50
50
|
# self.emin = -42.0
|
51
|
-
#print(f"A gap is found at {self.emin}, set emin to it.")
|
51
|
+
# print(f"A gap is found at {self.emin}, set emin to it.")
|
52
52
|
|
53
53
|
def set_tbmodels(self, tbmodels):
|
54
54
|
pass
|
@@ -7,21 +7,28 @@
|
|
7
7
|
Modified on Wed Aug 01 11:44:51 2022
|
8
8
|
@author: Ji Yu-yang
|
9
9
|
"""
|
10
|
-
import warnings
|
11
|
-
warnings.simplefilter(action='ignore', category=FutureWarning)
|
12
|
-
|
13
10
|
|
14
11
|
import os
|
15
12
|
import re
|
16
13
|
import shutil
|
14
|
+
import warnings
|
15
|
+
from copy import deepcopy
|
16
|
+
from math import sqrt
|
17
17
|
from pathlib import Path
|
18
18
|
|
19
19
|
import numpy as np
|
20
20
|
from ase import Atoms
|
21
|
+
from ase.calculators.calculator import kpts2ndarray, kpts2sizeandoffsets
|
21
22
|
from ase.calculators.singlepoint import SinglePointDFTCalculator, arrays_to_kpoints
|
23
|
+
from ase.constraints import FixAtoms, FixCartesian
|
24
|
+
from ase.dft.kpoints import bandpath
|
25
|
+
from ase.stress import full_3x3_to_voigt_6_stress
|
22
26
|
from ase.units import Bohr, GPa, Hartree, Rydberg, _me, mol
|
23
27
|
from ase.utils import lazymethod, lazyproperty, reader, writer
|
24
28
|
|
29
|
+
warnings.simplefilter(action="ignore", category=FutureWarning)
|
30
|
+
|
31
|
+
|
25
32
|
_re_float = r"[-+]?\d+\.*\d*(?:[Ee][-+]\d+)?"
|
26
33
|
AU_to_MASS = mol * _me * 1e3
|
27
34
|
UNIT_V = np.sqrt(Hartree / AU_to_MASS)
|
@@ -42,7 +49,6 @@ def write_input(fd, parameters=None):
|
|
42
49
|
parameters: dict
|
43
50
|
The dictionary of all paramters for the calculation.
|
44
51
|
"""
|
45
|
-
from copy import deepcopy
|
46
52
|
|
47
53
|
params = deepcopy(parameters)
|
48
54
|
params["dft_functional"] = (
|
@@ -105,8 +111,6 @@ def write_kpt(fd=None, parameters=None, atoms=None):
|
|
105
111
|
return
|
106
112
|
elif kpts is not None:
|
107
113
|
if isinstance(kpts, dict) and "path" not in kpts:
|
108
|
-
from ase.calculators.calculator import kpts2sizeandoffsets
|
109
|
-
|
110
114
|
kgrid, shift = kpts2sizeandoffsets(atoms=atoms, **kpts)
|
111
115
|
koffset = []
|
112
116
|
for i, x in enumerate(shift):
|
@@ -121,8 +125,6 @@ def write_kpt(fd=None, parameters=None, atoms=None):
|
|
121
125
|
koffset = [koffset] * 3
|
122
126
|
|
123
127
|
if isinstance(kgrid, dict) or hasattr(kgrid, "kpts"):
|
124
|
-
from ase.calculators.calculator import kpts2ndarray
|
125
|
-
|
126
128
|
kmode = "Direct"
|
127
129
|
kgrid = kpts2ndarray(kgrid, atoms=atoms)
|
128
130
|
elif isinstance(kgrid, str) and (kgrid == "gamma"):
|
@@ -272,8 +274,6 @@ def judge_exist_stru(stru=None):
|
|
272
274
|
|
273
275
|
|
274
276
|
def read_ase_stru(stru=None, coordinates_type="Cartesian"):
|
275
|
-
from ase.constraints import FixAtoms, FixCartesian
|
276
|
-
|
277
277
|
fix_cart = np.ones((len(stru), 3), dtype=int).tolist()
|
278
278
|
for constr in stru.constraints:
|
279
279
|
for i in constr.index:
|
@@ -567,8 +567,6 @@ def read_kpt(fd, cell=None):
|
|
567
567
|
else:
|
568
568
|
knumbers = klines[:, 3].astype(int)
|
569
569
|
if cell is not None:
|
570
|
-
from ase.dft.kpoints import bandpath
|
571
|
-
|
572
570
|
return bandpath(kpts, cell, npoints=knumbers.sum())
|
573
571
|
else:
|
574
572
|
return {
|
@@ -683,8 +681,6 @@ def read_abacus(fd, latname=None, verbose=False):
|
|
683
681
|
If `verbose` is True, pseudo-potential, basis and other information along with the Atoms object will be output as a dict.
|
684
682
|
"""
|
685
683
|
|
686
|
-
from ase.constraints import FixCartesian
|
687
|
-
|
688
684
|
contents = fd.read()
|
689
685
|
title_str = r"(?:LATTICE_CONSTANT|NUMERICAL_DESCRIPTOR|NUMERICAL_ORBITAL|ABFS_ORBITAL|LATTICE_VECTORS|LATTICE_PARAMETERS|ATOMIC_POSITIONS)"
|
690
686
|
|
@@ -887,8 +883,6 @@ def read_abacus(fd, latname=None, verbose=False):
|
|
887
883
|
|
888
884
|
|
889
885
|
def get_lattice_from_latname(lines, latname=None):
|
890
|
-
from math import sqrt
|
891
|
-
|
892
886
|
if lines:
|
893
887
|
lines = lines.group(1).split(" ")
|
894
888
|
|
@@ -1488,7 +1482,6 @@ class AbacusOutCalcChunk(AbacusOutChunk):
|
|
1488
1482
|
@lazymethod
|
1489
1483
|
def get_stress(self):
|
1490
1484
|
"""Get the stress from the output file according to index"""
|
1491
|
-
from ase.stress import full_3x3_to_voigt_6_stress
|
1492
1485
|
|
1493
1486
|
try:
|
1494
1487
|
stress = (
|
@@ -165,7 +165,7 @@ Warning: The DMI component parallel to the spin orientation, the Jani which has
|
|
165
165
|
# thetas = [0, np.pi / 2, np.pi, 3 * np.pi / 2]
|
166
166
|
# phis = [0, 0, 0, 0]
|
167
167
|
# MAE.set_angles(thetas=thetas, phis=phis)
|
168
|
-
#MAE.set_xyz_angles()
|
168
|
+
# MAE.set_xyz_angles()
|
169
169
|
MAE.run(output_path=f"{output_path}_anisotropy", with_eigen=False)
|
170
170
|
# print(
|
171
171
|
# f"MAE calculation finished. The results are in {output_path} directory."
|
TB2J/io_exchange/io_exchange.py
CHANGED
@@ -340,20 +340,19 @@ Generation time: {now.strftime("%y/%m/%d %H:%M:%S")}
|
|
340
340
|
"""
|
341
341
|
i = self.i_spin(i)
|
342
342
|
j = self.i_spin(j)
|
343
|
-
J=D=Ja = None
|
343
|
+
J = D = Ja = None
|
344
344
|
if Jiso:
|
345
|
-
J= self.get_Jiso(i, j, R)
|
345
|
+
J = self.get_Jiso(i, j, R)
|
346
346
|
if DMI:
|
347
347
|
D = self.get_DMI(i, j, R)
|
348
348
|
if D is not None:
|
349
|
-
D*=1
|
349
|
+
D *= 1
|
350
350
|
if Jani:
|
351
351
|
Ja = self.get_Jani(i, j, R)
|
352
352
|
if Ja is not None:
|
353
|
-
Ja*=1
|
353
|
+
Ja *= 1
|
354
354
|
Jtensor = combine_J_tensor(Jiso=J, D=D, Jani=Ja)
|
355
355
|
|
356
|
-
|
357
356
|
# if iso_only:
|
358
357
|
# J = self.get_Jiso(i, j, R)
|
359
358
|
# if J is not None:
|
TB2J/io_exchange/io_vampire.py
CHANGED
TB2J/io_merge.py
CHANGED
@@ -1,31 +1,35 @@
|
|
1
|
-
import os
|
2
1
|
import copy
|
2
|
+
import os
|
3
3
|
import warnings
|
4
|
+
from itertools import combinations_with_replacement
|
5
|
+
|
4
6
|
import numpy as np
|
5
|
-
|
7
|
+
|
6
8
|
from TB2J.io_exchange import SpinIO
|
7
9
|
|
8
10
|
u0 = np.zeros(3)
|
9
11
|
uy = np.array([0.0, 1.0, 0.0])
|
10
12
|
uz = np.array([0.0, 0.0, 1.0])
|
11
13
|
|
12
|
-
def get_Jani_coefficients(a, R=np.eye(3)):
|
13
14
|
|
15
|
+
def get_Jani_coefficients(a, R=np.eye(3)):
|
14
16
|
if len(a) == 1:
|
15
17
|
u = a
|
16
18
|
v = a
|
17
19
|
else:
|
18
20
|
u = a[[0, 0, 1]]
|
19
21
|
v = a[[0, 1, 1]]
|
20
|
-
|
22
|
+
|
21
23
|
ur = u @ R.T
|
22
24
|
vr = v @ R.T
|
23
|
-
coefficients = np.hstack(
|
25
|
+
coefficients = np.hstack(
|
26
|
+
[ur * vr, np.roll(ur, -1, axis=-1) * vr + np.roll(vr, -1, axis=-1) * ur]
|
27
|
+
)
|
24
28
|
|
25
29
|
return coefficients, u, v
|
26
30
|
|
27
|
-
def get_projections(a, b, tol=1e-2):
|
28
31
|
|
32
|
+
def get_projections(a, b, tol=1e-2):
|
29
33
|
projections = np.empty((2, 3))
|
30
34
|
if np.linalg.matrix_rank([a, b], tol=tol) == 1:
|
31
35
|
if np.linalg.matrix_rank([a, uy], tol=tol) == 1:
|
@@ -41,13 +45,13 @@ def get_projections(a, b, tol=1e-2):
|
|
41
45
|
|
42
46
|
return projections
|
43
47
|
|
48
|
+
|
44
49
|
class SpinIO_merge(SpinIO):
|
45
50
|
def __init__(self, *args, **kwargs):
|
46
51
|
super(SpinIO_merge, self).__init__(*args, **kwargs)
|
47
52
|
self.projv = None
|
48
53
|
|
49
54
|
def _set_projection_vectors(self):
|
50
|
-
|
51
55
|
norm = np.linalg.norm(self.spinat, axis=-1).reshape(-1, 1)
|
52
56
|
spinat = self.spinat / norm
|
53
57
|
idx = [self.ind_atoms[i] for i in self.index_spin if i >= 0]
|
@@ -60,27 +64,29 @@ class SpinIO_merge(SpinIO):
|
|
60
64
|
self.projv = projv
|
61
65
|
|
62
66
|
@classmethod
|
63
|
-
def load_pickle(cls, path=
|
67
|
+
def load_pickle(cls, path="TB2J_results", fname="TB2J.pickle"):
|
64
68
|
obj = super(SpinIO_merge, cls).load_pickle(path=path, fname=fname)
|
65
69
|
obj._set_projection_vectors()
|
66
70
|
|
67
71
|
return obj
|
68
72
|
|
73
|
+
|
69
74
|
def read_pickle(path):
|
70
|
-
p1 = os.path.join(path,
|
71
|
-
p2 = os.path.join(path,
|
75
|
+
p1 = os.path.join(path, "TB2J_results", "TB2J.pickle")
|
76
|
+
p2 = os.path.join(path, "TB2J.pickle")
|
72
77
|
if os.path.exists(p1) and os.path.exists(p2):
|
73
78
|
print(f" WARNING!: Both file {p1} and {p2} exist. Use default {p1}.")
|
74
79
|
if os.path.exists(p1):
|
75
|
-
ret = SpinIO_merge.load_pickle(os.path.join(path,
|
80
|
+
ret = SpinIO_merge.load_pickle(os.path.join(path, "TB2J_results"))
|
76
81
|
elif os.path.exists(p2):
|
77
82
|
ret = SpinIO_merge.load_pickle(path)
|
78
83
|
else:
|
79
84
|
raise FileNotFoundError(f"Cannot find either file {p1} or {p2}")
|
80
85
|
return ret
|
81
86
|
|
82
|
-
|
83
|
-
|
87
|
+
|
88
|
+
class Merger:
|
89
|
+
def __init__(self, *paths, main_path=None):
|
84
90
|
self.dat = [read_pickle(path) for path in paths]
|
85
91
|
|
86
92
|
if main_path is None:
|
@@ -90,32 +96,34 @@ class Merger():
|
|
90
96
|
self.dat.append(copy.deepcopy(self.main_dat))
|
91
97
|
|
92
98
|
self._set_projv()
|
93
|
-
|
99
|
+
|
94
100
|
def _set_projv(self):
|
95
101
|
cell = self.main_dat.atoms.cell.array
|
96
|
-
rotated_cells = np.stack(
|
97
|
-
[obj.atoms.cell.array for obj in self.dat], axis=0
|
98
|
-
)
|
102
|
+
rotated_cells = np.stack([obj.atoms.cell.array for obj in self.dat], axis=0)
|
99
103
|
R = np.linalg.solve(cell, rotated_cells)
|
100
104
|
indices = range(len(self.dat))
|
101
105
|
|
102
|
-
proju = {}
|
106
|
+
proju = {}
|
107
|
+
projv = {}
|
108
|
+
coeff_matrix = {}
|
109
|
+
projectors = {}
|
103
110
|
for key in self.main_dat.projv.keys():
|
104
111
|
vectors = [obj.projv[key] for obj in self.dat]
|
105
|
-
coefficients, u, v = zip(
|
112
|
+
coefficients, u, v = zip(
|
113
|
+
*[get_Jani_coefficients(vectors[i], R=R[i]) for i in indices]
|
114
|
+
)
|
106
115
|
projectors[key] = np.vstack([u[i] @ R[i].T for i in indices])
|
107
116
|
coeff_matrix[key] = np.vstack(coefficients)
|
108
117
|
proju[key] = np.stack(u)
|
109
118
|
projv[key] = np.stack(v)
|
110
119
|
if np.linalg.matrix_rank(coeff_matrix[key], tol=1e-2) < 6:
|
111
|
-
warnings.warn(
|
120
|
+
warnings.warn("""
|
112
121
|
WARNING: The matrix of equations to reconstruct the exchange tensors is
|
113
122
|
close to being singular. This happens when the magnetic moments between
|
114
123
|
different configurations are cloes to being parallel. You need to consider
|
115
124
|
more rotated spin configurations, otherwise the results might have a large
|
116
|
-
error.
|
117
|
-
|
118
|
-
|
125
|
+
error.""")
|
126
|
+
|
119
127
|
self.proju = proju
|
120
128
|
self.projv = projv
|
121
129
|
self.coeff_matrix = coeff_matrix
|
@@ -123,71 +131,78 @@ class Merger():
|
|
123
131
|
|
124
132
|
def merge_Jani(self):
|
125
133
|
Jani_dict = {}
|
126
|
-
proju = self.proju
|
134
|
+
proju = self.proju
|
135
|
+
projv = self.projv
|
136
|
+
coeff_matrix = self.coeff_matrix
|
127
137
|
for key in self.main_dat.Jani_dict.keys():
|
128
138
|
try:
|
129
139
|
R, i, j = key
|
130
140
|
u = proju[i, j]
|
131
141
|
v = projv[i, j]
|
132
142
|
Jani = np.stack([sio.Jani_dict[key] for sio in self.dat])
|
133
|
-
projections = np.einsum(
|
143
|
+
projections = np.einsum("nmi,nij,nmj->nm", u, Jani, v).flatten()
|
134
144
|
except KeyError as err:
|
135
145
|
raise KeyError(
|
136
146
|
"Can not find key: %s, Please make sure the three calculations use the same k-mesh and same Rcut."
|
137
|
-
% err
|
147
|
+
% err
|
148
|
+
)
|
138
149
|
newJani = np.linalg.lstsq(coeff_matrix[i, j], projections, rcond=1e-2)[0]
|
139
|
-
Jani_dict[key] = np.array(
|
140
|
-
[
|
141
|
-
|
142
|
-
|
143
|
-
|
150
|
+
Jani_dict[key] = np.array(
|
151
|
+
[
|
152
|
+
[newJani[0], newJani[3], newJani[5]],
|
153
|
+
[newJani[3], newJani[1], newJani[4]],
|
154
|
+
[newJani[5], newJani[4], newJani[2]],
|
155
|
+
]
|
156
|
+
)
|
144
157
|
self.main_dat.Jani_dict = Jani_dict
|
145
158
|
|
146
159
|
def merge_Jiso(self):
|
147
|
-
Jdict={}
|
160
|
+
Jdict = {}
|
148
161
|
for key in self.main_dat.exchange_Jdict.keys():
|
149
162
|
try:
|
150
|
-
|
163
|
+
J = np.mean([obj.exchange_Jdict[key] for obj in self.dat])
|
151
164
|
except KeyError as err:
|
152
165
|
raise KeyError(
|
153
|
-
|
154
|
-
|
166
|
+
"Can not find key: %s, Please make sure the three calculations use the same k-mesh and same Rcut."
|
167
|
+
% err
|
168
|
+
)
|
155
169
|
Jdict[key] = J
|
156
170
|
self.main_dat.exchange_Jdict = Jdict
|
157
|
-
|
158
171
|
|
159
172
|
def merge_DMI(self):
|
160
173
|
dmi_ddict = {}
|
161
174
|
if all(obj.has_dmi for obj in self.dat):
|
162
|
-
projectors = self.projectors
|
175
|
+
projectors = self.projectors
|
176
|
+
proju = self.proju
|
163
177
|
for key in self.main_dat.dmi_ddict.keys():
|
164
178
|
try:
|
165
179
|
R, i, j = key
|
166
180
|
u = proju[i, j]
|
167
181
|
DMI = np.stack([sio.dmi_ddict[key] for sio in self.dat])
|
168
|
-
projections = np.einsum(
|
182
|
+
projections = np.einsum("nmi,ni->nm", u, DMI).flatten()
|
169
183
|
except KeyError as err:
|
170
184
|
raise KeyError(
|
171
185
|
"Can not find key: %s, Please make sure the three calculations use the same k-mesh and same Rcut."
|
172
|
-
% err
|
186
|
+
% err
|
187
|
+
)
|
173
188
|
newDMI = np.linalg.lstsq(projectors[i, j], projections, rcond=4e-1)[0]
|
174
189
|
dmi_ddict[key] = newDMI
|
175
190
|
self.main_dat.dmi_ddict = dmi_ddict
|
176
191
|
|
177
192
|
def standardize(self):
|
178
193
|
# make sure that the Jani has the trace of zero
|
179
|
-
Jdict=self.main_dat.exchange_Jdict
|
180
|
-
Jani_dict=self.main_dat.Jani_dict
|
194
|
+
Jdict = self.main_dat.exchange_Jdict
|
195
|
+
Jani_dict = self.main_dat.Jani_dict
|
181
196
|
for key in self.main_dat.Jani_dict.keys():
|
182
197
|
Jani = self.main_dat.Jani_dict[key]
|
183
|
-
shift = np.trace(Jani)/3.0
|
184
|
-
Jani_dict[key]
|
198
|
+
shift = np.trace(Jani) / 3.0
|
199
|
+
Jani_dict[key] -= shift * np.eye(3)
|
185
200
|
Jdict[key] += shift
|
186
201
|
self.main_dat.Jani_dict = Jani_dict
|
187
202
|
self.main_dat.exchange_Jdict = Jdict
|
188
|
-
|
189
203
|
|
190
|
-
|
204
|
+
|
205
|
+
def merge(*paths, main_path=None, save=True, write_path="TB2J_results"):
|
191
206
|
m = Merger(*paths, main_path=main_path)
|
192
207
|
m.merge_Jiso()
|
193
208
|
m.merge_DMI()
|
TB2J/magnon/magnon3.py
CHANGED
@@ -2,12 +2,16 @@ from dataclasses import asdict, dataclass
|
|
2
2
|
from pathlib import Path
|
3
3
|
from typing import List, Optional, Tuple, Union
|
4
4
|
|
5
|
+
import matplotlib.pyplot as plt
|
5
6
|
import numpy as np
|
6
7
|
import tomli
|
7
8
|
import tomli_w
|
9
|
+
from ase.dft.dos import DOS
|
10
|
+
from ase.units import J, eV
|
8
11
|
from scipy.spatial.transform import Rotation
|
9
12
|
|
10
13
|
from TB2J.io_exchange import SpinIO
|
14
|
+
from TB2J.kpoints import monkhorst_pack
|
11
15
|
from TB2J.magnon.magnon_band import MagnonBand
|
12
16
|
from TB2J.magnon.magnon_math import get_rotation_arrays
|
13
17
|
from TB2J.mathutils.auto_kpath import auto_kpath
|
@@ -162,10 +166,10 @@ class Magnon:
|
|
162
166
|
phase = 2 * np.pi * R @ qpt
|
163
167
|
Jq[iqpt] += np.exp(1j * phase) * JRprime[iR]
|
164
168
|
|
165
|
-
#Jq_copy = Jq.copy()
|
166
|
-
#Jq.swapaxes(-1, -2) # swap xyz
|
167
|
-
#Jq.swapaxes(-3, -4) # swap ij
|
168
|
-
#Jq = (Jq.conj() + Jq_copy) / 2.0
|
169
|
+
# Jq_copy = Jq.copy()
|
170
|
+
# Jq.swapaxes(-1, -2) # swap xyz
|
171
|
+
# Jq.swapaxes(-3, -4) # swap ij
|
172
|
+
# Jq = (Jq.conj() + Jq_copy) / 2.0
|
169
173
|
return Jq
|
170
174
|
|
171
175
|
def Hq(self, kpoints):
|
@@ -187,7 +191,6 @@ class Magnon:
|
|
187
191
|
magmoms = self.magmom.copy()
|
188
192
|
magmoms /= np.linalg.norm(magmoms, axis=-1)[:, None]
|
189
193
|
|
190
|
-
|
191
194
|
U, V = get_rotation_arrays(magmoms, u=self._uz)
|
192
195
|
|
193
196
|
J0 = -self.Jq(np.zeros((1, 3)))[0]
|
@@ -195,7 +198,6 @@ class Magnon:
|
|
195
198
|
# Jq = -Hermitize(self.Jq(kpoints, anisotropic=anisotropic))
|
196
199
|
|
197
200
|
Jq = -self.Jq(kpoints)
|
198
|
-
print(f"J0 shape: {J0.shape}")
|
199
201
|
|
200
202
|
C = np.diag(np.einsum("ix,ijxy,jy->i", V, 2 * J0, V))
|
201
203
|
B = np.einsum("ix,kijxy,jy->kij", U, Jq, U)
|
@@ -203,7 +205,6 @@ class Magnon:
|
|
203
205
|
A2 = np.einsum("ix,kijxy,jy->kij", U.conj(), Jq, U)
|
204
206
|
|
205
207
|
H = np.block([[A1 - C, B], [B.swapaxes(-1, -2).conj(), A2 - C]])
|
206
|
-
print(f"H shape: {H.shape}")
|
207
208
|
return H
|
208
209
|
|
209
210
|
def _magnon_energies(self, kpoints, u=None):
|
@@ -801,12 +802,11 @@ def main():
|
|
801
802
|
)
|
802
803
|
|
803
804
|
parser.add_argument(
|
804
|
-
"--show",
|
805
|
+
"--show",
|
805
806
|
action="store_true",
|
806
807
|
default=False,
|
807
808
|
help="show figure on screen.",
|
808
|
-
|
809
|
-
|
809
|
+
)
|
810
810
|
|
811
811
|
args = parser.parse_args()
|
812
812
|
|
@@ -834,7 +834,7 @@ def main():
|
|
834
834
|
Q=args.Q if args.Q is not None else None,
|
835
835
|
uz_file=args.uz_file,
|
836
836
|
n=args.n if args.n is not None else None,
|
837
|
-
show=args.show
|
837
|
+
show=args.show,
|
838
838
|
)
|
839
839
|
|
840
840
|
plot_magnon_bands_from_TB2J(params)
|
@@ -842,3 +842,97 @@ def main():
|
|
842
842
|
|
843
843
|
if __name__ == "__main__":
|
844
844
|
main()
|
845
|
+
|
846
|
+
|
847
|
+
class MagnonASEWrapper:
|
848
|
+
def __init__(self, magnon: Magnon):
|
849
|
+
self.magnon = magnon
|
850
|
+
self.atoms = None
|
851
|
+
self.kpts = None
|
852
|
+
self.weights = None
|
853
|
+
self.dos_args = {}
|
854
|
+
|
855
|
+
def set(self, atoms=None, kmesh=[9, 9, 9], gamma=True, **kwargs):
|
856
|
+
self.atoms = atoms
|
857
|
+
self.dos_args = {
|
858
|
+
"kmesh": kmesh,
|
859
|
+
"gamma": gamma,
|
860
|
+
}
|
861
|
+
self.kpts = monkhorst_pack(
|
862
|
+
self.dos_args["kmesh"], gamma_center=self.dos_args["gamma"]
|
863
|
+
)
|
864
|
+
self.weights = np.ones(len(self.kpts)) / len(self.kpts)
|
865
|
+
|
866
|
+
def get_k_points_and_weights(self):
|
867
|
+
return self.kpts, self.weights
|
868
|
+
|
869
|
+
def get_k_point_weights(self):
|
870
|
+
return self.weights
|
871
|
+
|
872
|
+
def get_number_of_spins(self):
|
873
|
+
return 1
|
874
|
+
|
875
|
+
def get_eigenvalues(self, kpt, spin=0):
|
876
|
+
"""
|
877
|
+
return the eigenvalues at a given k-point. The energy unit is eV
|
878
|
+
args:
|
879
|
+
kpt: k-point index.
|
880
|
+
spin: spin index.
|
881
|
+
"""
|
882
|
+
kpoint = self.kpts[kpt]
|
883
|
+
# Magnon energies are already in eV, convert to meV for consistency with plot
|
884
|
+
evals = self.magnon._magnon_energies(np.array([kpoint]))[0]
|
885
|
+
evals = evals * J / eV # Convert to eV
|
886
|
+
return evals
|
887
|
+
|
888
|
+
def get_fermi_level(self):
|
889
|
+
return 0.0
|
890
|
+
|
891
|
+
def get_bz_k_points(self):
|
892
|
+
return self.kpts
|
893
|
+
|
894
|
+
def get_dos(self, width=0.1, window=None, npts=401):
|
895
|
+
dos = DOS(self, width=width, window=window, npts=npts)
|
896
|
+
energies = dos.get_energies()
|
897
|
+
tdos = dos.get_dos()
|
898
|
+
return energies, tdos
|
899
|
+
|
900
|
+
def plot_dos(
|
901
|
+
self,
|
902
|
+
smearing_width=0.0001,
|
903
|
+
window=None,
|
904
|
+
npts=401,
|
905
|
+
output="magnon_dos.pdf",
|
906
|
+
ax=None,
|
907
|
+
show=True,
|
908
|
+
dos_filename="magnon_dos.txt",
|
909
|
+
):
|
910
|
+
"""
|
911
|
+
plot total DOS.
|
912
|
+
:param width: width of Gaussian smearing
|
913
|
+
:param window: energy window
|
914
|
+
:param npts: number of points
|
915
|
+
:param output: output filename
|
916
|
+
:param ax: matplotlib axis
|
917
|
+
:return: ax
|
918
|
+
"""
|
919
|
+
if ax is None:
|
920
|
+
_fig, ax = plt.subplots()
|
921
|
+
energies, tdos = self.get_dos(width=smearing_width, window=window, npts=npts)
|
922
|
+
energies = energies * 1000 # Convert to meV
|
923
|
+
tdos = tdos / 1000 # Convert to states/meV
|
924
|
+
if dos_filename is not None:
|
925
|
+
np.savetxt(
|
926
|
+
dos_filename,
|
927
|
+
np.array([energies, tdos]).T,
|
928
|
+
header="Energy(meV) DOS(state/meV)",
|
929
|
+
)
|
930
|
+
ax.plot(energies, tdos)
|
931
|
+
ax.set_xlabel("Energy (meV)")
|
932
|
+
ax.set_ylabel("DOS (states/meV)")
|
933
|
+
ax.set_title("Total DOS")
|
934
|
+
if output is not None:
|
935
|
+
plt.savefig(output)
|
936
|
+
if show:
|
937
|
+
plt.show()
|
938
|
+
return ax
|
TB2J/magnon/magnon_band.py
CHANGED
@@ -40,7 +40,7 @@ class MagnonBand:
|
|
40
40
|
if self.xcoords is None:
|
41
41
|
self.xcoords = np.arange(len(self.kpoints))
|
42
42
|
|
43
|
-
def plot(self, ax=None, filename=None, show=False, shift=0.0
|
43
|
+
def plot(self, ax=None, filename=None, show=False, shift=0.0, **kwargs):
|
44
44
|
"""Plot the magnon band structure.
|
45
45
|
|
46
46
|
Parameters
|
@@ -79,7 +79,7 @@ class MagnonBand:
|
|
79
79
|
for band in segment_bands:
|
80
80
|
ax.plot(
|
81
81
|
x,
|
82
|
-
band[start_idx : start_idx + nbands]+shift,
|
82
|
+
band[start_idx : start_idx + nbands] + shift,
|
83
83
|
linewidth=linewidth,
|
84
84
|
color=color,
|
85
85
|
linestyle=linestyle,
|
@@ -91,7 +91,7 @@ class MagnonBand:
|
|
91
91
|
for band in self.energies.T:
|
92
92
|
ax.plot(
|
93
93
|
self.xcoords,
|
94
|
-
band+shift,
|
94
|
+
band + shift,
|
95
95
|
linewidth=linewidth,
|
96
96
|
color=color,
|
97
97
|
linestyle=linestyle,
|
@@ -0,0 +1,308 @@
|
|
1
|
+
"""Module for magnon density of states calculations and plotting."""
|
2
|
+
|
3
|
+
import json
|
4
|
+
from dataclasses import dataclass
|
5
|
+
from pathlib import Path
|
6
|
+
from typing import Optional
|
7
|
+
|
8
|
+
import matplotlib.pyplot as plt
|
9
|
+
import numpy as np
|
10
|
+
from ase.dft.dos import DOS
|
11
|
+
|
12
|
+
from TB2J.kpoints import monkhorst_pack
|
13
|
+
|
14
|
+
|
15
|
+
@dataclass
|
16
|
+
class MagnonDOS:
|
17
|
+
"""Data class for storing magnon DOS data"""
|
18
|
+
|
19
|
+
energies: np.ndarray # DOS energy points in meV
|
20
|
+
dos: np.ndarray # DOS values in states/meV
|
21
|
+
weights: Optional[np.ndarray] = None # k-point weights
|
22
|
+
kpoints: Optional[np.ndarray] = None # k-points used for DOS
|
23
|
+
|
24
|
+
def save(self, filename: str):
|
25
|
+
"""Save DOS data to a JSON file.
|
26
|
+
|
27
|
+
Parameters
|
28
|
+
----------
|
29
|
+
filename : str
|
30
|
+
Output filename (should end in .json)
|
31
|
+
"""
|
32
|
+
# Convert numpy arrays to lists for JSON serialization
|
33
|
+
data = {
|
34
|
+
"energies": self.energies.tolist(),
|
35
|
+
"dos": self.dos.tolist(),
|
36
|
+
}
|
37
|
+
if self.weights is not None:
|
38
|
+
data["weights"] = self.weights.tolist()
|
39
|
+
if self.kpoints is not None:
|
40
|
+
data["kpoints"] = self.kpoints.tolist()
|
41
|
+
|
42
|
+
with open(filename, "w") as f:
|
43
|
+
json.dump(data, f)
|
44
|
+
|
45
|
+
@classmethod
|
46
|
+
def load(cls, filename: str) -> "MagnonDOS":
|
47
|
+
"""Load DOS data from a JSON file.
|
48
|
+
|
49
|
+
Parameters
|
50
|
+
----------
|
51
|
+
filename : str
|
52
|
+
Input JSON filename
|
53
|
+
|
54
|
+
Returns
|
55
|
+
-------
|
56
|
+
MagnonDOS
|
57
|
+
Loaded DOS object
|
58
|
+
"""
|
59
|
+
with open(filename) as f:
|
60
|
+
data = json.load(f)
|
61
|
+
|
62
|
+
# Convert lists back to numpy arrays
|
63
|
+
data["energies"] = np.array(data["energies"])
|
64
|
+
data["dos"] = np.array(data["dos"])
|
65
|
+
if "weights" in data:
|
66
|
+
data["weights"] = np.array(data["weights"])
|
67
|
+
if "kpoints" in data:
|
68
|
+
data["kpoints"] = np.array(data["kpoints"])
|
69
|
+
|
70
|
+
return cls(**data)
|
71
|
+
|
72
|
+
def plot(self, ax=None, color="blue", show=True, filename=None, **plot_kwargs):
|
73
|
+
"""Plot the magnon DOS.
|
74
|
+
|
75
|
+
Parameters
|
76
|
+
----------
|
77
|
+
ax : matplotlib.axes.Axes, optional
|
78
|
+
Axis to plot on. If None, creates new figure
|
79
|
+
color : str, optional
|
80
|
+
Color for DOS line
|
81
|
+
show : bool, optional
|
82
|
+
Whether to show plot
|
83
|
+
filename : str, optional
|
84
|
+
If provided, saves plot to file
|
85
|
+
**plot_kwargs : dict
|
86
|
+
Additional keyword arguments passed to plot
|
87
|
+
|
88
|
+
Returns
|
89
|
+
-------
|
90
|
+
matplotlib.axes.Axes
|
91
|
+
The plotting axes
|
92
|
+
"""
|
93
|
+
if ax is None:
|
94
|
+
_, ax = plt.subplots()
|
95
|
+
|
96
|
+
ax.plot(self.energies, self.dos, color=color, **plot_kwargs)
|
97
|
+
ax.set_xlabel("Energy (meV)")
|
98
|
+
ax.set_ylabel("DOS (states/meV)")
|
99
|
+
ax.set_title("Magnon DOS")
|
100
|
+
|
101
|
+
if filename:
|
102
|
+
plt.savefig(filename)
|
103
|
+
if show:
|
104
|
+
plt.show()
|
105
|
+
|
106
|
+
return ax
|
107
|
+
|
108
|
+
|
109
|
+
class MagnonDOSCalculator:
|
110
|
+
"""Calculator for magnon density of states"""
|
111
|
+
|
112
|
+
def __init__(self, magnon):
|
113
|
+
"""Initialize DOS calculator
|
114
|
+
|
115
|
+
Parameters
|
116
|
+
----------
|
117
|
+
magnon : Magnon
|
118
|
+
Magnon object containing exchange parameters
|
119
|
+
"""
|
120
|
+
self.magnon = magnon
|
121
|
+
self.kpts = None
|
122
|
+
self.weights = None
|
123
|
+
self.dos_args = {}
|
124
|
+
|
125
|
+
def estimate_energy_range(self, padding_factor=1.2):
|
126
|
+
"""Estimate the energy range of eigenvalues.
|
127
|
+
|
128
|
+
Computes eigenvalues at zone center and high-symmetry points at zone boundaries
|
129
|
+
to estimate the full range of magnon energies.
|
130
|
+
|
131
|
+
Parameters
|
132
|
+
----------
|
133
|
+
padding_factor : float, optional
|
134
|
+
Factor to extend the energy window beyond min/max values.
|
135
|
+
Default is 1.2 (20% padding).
|
136
|
+
|
137
|
+
Returns
|
138
|
+
-------
|
139
|
+
tuple
|
140
|
+
(min_energy, max_energy) in eV
|
141
|
+
"""
|
142
|
+
# Generate high-symmetry points
|
143
|
+
kpoints = np.array(
|
144
|
+
[
|
145
|
+
[0.0, 0.0, 0.0], # Γ (zone center)
|
146
|
+
[0.5, 0.0, 0.0], # X
|
147
|
+
[0.5, 0.5, 0.0], # M
|
148
|
+
[0.5, 0.5, 0.5], # R (zone corner)
|
149
|
+
[0.0, 0.5, 0.0], # Y
|
150
|
+
[0.0, 0.0, 0.5], # Z
|
151
|
+
]
|
152
|
+
)
|
153
|
+
|
154
|
+
# Calculate eigenvalues at these points
|
155
|
+
evals = self.magnon._magnon_energies(kpoints)
|
156
|
+
min_energy = evals.min()
|
157
|
+
max_energy = evals.max()
|
158
|
+
|
159
|
+
# Add padding and convert to eV
|
160
|
+
window_size = max_energy - min_energy
|
161
|
+
min_energy = min_energy - (padding_factor - 1) * window_size
|
162
|
+
max_energy = max_energy + (padding_factor - 1) * window_size
|
163
|
+
|
164
|
+
return min_energy, max_energy
|
165
|
+
|
166
|
+
def set_kmesh(self, kmesh=[9, 9, 9], gamma=True):
|
167
|
+
"""Set k-point mesh for DOS calculation.
|
168
|
+
|
169
|
+
Parameters
|
170
|
+
----------
|
171
|
+
kmesh : list, optional
|
172
|
+
Number of k-points along each direction
|
173
|
+
gamma : bool, optional
|
174
|
+
Whether to include Gamma point
|
175
|
+
"""
|
176
|
+
self.kpts = monkhorst_pack(kmesh, gamma_center=gamma)
|
177
|
+
self.weights = np.ones(len(self.kpts)) / len(self.kpts)
|
178
|
+
|
179
|
+
def get_fermi_level(self):
|
180
|
+
return 0.0 # Fermi energy is not used in magnon calculations
|
181
|
+
|
182
|
+
def get_eigenvalues(self, kpt, spin=0):
|
183
|
+
"""Get eigenvalues at a k-point.
|
184
|
+
|
185
|
+
Parameters
|
186
|
+
----------
|
187
|
+
kpt : int
|
188
|
+
K-point index
|
189
|
+
spin : int, optional
|
190
|
+
Spin index (unused)
|
191
|
+
|
192
|
+
Returns
|
193
|
+
-------
|
194
|
+
numpy.ndarray
|
195
|
+
Eigenvalues in eV
|
196
|
+
"""
|
197
|
+
kpoint = self.kpts[kpt]
|
198
|
+
evals = self.magnon._magnon_energies(np.array([kpoint]))[0]
|
199
|
+
return evals
|
200
|
+
|
201
|
+
def get_dos(self, width=0.1, window=None, npts=1001):
|
202
|
+
"""Calculate DOS using ASE's DOS module.
|
203
|
+
|
204
|
+
Parameters
|
205
|
+
----------
|
206
|
+
width : float, optional
|
207
|
+
Gaussian smearing width in eV
|
208
|
+
window : tuple, optional
|
209
|
+
Energy window (min, max) in eV
|
210
|
+
npts : int, optional
|
211
|
+
Number of energy points
|
212
|
+
|
213
|
+
Returns
|
214
|
+
-------
|
215
|
+
MagnonDOS
|
216
|
+
Calculated DOS object
|
217
|
+
"""
|
218
|
+
if self.kpts is None:
|
219
|
+
self.set_kmesh()
|
220
|
+
|
221
|
+
# Estimate energy window if not provided
|
222
|
+
if window is None:
|
223
|
+
window = self.estimate_energy_range()
|
224
|
+
|
225
|
+
dos_calc = DOS(self, width=width, window=window, npts=npts)
|
226
|
+
energies = dos_calc.get_energies()
|
227
|
+
dos_vals = dos_calc.get_dos()
|
228
|
+
|
229
|
+
# Convert to meV
|
230
|
+
energies = energies * 1000 # eV to meV
|
231
|
+
dos_vals = dos_vals / 1000 # states/eV to states/meV
|
232
|
+
|
233
|
+
return MagnonDOS(
|
234
|
+
energies=energies,
|
235
|
+
dos=dos_vals,
|
236
|
+
weights=self.weights,
|
237
|
+
kpoints=self.kpts,
|
238
|
+
)
|
239
|
+
|
240
|
+
def get_number_of_spins(self):
|
241
|
+
"""Required by ASE DOS calculator."""
|
242
|
+
return 1
|
243
|
+
|
244
|
+
def get_k_point_weights(self):
|
245
|
+
"""Required by ASE DOS calculator."""
|
246
|
+
return self.weights
|
247
|
+
|
248
|
+
def get_bz_k_points(self):
|
249
|
+
"""Required by ASE DOS calculator."""
|
250
|
+
return self.kpts
|
251
|
+
|
252
|
+
|
253
|
+
def plot_magnon_dos(
|
254
|
+
magnon,
|
255
|
+
kmesh=[9, 9, 9],
|
256
|
+
gamma=True,
|
257
|
+
width=0.0005,
|
258
|
+
window=None,
|
259
|
+
xlim=None,
|
260
|
+
npts=1001,
|
261
|
+
filename=None,
|
262
|
+
save_data=True,
|
263
|
+
show=True,
|
264
|
+
):
|
265
|
+
"""Convenience function to calculate and plot magnon DOS.
|
266
|
+
|
267
|
+
Parameters
|
268
|
+
----------
|
269
|
+
magnon : Magnon
|
270
|
+
Magnon object containing exchange parameters
|
271
|
+
kmesh : list, optional
|
272
|
+
Number of k-points along each direction
|
273
|
+
gamma : bool, optional
|
274
|
+
Whether to include Gamma point
|
275
|
+
width : float, optional
|
276
|
+
Gaussian smearing width in eV
|
277
|
+
window : tuple, optional
|
278
|
+
Energy window (min, max) in eV
|
279
|
+
npts : int, optional
|
280
|
+
Number of energy points
|
281
|
+
filename : str, optional
|
282
|
+
Output filename for plot
|
283
|
+
save_data : bool, optional
|
284
|
+
Whether to save DOS data to JSON
|
285
|
+
show : bool, optional
|
286
|
+
Whether to show plot
|
287
|
+
|
288
|
+
Returns
|
289
|
+
-------
|
290
|
+
MagnonDOS
|
291
|
+
The calculated DOS object
|
292
|
+
"""
|
293
|
+
calculator = MagnonDOSCalculator(magnon)
|
294
|
+
calculator.set_kmesh(kmesh=kmesh, gamma=gamma)
|
295
|
+
dos = calculator.get_dos(width=width, window=window, npts=npts)
|
296
|
+
|
297
|
+
# Plot DOS
|
298
|
+
dos.plot(filename=filename, show=show)
|
299
|
+
|
300
|
+
# Save data if requested
|
301
|
+
if save_data:
|
302
|
+
data_file = (
|
303
|
+
Path(filename).with_suffix(".json") if filename else Path("magnon_dos.json")
|
304
|
+
)
|
305
|
+
dos.save(data_file)
|
306
|
+
print(f"DOS data saved to {data_file}")
|
307
|
+
|
308
|
+
return dos
|
TB2J/magnon/magnon_math.py
CHANGED
@@ -32,7 +32,7 @@ def get_rotation_arrays(magmoms, u=uz):
|
|
32
32
|
v = magmoms
|
33
33
|
n = np.cross(u, v)
|
34
34
|
n /= np.linalg.norm(n, axis=-1).reshape(dim, 1)
|
35
|
-
#z = u #np.repeat(u, dim, axis=0)
|
35
|
+
# z = u #np.repeat(u, dim, axis=0)
|
36
36
|
z = np.repeat(u, dim, axis=0)
|
37
37
|
A = np.stack([z, np.cross(n, z), n], axis=1)
|
38
38
|
B = np.stack([v, np.cross(n, v), n], axis=1)
|
@@ -0,0 +1,99 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""Command-line tool for plotting magnon DOS from TB2J results."""
|
3
|
+
|
4
|
+
import argparse
|
5
|
+
from pathlib import Path
|
6
|
+
|
7
|
+
from TB2J.magnon.magnon3 import Magnon
|
8
|
+
from TB2J.magnon.magnon_dos import plot_magnon_dos
|
9
|
+
|
10
|
+
|
11
|
+
def main():
|
12
|
+
parser = argparse.ArgumentParser(
|
13
|
+
description="Calculate and plot magnon DOS from TB2J results"
|
14
|
+
)
|
15
|
+
parser.add_argument(
|
16
|
+
"--path",
|
17
|
+
default="TB2J_results",
|
18
|
+
help="Path to TB2J results directory (default: TB2J_results)",
|
19
|
+
)
|
20
|
+
parser.add_argument(
|
21
|
+
"--kmesh",
|
22
|
+
type=int,
|
23
|
+
nargs=3,
|
24
|
+
default=[20, 20, 20],
|
25
|
+
metavar=("nx", "ny", "nz"),
|
26
|
+
help="k-point mesh dimensions (default: 20, 20, 20)",
|
27
|
+
)
|
28
|
+
parser.add_argument(
|
29
|
+
"--no-gamma",
|
30
|
+
action="store_false",
|
31
|
+
dest="gamma",
|
32
|
+
help="Exclude Gamma point from k-mesh",
|
33
|
+
)
|
34
|
+
parser.add_argument(
|
35
|
+
"--width",
|
36
|
+
type=float,
|
37
|
+
default=0.001,
|
38
|
+
help="Gaussian smearing width in eV (default: 0.001)",
|
39
|
+
)
|
40
|
+
parser.add_argument(
|
41
|
+
"--window",
|
42
|
+
type=float,
|
43
|
+
nargs=2,
|
44
|
+
metavar=("emin", "emax"),
|
45
|
+
help="Energy window in meV (optional)",
|
46
|
+
)
|
47
|
+
parser.add_argument(
|
48
|
+
"--npts",
|
49
|
+
type=int,
|
50
|
+
default=401,
|
51
|
+
help="Number of energy points (default: 401)",
|
52
|
+
)
|
53
|
+
parser.add_argument(
|
54
|
+
"--output",
|
55
|
+
default="magnon_dos.png",
|
56
|
+
help="Output filename for plot (default: magnon_dos.png)",
|
57
|
+
)
|
58
|
+
parser.add_argument(
|
59
|
+
"-show",
|
60
|
+
action="store_true",
|
61
|
+
dest="show",
|
62
|
+
help="Show plot window",
|
63
|
+
)
|
64
|
+
|
65
|
+
args = parser.parse_args()
|
66
|
+
|
67
|
+
# Check if TB2J results exist
|
68
|
+
if not Path(args.path).exists():
|
69
|
+
raise FileNotFoundError(f"TB2J results not found at {args.path}")
|
70
|
+
|
71
|
+
# Load magnon calculator
|
72
|
+
print(f"Loading exchange parameters from {args.path}...")
|
73
|
+
magnon = Magnon.from_TB2J_results(path=args.path)
|
74
|
+
|
75
|
+
# Convert window from meV to eV if provided
|
76
|
+
window = None
|
77
|
+
if args.window is not None:
|
78
|
+
window = (args.window[0] / 1000, args.window[1] / 1000)
|
79
|
+
|
80
|
+
# Calculate and plot DOS
|
81
|
+
print("\nCalculating magnon DOS...")
|
82
|
+
_dos = plot_magnon_dos(
|
83
|
+
magnon,
|
84
|
+
kmesh=args.kmesh,
|
85
|
+
gamma=args.gamma,
|
86
|
+
width=args.width,
|
87
|
+
window=window,
|
88
|
+
npts=args.npts,
|
89
|
+
filename=args.output,
|
90
|
+
show=args.show,
|
91
|
+
)
|
92
|
+
|
93
|
+
print(f"\nPlot saved to {args.output}")
|
94
|
+
data_file = Path(args.output).with_suffix(".json")
|
95
|
+
print(f"DOS data saved to {data_file}")
|
96
|
+
|
97
|
+
|
98
|
+
if __name__ == "__main__":
|
99
|
+
main()
|
@@ -1,8 +1,8 @@
|
|
1
1
|
#!python
|
2
2
|
import argparse
|
3
|
-
|
4
|
-
import sys
|
3
|
+
|
5
4
|
from TB2J.io_merge import merge
|
5
|
+
from TB2J.version_info import print_license
|
6
6
|
|
7
7
|
|
8
8
|
def main():
|
@@ -32,14 +32,18 @@ def main():
|
|
32
32
|
"--main_path",
|
33
33
|
help="The path containning the reference structure.",
|
34
34
|
type=str,
|
35
|
-
default=None
|
35
|
+
default=None,
|
36
36
|
)
|
37
37
|
|
38
38
|
args = parser.parse_args()
|
39
39
|
# merge(*(args.directories), args.type.strip().lower(), path=args.output_path)
|
40
40
|
# merge(*(args.directories), method=args.type.strip().lower(), path=args.output_path)
|
41
|
-
#merge2(args.directories, args.type.strip().lower(), path=args.output_path)
|
41
|
+
# merge2(args.directories, args.type.strip().lower(), path=args.output_path)
|
42
|
+
print_license()
|
43
|
+
print("Merging the TB2J results from the following directories: ", args.directories)
|
42
44
|
merge(*args.directories, main_path=args.main_path, write_path=args.output_path)
|
45
|
+
print("Merging completed. The results are saved in:", args.output_path)
|
43
46
|
|
44
47
|
|
45
|
-
|
48
|
+
if __name__ == "__main__":
|
49
|
+
main()
|
@@ -0,0 +1,27 @@
|
|
1
|
+
#!python
|
2
|
+
|
3
|
+
|
4
|
+
if __name__ == "__main__":
|
5
|
+
# add a warning messege that this functionality is under development and should not be used in production.
|
6
|
+
# make it visually distinct, e.g. with a different color or formatting.
|
7
|
+
import warnings
|
8
|
+
|
9
|
+
from TB2J.magnon.magnon3 import main
|
10
|
+
|
11
|
+
warnings.warn(
|
12
|
+
"""
|
13
|
+
# !!!!!!!!!!!!!!!!!! WARNING: =============================
|
14
|
+
#
|
15
|
+
# This functionality is under development and should not be used in production.
|
16
|
+
# It is provided for testing and development purposes only.
|
17
|
+
# Please use with caution and report any issues to the developers.
|
18
|
+
#
|
19
|
+
# This warning will be removed in future releases.
|
20
|
+
# =====================================
|
21
|
+
|
22
|
+
""",
|
23
|
+
UserWarning,
|
24
|
+
stacklevel=2,
|
25
|
+
)
|
26
|
+
# Call the main function from the magnons module
|
27
|
+
main()
|
@@ -1,17 +1,17 @@
|
|
1
1
|
TB2J/Jdownfolder.py,sha256=n5BeQCYP4mD9JsAPeE1F3ZKKR3SUxADfDbaG_rzi77k,9658
|
2
2
|
TB2J/Jtensor.py,sha256=Wi06AAbfKFU6f2a2jkFr9zU2cwwfVroajTp-dwCtkTE,3160
|
3
3
|
TB2J/MAE.py,sha256=fM8U-Dgp9HcQOEeC_kyZV1oVrygBvcux9BraUXVouvY,10994
|
4
|
-
TB2J/MAEGreen.py,sha256=
|
4
|
+
TB2J/MAEGreen.py,sha256=zVLjQtFDFcRmC8PYm7MOgKnkOuFJEMKr-eoNBABUsMc,15759
|
5
5
|
TB2J/Oiju.py,sha256=cNGv8N5uH_swGq7cnAt2OyiDfqtjLlLrwseGu0E4iaM,3383
|
6
6
|
TB2J/Oiju_epc.py,sha256=oytM3NYW7nWmklrGgNlqwIpI_JYv_hb7ZnR4o9nYNog,6809
|
7
|
-
TB2J/__init__.py,sha256=
|
7
|
+
TB2J/__init__.py,sha256=VIRI2sifGP0sSwxkmWEp9IvuVn2fBhvrNpmqbwLrG8s,25
|
8
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=-BEXSQqx374cRbJWjvjyJAZkchzNK1uPUDvTYox3Axs,26982
|
15
15
|
TB2J/exchangeCL2.py,sha256=P7bklMXVYX_tn9DbjEPqeTir1SeZyfPBIP1fhWUzLmY,11419
|
16
16
|
TB2J/exchange_params.py,sha256=VW9nGVio6M_Ub9-36L_LExhjgdD1E_joYpI8AxmM360,8029
|
17
17
|
TB2J/exchange_pert.py,sha256=jmFMtQbYa_uczM4VAeS6TijkIHRFIqEzZJswzE9Wfuo,8523
|
@@ -19,7 +19,7 @@ TB2J/exchange_qspace.py,sha256=ZL68qBGFUaQ9BsSPsJaaoWOr9RssPiqX34R_9I3nk_8,8436
|
|
19
19
|
TB2J/gpaw_wrapper.py,sha256=aJ--9Dtyq7jOP1Hkh-Sh1nWcfXm6zKcljOCO0DNCAr0,6890
|
20
20
|
TB2J/green.py,sha256=ySXjoV3Cj_E2C41dRfc3TkS7M4vmM6qcHXkt-_-jxsY,17071
|
21
21
|
TB2J/greentest.py,sha256=2ISSfhor9ecSEOi_E6b4Cv26wEIQlwlzca0ru8z44_E,1603
|
22
|
-
TB2J/io_merge.py,sha256=
|
22
|
+
TB2J/io_merge.py,sha256=mgoc8ZUP_a_g-9KvWWISp25qo6aBJ8TQsDsBrMSB5nk,7509
|
23
23
|
TB2J/kpoints.py,sha256=9L7tBarFBHoIhpuc9zuwA6HdnlgH834SQrPek4yRoWk,3191
|
24
24
|
TB2J/myTB.py,sha256=ok_B4my29bOIghMSZfx0Es6G8FaXaIiLP4gPxTdSj00,17659
|
25
25
|
TB2J/mycfr.py,sha256=ZF1PEE2khlKd_4gPyMkoNXepX3XqwWAL2kDbRJNVX-Y,3908
|
@@ -43,31 +43,33 @@ TB2J/interfaces/__init__.py,sha256=4tiLoXQ73Nlyi9L4j8jJXOYzXluVNPxQZkwfkQZEGHg,3
|
|
43
43
|
TB2J/interfaces/gpaw_interface.py,sha256=GCDlJ-hRWfChvWwsgBDYSmVqO4sH9HAuGZTV9GqgN6c,1504
|
44
44
|
TB2J/interfaces/lawaf_interface.py,sha256=PieLnmppdafOYsgeHznqOou1g9L1sam5jOm3KaObdqo,4408
|
45
45
|
TB2J/interfaces/manager.py,sha256=PQMLEfMCT5GnDWSl2nI4JOgRPm_fysyR-6Y6l97xWcw,860
|
46
|
-
TB2J/interfaces/siesta_interface.py,sha256=
|
46
|
+
TB2J/interfaces/siesta_interface.py,sha256=Te9-Au0ZGAt-LfEnG8exUFD4lMj6uZu8RmHjopb0XLU,7595
|
47
47
|
TB2J/interfaces/wannier90_interface.py,sha256=qzRgXUBb7t1Aiegrl_RV51BB8csdtVM0EP0Z4pjmTcs,4467
|
48
48
|
TB2J/interfaces/abacus/__init__.py,sha256=leas71oCvM_HxrF4gnO5A_VKcJmDAgsI1BUctLU3OBw,177
|
49
49
|
TB2J/interfaces/abacus/abacus_api.py,sha256=lNV4LNkLcKw7Zux4MQYM9wnh3eFTlcSqbf4Pb7pqhrk,7243
|
50
50
|
TB2J/interfaces/abacus/abacus_wrapper.py,sha256=bQFnvvy-0hruTavH_VspMk1wD32t2UFybEkCgwkwle0,11941
|
51
51
|
TB2J/interfaces/abacus/gen_exchange_abacus.py,sha256=v-AUHGkJWeMNf4D5A4wOpCjM6DygQsFB6SWu-BGdTxM,3262
|
52
52
|
TB2J/interfaces/abacus/orbital_api.py,sha256=QEAyy4y_uM5GnwRyLvmShjd_z5A2fwqk7L8yj0Y2Mgc,1577
|
53
|
-
TB2J/interfaces/abacus/stru_api.py,sha256=
|
53
|
+
TB2J/interfaces/abacus/stru_api.py,sha256=Hb9MCo_4d_DVEEhntvBNAFw5KQ46TkAADn4z7CT6qOk,67801
|
54
54
|
TB2J/interfaces/abacus/test_density_matrix.py,sha256=bMWWJYtDS57SpPZ-eZXZ9Hr_UK4mv8ZHM7SzItG3IVA,774
|
55
55
|
TB2J/interfaces/abacus/test_read_HRSR.py,sha256=W1oO_yigT50Yb5_u-KB_IfTpM7kArGkBuMSMs0H4CTs,1235
|
56
56
|
TB2J/interfaces/abacus/test_read_stru.py,sha256=hoKPHVco8vwzC7Gao4bOPCdAPhh29x-9DTJJqRr7AYM,788
|
57
57
|
TB2J/io_exchange/__init__.py,sha256=LqEnG67qDVKt4hCUywDEQvUIJ7jsQjmtueOW_J16NOE,54
|
58
|
-
TB2J/io_exchange/io_exchange.py,sha256=
|
58
|
+
TB2J/io_exchange/io_exchange.py,sha256=TQPsxpV0vyFq-HFRRt9GQbpnmRw-5KwyM26eb9fKaoY,22743
|
59
59
|
TB2J/io_exchange/io_multibinit.py,sha256=8PDmWxzGuv-GwJosj2ZTmiyNY_duFVWJ4ekCuSqGdd8,6739
|
60
60
|
TB2J/io_exchange/io_tomsasd.py,sha256=NqkAC1Fl-CUnFA21eBzSy_S5F_oeQFJysw4UukQbN8o,4173
|
61
61
|
TB2J/io_exchange/io_txt.py,sha256=BMr1eSILlKpgtjvDx7uw2VMAkEKSvGEPNxpaT_zev0I,10547
|
62
62
|
TB2J/io_exchange/io_uppasd.py,sha256=bI4iPEgnK4TvCZNvb6x2xYXgjW7pEehCqmcizy2pqFU,3301
|
63
|
-
TB2J/io_exchange/io_vampire.py,sha256=
|
63
|
+
TB2J/io_exchange/io_vampire.py,sha256=vOStLmtCiWLp9GPhZpsAmrtaRHg9KSmtOM2Fky6yCQA,5762
|
64
64
|
TB2J/magnon/__init__.py,sha256=Q69duroGIIotgW_71ZdHYarSiJLGAu9NPYgEacUk6eI,117
|
65
65
|
TB2J/magnon/io_exchange2.py,sha256=EcC3x6H13qq61WBsr__xKzHDtSvy_dMz1tEdUaqSG2I,23265
|
66
|
-
TB2J/magnon/magnon3.py,sha256=
|
67
|
-
TB2J/magnon/magnon_band.py,sha256=
|
66
|
+
TB2J/magnon/magnon3.py,sha256=PFZFiyfYTIBscVu8SzvGxtrXhT-QM66EwIhWOqgSYQE,30219
|
67
|
+
TB2J/magnon/magnon_band.py,sha256=ZLHK1qf2Clu9jlcvBwBCBEi1MrPfy2BWrow8JJ2v5pk,6103
|
68
|
+
TB2J/magnon/magnon_dos.py,sha256=uztzsxCmFZpDHl-JziaUb_NNwBeZw9-L2ozd3XNTvxI,8506
|
68
69
|
TB2J/magnon/magnon_io.py,sha256=H4bmzCcnh1D3Sb6UBIIKWa6jIrA20dg9lX4wfLXHEjo,1241
|
69
|
-
TB2J/magnon/magnon_math.py,sha256=
|
70
|
+
TB2J/magnon/magnon_math.py,sha256=0mVQY5tXe6fHsfijivXf-1-Tk-Ku02Gv7-TdAd8qUpc,1359
|
70
71
|
TB2J/magnon/plot.py,sha256=oNavax15jRfW4Sxl6FgwLXSrMntyVz2cgVDbmgkWehw,3193
|
72
|
+
TB2J/magnon/plot_magnon_dos_cli.py,sha256=XVozodrIO_w4FBhp-ZcpIZbyGWLdegUFLdvY5zL3XOI,2592
|
71
73
|
TB2J/magnon/structure.py,sha256=rKefzXgQlEjVvV-A7E2IogVDWwf5egZr3Wxxu6HuJU4,7685
|
72
74
|
TB2J/mathutils/__init__.py,sha256=E70Mx8mLXh3DJGmdN3TLWmGYMcsbvKxmgxUbAI6Mzmo,49
|
73
75
|
TB2J/mathutils/auto_kpath.py,sha256=bDXDyQzRByVpPSOpz7NU7F5hYqS6IX-47lwWoMU89lM,4978
|
@@ -89,21 +91,21 @@ TB2J/spinham/supercell.py,sha256=y17uUC6r3gQb278FhxIW4CABihfLTvKFj6flyXrCPR8,122
|
|
89
91
|
TB2J/wannier/__init__.py,sha256=7ojCbM84PYv1X1Tbo4NHI-d3gWmQsZB_xiYqbfxVV1E,80
|
90
92
|
TB2J/wannier/w90_parser.py,sha256=dbd63LuKyv2DVUzqRINGsbDzEsOxsQyE8_Ear_LQIRg,4620
|
91
93
|
TB2J/wannier/w90_tb_parser.py,sha256=qt8pnuprmPp9iIAYwPkPbmEzk6ZPgMq2xognoQp7vwc,4610
|
92
|
-
tb2j-0.9.9.
|
93
|
-
tb2j-0.9.9.
|
94
|
-
tb2j-0.9.9.
|
95
|
-
tb2j-0.9.9.
|
96
|
-
tb2j-0.9.9.
|
97
|
-
tb2j-0.9.9.
|
98
|
-
tb2j-0.9.9.
|
99
|
-
tb2j-0.9.9.
|
100
|
-
tb2j-0.9.9.
|
101
|
-
tb2j-0.9.9.
|
102
|
-
tb2j-0.9.9.
|
103
|
-
tb2j-0.9.9.
|
104
|
-
tb2j-0.9.9.
|
105
|
-
tb2j-0.9.9.
|
106
|
-
tb2j-0.9.9.
|
107
|
-
tb2j-0.9.9.
|
108
|
-
tb2j-0.9.9.
|
109
|
-
tb2j-0.9.9.
|
94
|
+
tb2j-0.9.9.15.data/scripts/TB2J_downfold.py,sha256=i4BVqnpDdgrX_amookVWeLGefGBn-qeAutWiwuY9SfQ,2099
|
95
|
+
tb2j-0.9.9.15.data/scripts/TB2J_eigen.py,sha256=Qs9v2hnMm2Tpfoa4h53muUKty2dZjwx8948MBoQooNg,1128
|
96
|
+
tb2j-0.9.9.15.data/scripts/TB2J_magnon.py,sha256=q7UwAmorRcFNk4tfE7gl_ny05l6p7pbD9Wm_LkIpKEw,3101
|
97
|
+
tb2j-0.9.9.15.data/scripts/TB2J_magnon2.py,sha256=tMa7Fg_Wd2UytnrH3C_AsgGM7BciUW0iy6NiPlWvar8,1920
|
98
|
+
tb2j-0.9.9.15.data/scripts/TB2J_magnon_dos.py,sha256=TMXQvD2dIbO5FZ4tUMmxJgCgH2O2hDAPUNfEKO4z-x4,110
|
99
|
+
tb2j-0.9.9.15.data/scripts/TB2J_merge.py,sha256=WQVrS2FvrjxkKukiPLY-naQxEK-d2T-bAb5n7e6ml2I,2096
|
100
|
+
tb2j-0.9.9.15.data/scripts/TB2J_plot_magnon_bands.py,sha256=wLlueNI-wU79cmPFu4LmPDwVBMerKSb5sUlTDSPQK6I,874
|
101
|
+
tb2j-0.9.9.15.data/scripts/TB2J_rotate.py,sha256=zgiDFuYZNmzKK0rwDmTaYD2OpRlmKA_VGeBx83w2Xwc,873
|
102
|
+
tb2j-0.9.9.15.data/scripts/TB2J_rotateDM.py,sha256=kCvF7gotuqAX1VnJ06cwfVm7RrhrdtiV5v7d9P2Pn_E,567
|
103
|
+
tb2j-0.9.9.15.data/scripts/abacus2J.py,sha256=ozYI7qZyja1WEs9oCYVpeYAfVj5PGhehhmdZFcvrd3g,1795
|
104
|
+
tb2j-0.9.9.15.data/scripts/siesta2J.py,sha256=QJ6c0DbqxaqYEesxiL5R9nK9-flNLrr7hajKfCwirYc,2318
|
105
|
+
tb2j-0.9.9.15.data/scripts/wann2J.py,sha256=OA31VHEXbQMD-JozoLUHDF6vB9Sr62d804OApSKtSnU,3240
|
106
|
+
tb2j-0.9.9.15.dist-info/licenses/LICENSE,sha256=CbZI-jyRTjiqIcWa244cRSHJdjjtUNqGR4HeJkgEwJw,1332
|
107
|
+
tb2j-0.9.9.15.dist-info/METADATA,sha256=4MA4Id_pqZPASA3ig_9xiICywGLB06rlUT6bPiccdW4,1768
|
108
|
+
tb2j-0.9.9.15.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
109
|
+
tb2j-0.9.9.15.dist-info/entry_points.txt,sha256=S81PATMTaKCIznjd3pei3Kb3q3xOy2dU-x_cAFSCS7k,194
|
110
|
+
tb2j-0.9.9.15.dist-info/top_level.txt,sha256=whYa5ByLYhl5XnTPBHSWr-IGD6VWmr5Ql2bye2qwV_s,5
|
111
|
+
tb2j-0.9.9.15.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
|