TB2J 0.9.9.13__tar.gz → 0.9.9.15__tar.gz
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-0.9.9.13 → tb2j-0.9.9.15}/PKG-INFO +1 -1
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/MAEGreen.py +1 -2
- tb2j-0.9.9.15/TB2J/__init__.py +1 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/exchange.py +1 -1
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/interfaces/abacus/stru_api.py +10 -17
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/interfaces/siesta_interface.py +1 -1
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/io_exchange/io_exchange.py +4 -5
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/io_exchange/io_vampire.py +1 -1
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/io_merge.py +60 -45
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/magnon/magnon3.py +105 -11
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/magnon/magnon_band.py +3 -3
- tb2j-0.9.9.15/TB2J/magnon/magnon_dos.py +308 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/magnon/magnon_math.py +1 -1
- tb2j-0.9.9.15/TB2J/magnon/plot_magnon_dos_cli.py +99 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J.egg-info/PKG-INFO +1 -1
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J.egg-info/SOURCES.txt +2 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J.egg-info/entry_points.txt +1 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/scripts/TB2J_merge.py +9 -5
- tb2j-0.9.9.15/scripts/TB2J_plot_magnon_bands.py +27 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/setup.py +2 -1
- tb2j-0.9.9.13/TB2J/__init__.py +0 -1
- tb2j-0.9.9.13/scripts/TB2J_plot_magnon_bands.py +0 -7
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/LICENSE +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/README.md +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/Jdownfolder.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/Jtensor.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/MAE.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/Oiju.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/Oiju_epc.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/anisotropy.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/basis.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/citation.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/contour.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/density_matrix.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/epc.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/exchangeCL2.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/exchange_params.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/exchange_pert.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/exchange_qspace.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/external/__init__.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/external/p_tqdm.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/gpaw_wrapper.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/green.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/greentest.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/interfaces/__init__.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/interfaces/abacus/__init__.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/interfaces/abacus/abacus_api.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/interfaces/abacus/abacus_wrapper.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/interfaces/abacus/gen_exchange_abacus.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/interfaces/abacus/orbital_api.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/interfaces/abacus/test_density_matrix.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/interfaces/abacus/test_read_HRSR.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/interfaces/abacus/test_read_stru.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/interfaces/gpaw_interface.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/interfaces/lawaf_interface.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/interfaces/manager.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/interfaces/wannier90_interface.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/io_exchange/__init__.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/io_exchange/io_multibinit.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/io_exchange/io_tomsasd.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/io_exchange/io_txt.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/io_exchange/io_uppasd.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/kpoints.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/magnon/__init__.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/magnon/io_exchange2.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/magnon/magnon_io.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/magnon/plot.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/magnon/structure.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/mathutils/__init__.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/mathutils/auto_kpath.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/mathutils/fermi.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/mathutils/fibonacci_sphere.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/mathutils/kR_convert.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/mathutils/lowdin.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/mathutils/rotate_spin.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/myTB.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/mycfr.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/orbital_magmom.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/orbmap.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/pauli.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/pert.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/plot.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/rotate_atoms.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/rotate_siestaDM.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/sisl_wrapper.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/spinham/__init__.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/spinham/base_parser.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/spinham/constants.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/spinham/hamiltonian.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/spinham/hamiltonian_terms.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/spinham/plot.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/spinham/qsolver.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/spinham/spin_api.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/spinham/spin_xml.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/spinham/supercell.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/symmetrize_J.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/tensor_rotate.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/thetaphi.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/utest.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/utils.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/versioninfo.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/wannier/__init__.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/wannier/w90_parser.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J/wannier/w90_tb_parser.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J.egg-info/dependency_links.txt +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J.egg-info/requires.txt +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/TB2J.egg-info/top_level.txt +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/scripts/TB2J_downfold.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/scripts/TB2J_eigen.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/scripts/TB2J_magnon.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/scripts/TB2J_magnon2.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/scripts/TB2J_magnon_dos.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/scripts/TB2J_rotate.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/scripts/TB2J_rotateDM.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/scripts/abacus2J.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/scripts/siesta2J.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/scripts/wann2J.py +0 -0
- {tb2j-0.9.9.13 → tb2j-0.9.9.15}/setup.cfg +0 -0
@@ -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
|
@@ -0,0 +1 @@
|
|
1
|
+
__version__ = "0.9.9.14"
|
@@ -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."
|
@@ -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:
|
@@ -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()
|
@@ -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
|
@@ -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,
|