TB2J 0.9.5rc0__py3-none-any.whl → 0.9.7rc0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- TB2J/MAE.py +144 -27
- TB2J/MAEGreen.py +84 -0
- TB2J/anisotropy.py +672 -0
- TB2J/contour.py +8 -0
- TB2J/exchange.py +38 -172
- TB2J/exchangeCL2.py +11 -13
- TB2J/exchange_params.py +213 -0
- TB2J/green.py +62 -27
- TB2J/interfaces/__init__.py +12 -0
- TB2J/interfaces/abacus/__init__.py +4 -0
- TB2J/{abacus → interfaces/abacus}/abacus_api.py +3 -3
- TB2J/{abacus → interfaces/abacus}/abacus_wrapper.py +11 -8
- TB2J/{abacus → interfaces/abacus}/gen_exchange_abacus.py +5 -3
- TB2J/{abacus → interfaces/abacus}/orbital_api.py +4 -4
- TB2J/{abacus → interfaces/abacus}/stru_api.py +11 -11
- TB2J/{abacus → interfaces/abacus}/test_read_HRSR.py +0 -1
- TB2J/{abacus → interfaces/abacus}/test_read_stru.py +5 -3
- TB2J/interfaces/gpaw_interface.py +54 -0
- TB2J/interfaces/lawaf_interface.py +129 -0
- TB2J/interfaces/manager.py +23 -0
- TB2J/interfaces/siesta_interface.py +202 -0
- TB2J/interfaces/wannier90_interface.py +115 -0
- TB2J/io_exchange/io_exchange.py +21 -7
- TB2J/io_merge.py +2 -1
- TB2J/mathutils/fermi.py +11 -6
- TB2J/mathutils/lowdin.py +12 -2
- TB2J/mathutils/rotate_spin.py +222 -4
- TB2J/pauli.py +11 -3
- TB2J/symmetrize_J.py +120 -0
- TB2J/utils.py +82 -1
- {TB2J-0.9.5rc0.data → TB2J-0.9.7rc0.data}/scripts/abacus2J.py +5 -4
- {TB2J-0.9.5rc0.data → TB2J-0.9.7rc0.data}/scripts/siesta2J.py +21 -4
- TB2J-0.9.7rc0.data/scripts/wann2J.py +96 -0
- {TB2J-0.9.5rc0.dist-info → TB2J-0.9.7rc0.dist-info}/METADATA +5 -3
- {TB2J-0.9.5rc0.dist-info → TB2J-0.9.7rc0.dist-info}/RECORD +47 -46
- {TB2J-0.9.5rc0.dist-info → TB2J-0.9.7rc0.dist-info}/WHEEL +1 -1
- TB2J-0.9.7rc0.dist-info/entry_points.txt +3 -0
- TB2J/abacus/MAE.py +0 -320
- TB2J/abacus/__init__.py +0 -1
- TB2J/abacus/occupations.py +0 -278
- TB2J/cut_cell.py +0 -82
- TB2J/io_exchange/io_pickle.py +0 -0
- TB2J/manager.py +0 -445
- TB2J/mathutils.py +0 -12
- TB2J/patch.py +0 -50
- TB2J/spinham/h_matrix.py +0 -68
- TB2J/spinham/obtain_J.py +0 -79
- TB2J/supercell.py +0 -532
- TB2J-0.9.5rc0.data/scripts/wann2J.py +0 -194
- TB2J/{abacus → interfaces/abacus}/test_density_matrix.py +1 -1
- {TB2J-0.9.5rc0.data → TB2J-0.9.7rc0.data}/scripts/TB2J_downfold.py +0 -0
- {TB2J-0.9.5rc0.data → TB2J-0.9.7rc0.data}/scripts/TB2J_eigen.py +0 -0
- {TB2J-0.9.5rc0.data → TB2J-0.9.7rc0.data}/scripts/TB2J_magnon.py +0 -0
- {TB2J-0.9.5rc0.data → TB2J-0.9.7rc0.data}/scripts/TB2J_magnon_dos.py +0 -0
- {TB2J-0.9.5rc0.data → TB2J-0.9.7rc0.data}/scripts/TB2J_merge.py +0 -0
- {TB2J-0.9.5rc0.data → TB2J-0.9.7rc0.data}/scripts/TB2J_rotate.py +0 -0
- {TB2J-0.9.5rc0.data → TB2J-0.9.7rc0.data}/scripts/TB2J_rotateDM.py +0 -0
- {TB2J-0.9.5rc0.dist-info → TB2J-0.9.7rc0.dist-info}/LICENSE +0 -0
- {TB2J-0.9.5rc0.dist-info → TB2J-0.9.7rc0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,202 @@
|
|
1
|
+
import os
|
2
|
+
|
3
|
+
import numpy as np
|
4
|
+
|
5
|
+
from TB2J.exchange import ExchangeNCL
|
6
|
+
from TB2J.exchangeCL2 import ExchangeCL2
|
7
|
+
from TB2J.MAEGreen import MAEGreen
|
8
|
+
|
9
|
+
try:
|
10
|
+
from HamiltonIO.siesta import SislParser
|
11
|
+
except ImportError:
|
12
|
+
print(
|
13
|
+
"Cannot import SislWrapper from HamiltonIO.siesta. Please install HamiltonIO first."
|
14
|
+
)
|
15
|
+
|
16
|
+
|
17
|
+
def siesta_anisotropy(**kwargs):
|
18
|
+
pass
|
19
|
+
|
20
|
+
|
21
|
+
def gen_exchange_siesta(fdf_fname, read_H_soc=False, **kwargs):
|
22
|
+
"""
|
23
|
+
parameters:
|
24
|
+
fdf_fname: str
|
25
|
+
The fdf file for the calculation.
|
26
|
+
read_H_soc: bool
|
27
|
+
Whether to read the SOC Hamiltonian. Default is False.
|
28
|
+
|
29
|
+
parameters in **kwargs:
|
30
|
+
magnetic_elements: list of str
|
31
|
+
The magnetic elements to calculate the exchange. e.g. ["Fe", "Co", "Ni"]
|
32
|
+
include_orbs: dict
|
33
|
+
The included orbitals for each magnetic element. e.g. {"Fe": ["d"]}. Default is None.
|
34
|
+
kmesh: list of int
|
35
|
+
The k-point mesh for the calculation. e.g. [5, 5, 5]. Default is [5, 5, 5].
|
36
|
+
emin: float
|
37
|
+
The minimum energy for the calculation. Default is -12.0.
|
38
|
+
emax: float
|
39
|
+
The maximum energy for the calculation. Default is 0.0.
|
40
|
+
nz: int
|
41
|
+
The number of points for the energy mesh. Default is 100.
|
42
|
+
exclude_orbs: list of str
|
43
|
+
The excluded orbitals for the calculation. Default is [].
|
44
|
+
Rcut: float
|
45
|
+
The cutoff radius for the exchange calculation in angstrom. Default is None.
|
46
|
+
ne: int
|
47
|
+
The number of electrons for the calculation. Default is None.
|
48
|
+
nproc: int
|
49
|
+
The number of processors for the calculation. Default is 1.
|
50
|
+
use_cache: bool
|
51
|
+
Whether to use the cache for the calculation. Default is False.
|
52
|
+
output_path: str
|
53
|
+
The output path for the calculation. Default is "TB2J_results".
|
54
|
+
orb_decomposition: bool
|
55
|
+
Whether to calculate the orbital decomposition. Default is False.
|
56
|
+
orth: bool
|
57
|
+
Whether to orthogonalize the orbitals. Default is False.
|
58
|
+
description: str
|
59
|
+
The description for the calculation. Default is "".
|
60
|
+
"""
|
61
|
+
|
62
|
+
exargs = dict(
|
63
|
+
magnetic_elements=[],
|
64
|
+
include_orbs=None,
|
65
|
+
kmesh=[5, 5, 5],
|
66
|
+
emin=-12.0,
|
67
|
+
emax=0.0,
|
68
|
+
nz=100,
|
69
|
+
exclude_orbs=[],
|
70
|
+
Rcut=None,
|
71
|
+
ne=None,
|
72
|
+
nproc=1,
|
73
|
+
use_cache=False,
|
74
|
+
output_path="TB2J_results",
|
75
|
+
orb_decomposition=False,
|
76
|
+
orth=False,
|
77
|
+
description="",
|
78
|
+
)
|
79
|
+
exargs.update(kwargs)
|
80
|
+
try:
|
81
|
+
import sisl
|
82
|
+
except ImportError:
|
83
|
+
raise ImportError("sisl cannot be imported. Please install sisl first.")
|
84
|
+
|
85
|
+
from packaging import version
|
86
|
+
|
87
|
+
if version.parse(sisl.__version__) <= version.parse("0.10.0"):
|
88
|
+
raise ImportError(
|
89
|
+
f"sisl version is {sisl.__version__}, but should be larger than 0.10.0."
|
90
|
+
)
|
91
|
+
|
92
|
+
magnetic_elements = exargs.pop("magnetic_elements")
|
93
|
+
include_orbs = exargs.pop("include_orbs")
|
94
|
+
if isinstance(magnetic_elements, str):
|
95
|
+
magnetic_elements = [magnetic_elements]
|
96
|
+
for element in magnetic_elements:
|
97
|
+
if "_" in element:
|
98
|
+
elem = element.split("_")[0]
|
99
|
+
orb = element.split("_")[1:]
|
100
|
+
include_orbs[elem] = orb
|
101
|
+
else:
|
102
|
+
include_orbs[element] = None
|
103
|
+
magnetic_elements = list(include_orbs.keys())
|
104
|
+
output_path = exargs.pop("output_path")
|
105
|
+
|
106
|
+
parser = SislParser(
|
107
|
+
fdf_fname=fdf_fname, ispin=None, read_H_soc=read_H_soc, orth=exargs["orth"]
|
108
|
+
)
|
109
|
+
if parser.spin.is_colinear:
|
110
|
+
print("Reading Siesta hamiltonian: colinear spin.")
|
111
|
+
# tbmodel_up = SislWrapper(fdf_fname=None, sisl_hamiltonian=H, spin=0, geom=geom)
|
112
|
+
# tbmodel_dn = SislWrapper(fdf_fname=None, sisl_hamiltonian=H, spin=1, geom=geom)
|
113
|
+
tbmodel_up, tbmodel_dn = parser.get_model()
|
114
|
+
basis = dict(zip(tbmodel_up.orbs, list(range(tbmodel_up.norb))))
|
115
|
+
print("Starting to calculate exchange.")
|
116
|
+
description = f""" Input from collinear Siesta data.
|
117
|
+
working directory: {os.getcwd()}
|
118
|
+
fdf_fname: {fdf_fname}.
|
119
|
+
\n"""
|
120
|
+
exchange = ExchangeCL2(
|
121
|
+
tbmodels=(tbmodel_up, tbmodel_dn),
|
122
|
+
atoms=tbmodel_up.atoms,
|
123
|
+
basis=basis,
|
124
|
+
efermi=0.0,
|
125
|
+
magnetic_elements=magnetic_elements,
|
126
|
+
include_orbs=include_orbs,
|
127
|
+
**exargs,
|
128
|
+
)
|
129
|
+
exchange.run(path=output_path)
|
130
|
+
print("\n")
|
131
|
+
print(f"All calculation finished. The results are in {output_path} directory.")
|
132
|
+
|
133
|
+
elif parser.spin.is_spinorbit or parser.spin.is_noncolinear:
|
134
|
+
print("Reading Siesta hamiltonian: non-colinear spin.")
|
135
|
+
model = parser.get_model()
|
136
|
+
basis = dict(zip(model.orbs, list(range(model.nbasis))))
|
137
|
+
print("Starting to calculate exchange.")
|
138
|
+
description = f""" Input from non-collinear Siesta data.
|
139
|
+
working directory: {os.getcwd()}
|
140
|
+
fdf_fname: {fdf_fname}.
|
141
|
+
Warning: The DMI component parallel to the spin orientation, the Jani which has the component of that orientation should be disregarded
|
142
|
+
e.g. if the spins are along z, the xz, yz, zz, zx, zy components and the z component of DMI.
|
143
|
+
If you need these component, try to do three calculations with spin along x, y, z, or use structure with z rotated to x, y and z. And then use TB2J_merge.py to get the full set of parameters.
|
144
|
+
\n"""
|
145
|
+
exargs["description"] = description
|
146
|
+
if not model.split_soc:
|
147
|
+
exchange = ExchangeNCL(
|
148
|
+
tbmodels=model,
|
149
|
+
atoms=model.atoms,
|
150
|
+
basis=basis,
|
151
|
+
efermi=0.0,
|
152
|
+
magnetic_elements=magnetic_elements,
|
153
|
+
include_orbs=include_orbs,
|
154
|
+
output_path=output_path,
|
155
|
+
**exargs,
|
156
|
+
)
|
157
|
+
exchange.run(path=output_path)
|
158
|
+
print("\n")
|
159
|
+
print(
|
160
|
+
f"All calculation finished. The results are in {output_path} directory."
|
161
|
+
)
|
162
|
+
else:
|
163
|
+
print("Starting to calculate MAE.")
|
164
|
+
model.set_so_strength(0.0)
|
165
|
+
MAE = MAEGreen(
|
166
|
+
tbmodels=model,
|
167
|
+
atoms=model.atoms,
|
168
|
+
basis=basis,
|
169
|
+
efermi=None,
|
170
|
+
magnetic_elements=magnetic_elements,
|
171
|
+
include_orbs=include_orbs,
|
172
|
+
**exargs,
|
173
|
+
)
|
174
|
+
# thetas = [0, np.pi / 2, np.pi, 3 * np.pi / 2]
|
175
|
+
# phis = [0, 0, 0, 0]
|
176
|
+
# MAE.set_angles(thetas=thetas, phis=phis)
|
177
|
+
MAE.run(output_path=output_path)
|
178
|
+
print(
|
179
|
+
f"MAE calculation finished. The results are in {output_path} directory."
|
180
|
+
)
|
181
|
+
|
182
|
+
angle = {"x": (np.pi / 2, 0), "y": (np.pi / 2, np.pi / 2), "z": (0, 0)}
|
183
|
+
for key, val in angle.items():
|
184
|
+
# model = parser.get_model()
|
185
|
+
theta, phi = val
|
186
|
+
model.set_Hsoc_rotation_angle([theta, phi])
|
187
|
+
basis = dict(zip(model.orbs, list(range(model.nbasis))))
|
188
|
+
output_path_full = f"{output_path}_{key}"
|
189
|
+
exchange = ExchangeNCL(
|
190
|
+
tbmodels=model,
|
191
|
+
atoms=model.atoms,
|
192
|
+
basis=basis,
|
193
|
+
efermi=None, # set to None, compute from efermi.
|
194
|
+
magnetic_elements=magnetic_elements,
|
195
|
+
include_orbs=include_orbs,
|
196
|
+
**exargs,
|
197
|
+
)
|
198
|
+
exchange.run(path=output_path_full)
|
199
|
+
print("\n")
|
200
|
+
print(
|
201
|
+
f"All calculation finished. The results are in {output_path_full} directory."
|
202
|
+
)
|
@@ -0,0 +1,115 @@
|
|
1
|
+
import os
|
2
|
+
|
3
|
+
from ase.io import read
|
4
|
+
|
5
|
+
from TB2J.myTB import MyTB
|
6
|
+
from TB2J.utils import auto_assign_basis_name
|
7
|
+
from TB2J.wannier import parse_atoms
|
8
|
+
|
9
|
+
from .manager import Manager
|
10
|
+
|
11
|
+
|
12
|
+
class WannierManager(Manager):
|
13
|
+
def __init__(
|
14
|
+
self,
|
15
|
+
path,
|
16
|
+
prefix_up,
|
17
|
+
prefix_dn,
|
18
|
+
prefix_SOC,
|
19
|
+
colinear=True,
|
20
|
+
groupby="spin",
|
21
|
+
posfile=None,
|
22
|
+
basis_fname=None,
|
23
|
+
qspace=False,
|
24
|
+
wannier_type="wannier90",
|
25
|
+
**kwargs,
|
26
|
+
):
|
27
|
+
# atoms
|
28
|
+
atoms = self.prepare_atoms(path, posfile, prefix_up, prefix_SOC, colinear)
|
29
|
+
output_path = kwargs.get("output_path", "TB2J_results")
|
30
|
+
# models and basis
|
31
|
+
if colinear:
|
32
|
+
tbmodels, basis = self.prepare_model_colinear(
|
33
|
+
path, prefix_up, prefix_dn, atoms, output_path=output_path
|
34
|
+
)
|
35
|
+
else:
|
36
|
+
tbmodels, basis = self.prepare_model_ncl(
|
37
|
+
path, prefix_SOC, atoms, groupby, output_path=output_path
|
38
|
+
)
|
39
|
+
|
40
|
+
description = self.description(path, prefix_up, prefix_dn, prefix_SOC, colinear)
|
41
|
+
kwargs["description"] = description
|
42
|
+
|
43
|
+
super().__init__(atoms, tbmodels, basis, colinear=colinear, **kwargs)
|
44
|
+
|
45
|
+
def prepare_atoms(self, path, posfile, prefix_up, prefix_SOC, colinear=True):
|
46
|
+
fname = os.path.join(path, posfile)
|
47
|
+
try:
|
48
|
+
print(f"Reading atomic structure from file {fname}.")
|
49
|
+
atoms = read(os.path.join(path, posfile))
|
50
|
+
except Exception:
|
51
|
+
print(
|
52
|
+
f"Cannot read atomic structure from file {fname}. Trying to read from Wannier input."
|
53
|
+
)
|
54
|
+
if colinear:
|
55
|
+
fname = os.path.join(path, f"{prefix_up}.win")
|
56
|
+
else:
|
57
|
+
fname = os.path.join(path, f"{prefix_SOC}.win")
|
58
|
+
|
59
|
+
print(f"Reading atomic structure from file {fname}.")
|
60
|
+
atoms = parse_atoms(fname)
|
61
|
+
return atoms
|
62
|
+
|
63
|
+
def prepare_model_colinear(self, path, prefix_up, prefix_dn, atoms, output_path):
|
64
|
+
print("Reading Wannier90 hamiltonian: spin up.")
|
65
|
+
tbmodel_up = MyTB.read_from_wannier_dir(
|
66
|
+
path=path, prefix=prefix_up, atoms=atoms, nls=False
|
67
|
+
)
|
68
|
+
print("Reading Wannier90 hamiltonian: spin down.")
|
69
|
+
tbmodel_dn = MyTB.read_from_wannier_dir(
|
70
|
+
path=path, prefix=prefix_dn, atoms=atoms, nls=False
|
71
|
+
)
|
72
|
+
basis, _ = auto_assign_basis_name(
|
73
|
+
tbmodel_up.xred,
|
74
|
+
atoms,
|
75
|
+
write_basis_file=os.path.join(output_path, "assigned_basis.txt"),
|
76
|
+
)
|
77
|
+
tbmodels = (tbmodel_up, tbmodel_dn)
|
78
|
+
return tbmodels, basis
|
79
|
+
|
80
|
+
def prepare_model_ncl(self, path, prefix_SOC, atoms, groupby, output_path):
|
81
|
+
print("Reading Wannier90 hamiltonian: non-colinear spin.")
|
82
|
+
groupby = groupby.lower().strip()
|
83
|
+
if groupby not in ["spin", "orbital"]:
|
84
|
+
raise ValueError("groupby can only be spin or orbital.")
|
85
|
+
tbmodel = MyTB.read_from_wannier_dir(
|
86
|
+
path=path, prefix=prefix_SOC, atoms=atoms, groupby=groupby, nls=True
|
87
|
+
)
|
88
|
+
basis, _ = auto_assign_basis_name(
|
89
|
+
tbmodel.xred,
|
90
|
+
atoms,
|
91
|
+
write_basis_file=os.path.join(output_path, "assigned_basis.txt"),
|
92
|
+
)
|
93
|
+
return tbmodel, basis
|
94
|
+
|
95
|
+
def description(self, path, prefix_up, prefix_dn, prefix_SOC, colinear=True):
|
96
|
+
if colinear:
|
97
|
+
description = f""" Input from collinear Wannier90 data.
|
98
|
+
Tight binding data from {path}.
|
99
|
+
Prefix of wannier function files:{prefix_up} and {prefix_dn}.
|
100
|
+
Warning: Please check if the noise level of Wannier function Hamiltonian to make sure it is much smaller than the exchange values.
|
101
|
+
\n"""
|
102
|
+
|
103
|
+
else:
|
104
|
+
description = f""" Input from non-collinear Wannier90 data.
|
105
|
+
Tight binding data from {path}.
|
106
|
+
Prefix of wannier function files:{prefix_SOC}.
|
107
|
+
Warning: Please check if the noise level of Wannier function Hamiltonian to make sure it is much smaller than the exchange values.
|
108
|
+
The DMI component parallel to the spin orientation, the Jani which has the component of that orientation should be disregarded
|
109
|
+
e.g. if the spins are along z, the xz, yz, zz, zx, zy components and the z component of DMI.
|
110
|
+
If you need these component, try to do three calculations with spin along x, y, z, or use structure with z rotated to x, y and z. And then use TB2J_merge.py to get the full set of parameters.\n"""
|
111
|
+
|
112
|
+
return description
|
113
|
+
|
114
|
+
|
115
|
+
gen_exchange = WannierManager
|
TB2J/io_exchange/io_exchange.py
CHANGED
@@ -9,16 +9,18 @@ write not only xml output.
|
|
9
9
|
"""
|
10
10
|
|
11
11
|
import os
|
12
|
+
import pickle
|
12
13
|
from collections.abc import Iterable
|
14
|
+
from datetime import datetime
|
15
|
+
|
16
|
+
import matplotlib.pyplot as plt
|
13
17
|
import numpy as np
|
14
|
-
|
15
|
-
import pickle
|
18
|
+
|
16
19
|
from TB2J import __version__
|
20
|
+
from TB2J.io_exchange.io_txt import write_Jq_info
|
17
21
|
from TB2J.Jtensor import combine_J_tensor
|
18
|
-
from
|
19
|
-
import matplotlib.pyplot as plt
|
22
|
+
from TB2J.kpoints import monkhorst_pack
|
20
23
|
from TB2J.spinham.spin_api import SpinModel
|
21
|
-
from TB2J.io_exchange.io_txt import write_Jq_info
|
22
24
|
from TB2J.utils import symbol_number
|
23
25
|
|
24
26
|
|
@@ -238,6 +240,18 @@ Generation time: {now.strftime("%y/%m/%d %H:%M:%S")}
|
|
238
240
|
def get_charge_iatom(self, iatom):
|
239
241
|
return self.charges[iatom]
|
240
242
|
|
243
|
+
def ijR_index_spin_to_atom(self, i, j, R):
|
244
|
+
return (self.iatom(i), self.iatom(j), R)
|
245
|
+
|
246
|
+
def ijR_index_atom_to_spin(self, iatom, jatom, R):
|
247
|
+
return (self.index_spin[iatom], self.index_spin[jatom], R)
|
248
|
+
|
249
|
+
def ijR_list(self):
|
250
|
+
return [(i, j, R) for R, i, j in self.exchange_Jdict]
|
251
|
+
|
252
|
+
def ijR_list_index_atom(self):
|
253
|
+
return [self.ijR_index_spin_to_atom(i, j, R) for R, i, j in self.exchange_Jdict]
|
254
|
+
|
241
255
|
def get_J(self, i, j, R, default=None):
|
242
256
|
i = self.i_spin(i)
|
243
257
|
j = self.i_spin(j)
|
@@ -380,6 +394,7 @@ Generation time: {now.strftime("%y/%m/%d %H:%M:%S")}
|
|
380
394
|
self.write_multibinit(path=os.path.join(path, "Multibinit"))
|
381
395
|
self.write_tom_format(path=os.path.join(path, "TomASD"))
|
382
396
|
self.write_vampire(path=os.path.join(path, "Vampire"))
|
397
|
+
|
383
398
|
self.plot_all(savefile=os.path.join(path, "JvsR.pdf"))
|
384
399
|
# self.write_Jq(kmesh=[9, 9, 9], path=path)
|
385
400
|
|
@@ -562,8 +577,8 @@ def gen_distance_dict(ind_mag_atoms, atoms, Rlist):
|
|
562
577
|
|
563
578
|
|
564
579
|
def test_spin_io():
|
565
|
-
from ase import Atoms
|
566
580
|
import numpy as np
|
581
|
+
from ase import Atoms
|
567
582
|
|
568
583
|
atoms = Atoms(
|
569
584
|
"SrMnO3",
|
@@ -579,7 +594,6 @@ def test_spin_io():
|
|
579
594
|
spinat = [[0, 0, x] for x in [0, 3, 0, 0, 0]]
|
580
595
|
charges = [2, 4, 5, 5, 5]
|
581
596
|
index_spin = [-1, 0, -1, -1, -1]
|
582
|
-
colinear = True
|
583
597
|
Rlist = [[0, 0, 0], [0, 0, 1]]
|
584
598
|
ind_mag_atoms = [1]
|
585
599
|
distance_dict = gen_distance_dict(ind_mag_atoms, atoms, Rlist)
|
TB2J/io_merge.py
CHANGED
@@ -48,7 +48,8 @@ class SpinIO_merge(SpinIO):
|
|
48
48
|
|
49
49
|
def _set_projection_vectors(self):
|
50
50
|
|
51
|
-
|
51
|
+
norm = np.linalg.norm(self.spinat, axis=-1).reshape(-1, 1)
|
52
|
+
spinat = self.spinat / norm
|
52
53
|
idx = [self.ind_atoms[i] for i in self.index_spin if i >= 0]
|
53
54
|
projv = {}
|
54
55
|
for i, j in combinations_with_replacement(range(self.nspin), 2):
|
TB2J/mathutils/fermi.py
CHANGED
@@ -1,8 +1,9 @@
|
|
1
|
-
import numpy as np
|
2
|
-
import warnings
|
3
1
|
import sys
|
4
2
|
|
5
|
-
|
3
|
+
import numpy as np
|
4
|
+
|
5
|
+
MAX_EXP_ARGUMENT = np.log(sys.float_info.max / 100000)
|
6
|
+
print(MAX_EXP_ARGUMENT)
|
6
7
|
|
7
8
|
|
8
9
|
def fermi(e, mu, width=0.01):
|
@@ -15,8 +16,12 @@ def fermi(e, mu, width=0.01):
|
|
15
16
|
"""
|
16
17
|
x = (e - mu) / width
|
17
18
|
# disable overflow warning
|
18
|
-
with warnings.catch_warnings():
|
19
|
-
|
20
|
-
|
19
|
+
# with warnings.catch_warnings():
|
20
|
+
# warnings.simplefilter("ignore")
|
21
|
+
# ret = np.where(x < MAX_EXP_ARGUMENT, 1 / (1.0 + np.exp(x)), 0.0)
|
21
22
|
|
23
|
+
ret = np.zeros_like(x, dtype=float)
|
24
|
+
for i, xi in enumerate(x):
|
25
|
+
if xi < 700:
|
26
|
+
ret[i] = 1 / (1.0 + np.exp(xi))
|
22
27
|
return ret
|
TB2J/mathutils/lowdin.py
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
import numpy as np
|
2
|
-
from scipy.linalg import
|
2
|
+
from scipy.linalg import eigh
|
3
3
|
|
4
4
|
|
5
5
|
def Lowdin(S):
|
@@ -9,4 +9,14 @@ def Lowdin(S):
|
|
9
9
|
psi_prime = S^(-1/2) psi
|
10
10
|
"""
|
11
11
|
eigval, eigvec = eigh(S)
|
12
|
-
|
12
|
+
S_half = eigvec @ np.diag(np.sqrt(1.0 / eigval)) @ (eigvec.T.conj())
|
13
|
+
return S_half
|
14
|
+
|
15
|
+
|
16
|
+
def Lowdin_symmetric_orthonormalization(H, S):
|
17
|
+
"""
|
18
|
+
Lowdin's symmetric orthonormalization.
|
19
|
+
"""
|
20
|
+
S_half = Lowdin(S)
|
21
|
+
H_prime = S_half @ H @ S_half
|
22
|
+
return H_prime
|
TB2J/mathutils/rotate_spin.py
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
import numpy as np
|
2
|
-
from
|
2
|
+
from scipy.sparse import eye_array, kron
|
3
|
+
|
4
|
+
from TB2J.pauli import gather_pauli_blocks, pauli_block_all
|
3
5
|
|
4
6
|
|
5
7
|
def rotate_Matrix_from_z_to_axis(M, axis, normalize=True):
|
@@ -27,12 +29,224 @@ def spherical_to_cartesian(theta, phi, normalize=True):
|
|
27
29
|
return vec
|
28
30
|
|
29
31
|
|
32
|
+
def rotation_matrix(theta, phi):
|
33
|
+
"""
|
34
|
+
The unitray operator U, that U^dagger * s3 * U is the rotated s3 by theta and phi
|
35
|
+
"""
|
36
|
+
U = np.array(
|
37
|
+
[
|
38
|
+
[np.cos(theta / 2), np.exp(-1j * phi) * np.sin(theta / 2)],
|
39
|
+
[-np.exp(1j * phi) * np.sin(theta / 2), np.cos(theta / 2)],
|
40
|
+
]
|
41
|
+
)
|
42
|
+
return U
|
43
|
+
|
44
|
+
|
45
|
+
def rotate_spinor_single_block(M, theta, phi):
|
46
|
+
"""
|
47
|
+
Rotate the spinor matrix M by theta and phi
|
48
|
+
"""
|
49
|
+
U = rotation_matrix(theta, phi)
|
50
|
+
Uinv = np.linalg.inv(U)
|
51
|
+
return Uinv @ M @ U
|
52
|
+
|
53
|
+
|
54
|
+
def rotate_spinor_matrix(M, theta, phi, method="einsum"):
|
55
|
+
"""
|
56
|
+
Rotate the spinor matrix M by theta and phi,
|
57
|
+
"""
|
58
|
+
if method == "plain":
|
59
|
+
return rotate_spinor_matrix_plain(M, theta, phi)
|
60
|
+
elif method == "einsum":
|
61
|
+
return rotate_spinor_matrix_einsum(M, theta, phi)
|
62
|
+
elif method == "reshape":
|
63
|
+
return rotate_spinor_matrix_reshape(M, theta, phi)
|
64
|
+
elif method == "kron":
|
65
|
+
return rotate_spinor_matrix_kron(M, theta, phi)
|
66
|
+
elif method == "spkron":
|
67
|
+
return rotate_spinor_matrix_spkron(M, theta, phi)
|
68
|
+
else:
|
69
|
+
raise ValueError(f"Unknown method: {method}")
|
70
|
+
|
71
|
+
|
72
|
+
def rotate_spinor_matrix_plain(M, theta, phi):
|
73
|
+
"""
|
74
|
+
M is a matrix with shape (2N, 2N), where N is the number of sites, and each site has a 2x2 matrix
|
75
|
+
rotate each 2x2 block by theta and phi
|
76
|
+
"""
|
77
|
+
Mnew = np.zeros_like(M)
|
78
|
+
U = rotation_matrix(theta, phi)
|
79
|
+
UT = U.conj().T
|
80
|
+
tmp = np.zeros((2, 2), dtype=np.complex128)
|
81
|
+
for i in range(M.shape[0] // 2):
|
82
|
+
for j in range(M.shape[0] // 2):
|
83
|
+
for k in range(2):
|
84
|
+
for l in range(2):
|
85
|
+
tmp[k, l] = M[2 * i + k, 2 * j + l]
|
86
|
+
# tmp[:, :]=M[2*i:2*i+2, 2*j:2*j+2]
|
87
|
+
Mnew[2 * i : 2 * i + 2, 2 * j : 2 * j + 2] = UT @ tmp @ U
|
88
|
+
return Mnew
|
89
|
+
|
90
|
+
|
91
|
+
def rotate_spinor_matrix_einsum(M, theta, phi):
|
92
|
+
"""
|
93
|
+
Rotate the spinor matrix M by theta and phi,
|
94
|
+
"""
|
95
|
+
N = M.shape[0] // 2
|
96
|
+
Mnew = np.reshape(M, (N, 2, N, 2)) # .swapaxes(1, 2)
|
97
|
+
# print("Mnew:", Mnew)
|
98
|
+
U = rotation_matrix(theta, phi)
|
99
|
+
UT = U.conj().T
|
100
|
+
Mnew = np.einsum(
|
101
|
+
"ij, rjsk, kl -> risl", UT, Mnew, U, optimize=True, dtype=np.complex128
|
102
|
+
)
|
103
|
+
Mnew = Mnew.reshape(2 * N, 2 * N)
|
104
|
+
return Mnew
|
105
|
+
|
106
|
+
|
107
|
+
def rotate_spinor_matrix_einsum_R(M, theta, phi):
|
108
|
+
"""
|
109
|
+
Rotate the spinor matrix M by theta and phi,
|
110
|
+
"""
|
111
|
+
nR = M.shape[0]
|
112
|
+
N = M.shape[1] // 2
|
113
|
+
Mnew = np.reshape(M, (nR, N, 2, N, 2)) # .swapaxes(1, 2)
|
114
|
+
# print("Mnew:", Mnew)
|
115
|
+
U = rotation_matrix(theta, phi)
|
116
|
+
UT = U.conj().T
|
117
|
+
Mnew = np.einsum(
|
118
|
+
"ij, nrjsk, kl -> nrisl", UT, Mnew, U, optimize=True, dtype=np.complex128
|
119
|
+
)
|
120
|
+
Mnew = Mnew.reshape(nR, 2 * N, 2 * N)
|
121
|
+
return Mnew
|
122
|
+
|
123
|
+
|
124
|
+
def rotate_spinor_Matrix_R(M, theta, phi):
|
125
|
+
return rotate_spinor_matrix_einsum_R(M, theta, phi)
|
126
|
+
|
127
|
+
|
128
|
+
def rotate_spinor_matrix_reshape(M, theta, phi):
|
129
|
+
"""
|
130
|
+
Rotate the spinor matrix M by theta and phi,
|
131
|
+
"""
|
132
|
+
N = M.shape[0] // 2
|
133
|
+
Mnew = np.reshape(M, (N, 2, N, 2)).swapaxes(1, 2)
|
134
|
+
# print("Mnew:", Mnew)
|
135
|
+
U = rotation_matrix(theta, phi)
|
136
|
+
UT = U.conj().T
|
137
|
+
Mnew = UT @ Mnew @ U
|
138
|
+
Mnew = Mnew.swapaxes(1, 2).reshape(2 * N, 2 * N)
|
139
|
+
return Mnew
|
140
|
+
|
141
|
+
|
142
|
+
def rotate_spinor_matrix_kron(M, theta, phi):
|
143
|
+
""" """
|
144
|
+
U = rotation_matrix(theta, phi)
|
145
|
+
# U = np.kron( U, np.eye(M.shape[0]//2))
|
146
|
+
# U = kron(eye_array(M.shape[0]//2), U)
|
147
|
+
U = np.kron(np.eye(M.shape[0] // 2), U)
|
148
|
+
M = U.conj().T @ M @ U
|
149
|
+
return M
|
150
|
+
|
151
|
+
|
152
|
+
def rotate_spinor_matrix_spkron(M, theta, phi):
|
153
|
+
""" """
|
154
|
+
U = rotation_matrix(theta, phi)
|
155
|
+
# U = np.kron( U, np.eye(M.shape[0]//2))
|
156
|
+
U = kron(eye_array(M.shape[0] // 2), U)
|
157
|
+
# U = np.kron(np.eye(M.shape[0]//2), U)
|
158
|
+
M = U.conj().T @ M @ U
|
159
|
+
return M
|
160
|
+
|
161
|
+
|
162
|
+
def test_rotate_spinor_M():
|
163
|
+
N = 2
|
164
|
+
M_re = np.random.rand(N * 2, N * 2)
|
165
|
+
M_im = np.random.rand(N * 2, N * 2)
|
166
|
+
M = M_re + 1j * M_im
|
167
|
+
M = M + M.T.conj()
|
168
|
+
# M=np.array([[1, 0], [0, 1]], dtype=np.complex128)
|
169
|
+
print(f"Original M: {M}")
|
170
|
+
|
171
|
+
import timeit
|
172
|
+
|
173
|
+
print("Time for rotate_spinor_matrix")
|
174
|
+
print(
|
175
|
+
timeit.timeit(lambda: rotate_spinor_matrix(M, np.pi / 2, np.pi / 2), number=10)
|
176
|
+
)
|
177
|
+
print("Time for rotate_spinor_matrix_einsum")
|
178
|
+
print(
|
179
|
+
timeit.timeit(
|
180
|
+
lambda: rotate_spinor_matrix_einsum(M, np.pi / 2, np.pi / 2), number=10
|
181
|
+
)
|
182
|
+
)
|
183
|
+
print("Time for rotate_spinor_matrix_reshape")
|
184
|
+
print(
|
185
|
+
timeit.timeit(
|
186
|
+
lambda: rotate_spinor_matrix_reshape(M, np.pi / 2, np.pi / 2), number=10
|
187
|
+
)
|
188
|
+
)
|
189
|
+
print("Time for rotate_spinor_matrix_kron")
|
190
|
+
print(
|
191
|
+
timeit.timeit(
|
192
|
+
lambda: rotate_spinor_matrix_kron(M, np.pi / 2, np.pi / 2), number=10
|
193
|
+
)
|
194
|
+
)
|
195
|
+
print("Time for rotate_spinor_matrix_spkron")
|
196
|
+
print(
|
197
|
+
timeit.timeit(
|
198
|
+
lambda: rotate_spinor_matrix_spkron(M, np.pi / 2, np.pi / 2), number=10
|
199
|
+
)
|
200
|
+
)
|
201
|
+
|
202
|
+
Mrot1 = rotate_spinor_matrix(M, np.pi / 2, np.pi / 2)
|
203
|
+
Mrot2 = rotate_spinor_matrix_einsum(M, np.pi / 2, np.pi / 2)
|
204
|
+
Mrot3 = rotate_spinor_matrix_reshape(M, np.pi / 2, np.pi / 2)
|
205
|
+
Mrot4 = rotate_spinor_matrix_kron(M, np.pi / 2, np.pi / 2)
|
206
|
+
Mrot5 = rotate_spinor_matrix_spkron(M, np.pi / 2, np.pi / 2)
|
207
|
+
print(f"Rotated M with jit:\n {Mrot1}")
|
208
|
+
print(f"Rotated M with einsum:\n {Mrot2-Mrot1}")
|
209
|
+
print(f"Rotated M with reshape:\n {Mrot3-Mrot1}")
|
210
|
+
print(f"Rotated M with kron:\n {Mrot4-Mrot1}")
|
211
|
+
print(f"Rotated M with spkron:\n {Mrot5-Mrot1}")
|
212
|
+
|
213
|
+
M_rot00 = rotate_spinor_matrix(M, 0, 0)
|
214
|
+
M_rot00_sph = rotate_Matrix_from_z_to_spherical(M, 0, 0)
|
215
|
+
print(f"Rotated M with theta=0, phi=0 compared with M:\n {M_rot00-M}")
|
216
|
+
print(f"Rotated M with theta=0, phi=0 compared with M:\n {M_rot00_sph-M}")
|
217
|
+
|
218
|
+
|
219
|
+
def test_rotate_spinor_oneblock():
|
220
|
+
M = np.array([[1.1, 0], [0, 0.9]])
|
221
|
+
print(np.array(pauli_block_all(M)).ravel())
|
222
|
+
print("Rotate by pi/2, pi/2 (z to y)")
|
223
|
+
Mnew = rotate_spinor_matrix_einsum(M, np.pi / 2, np.pi / 2)
|
224
|
+
print(np.array(pauli_block_all(Mnew)).ravel())
|
225
|
+
|
226
|
+
print("Rotate by pi/2, 0 (z to x)")
|
227
|
+
Mnew = rotate_spinor_matrix_kron(M, np.pi / 2, 0)
|
228
|
+
|
229
|
+
print(np.array(pauli_block_all(Mnew)).ravel())
|
230
|
+
|
231
|
+
print(Mnew)
|
232
|
+
|
233
|
+
|
30
234
|
def rotate_Matrix_from_z_to_spherical(M, theta, phi, normalize=True):
|
31
235
|
"""
|
32
236
|
Given a spinor matrix M, rotate it from z-axis to spherical coordinates
|
33
237
|
"""
|
34
|
-
axis = spherical_to_cartesian(theta, phi, normalize)
|
35
|
-
return rotate_Matrix_from_z_to_axis(M, axis, normalize)
|
238
|
+
# axis = spherical_to_cartesian(theta, phi, normalize)
|
239
|
+
# return rotate_Matrix_from_z_to_axis(M, axis, normalize)
|
240
|
+
return rotate_spinor_matrix_einsum(M, theta, phi)
|
241
|
+
|
242
|
+
|
243
|
+
def test_rotate_Matrix_from_z_to_spherical():
|
244
|
+
M_re = np.random.rand(2, 2)
|
245
|
+
M_im = np.random.rand(2, 2)
|
246
|
+
M = M_re + 1j * M_im
|
247
|
+
print(M)
|
248
|
+
M_rot = rotate_Matrix_from_z_to_spherical(M, 0, 0)
|
249
|
+
print(M_rot)
|
36
250
|
|
37
251
|
|
38
252
|
def test_rotate_Matrix_from_z_to_axis():
|
@@ -53,4 +267,8 @@ def test_rotate_Matrix_from_z_to_axis():
|
|
53
267
|
|
54
268
|
|
55
269
|
if __name__ == "__main__":
|
56
|
-
test_rotate_Matrix_from_z_to_axis()
|
270
|
+
# test_rotate_Matrix_from_z_to_axis()
|
271
|
+
# test_rotate_Matrix_from_z_to_spherical()
|
272
|
+
test_rotate_spinor_M()
|
273
|
+
# test_rotate_spinor_oneblock()
|
274
|
+
pass
|