TB2J 0.9.0.1__tar.gz → 0.9.1rc0__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.0.1 → tb2j-0.9.1rc0}/PKG-INFO +1 -1
- tb2j-0.9.1rc0/TB2J/abacus/MAE.py +320 -0
- {TB2J-0.9.0.1 → tb2j-0.9.1rc0}/TB2J/abacus/abacus_wrapper.py +20 -2
- tb2j-0.9.1rc0/TB2J/abacus/occupations.py +278 -0
- tb2j-0.9.1rc0/TB2J/abacus/test_density_matrix.py +38 -0
- {TB2J-0.9.0.1 → tb2j-0.9.1rc0}/TB2J/green.py +2 -13
- tb2j-0.9.1rc0/TB2J/mathutils/__init__.py +1 -0
- tb2j-0.9.1rc0/TB2J/mathutils/fermi.py +22 -0
- tb2j-0.9.1rc0/TB2J/mathutils/kR_convert.py +90 -0
- tb2j-0.9.1rc0/TB2J/mathutils/lowdin.py +12 -0
- tb2j-0.9.1rc0/TB2J/mathutils/rotate_spin.py +35 -0
- {TB2J-0.9.0.1 → tb2j-0.9.1rc0}/TB2J/pauli.py +17 -0
- {TB2J-0.9.0.1 → tb2j-0.9.1rc0}/TB2J.egg-info/PKG-INFO +1 -1
- {TB2J-0.9.0.1 → tb2j-0.9.1rc0}/TB2J.egg-info/SOURCES.txt +8 -0
- {TB2J-0.9.0.1 → tb2j-0.9.1rc0}/setup.py +1 -1
- {TB2J-0.9.0.1 → tb2j-0.9.1rc0}/LICENSE +0 -0
- {TB2J-0.9.0.1 → tb2j-0.9.1rc0}/README.md +0 -0
- {TB2J-0.9.0.1 → tb2j-0.9.1rc0}/TB2J/Jdownfolder.py +0 -0
- {TB2J-0.9.0.1 → tb2j-0.9.1rc0}/TB2J/Jtensor.py +0 -0
- {TB2J-0.9.0.1 → tb2j-0.9.1rc0}/TB2J/Oiju.py +0 -0
- {TB2J-0.9.0.1 → tb2j-0.9.1rc0}/TB2J/Oiju_epc.py +0 -0
- {TB2J-0.9.0.1 → tb2j-0.9.1rc0}/TB2J/__init__.py +0 -0
- {TB2J-0.9.0.1 → tb2j-0.9.1rc0}/TB2J/abacus/__init__.py +0 -0
- {TB2J-0.9.0.1 → tb2j-0.9.1rc0}/TB2J/abacus/abacus_api.py +0 -0
- {TB2J-0.9.0.1 → tb2j-0.9.1rc0}/TB2J/abacus/gen_exchange_abacus.py +0 -0
- {TB2J-0.9.0.1 → tb2j-0.9.1rc0}/TB2J/abacus/orbital_api.py +0 -0
- {TB2J-0.9.0.1 → tb2j-0.9.1rc0}/TB2J/abacus/stru_api.py +0 -0
- {TB2J-0.9.0.1 → tb2j-0.9.1rc0}/TB2J/abacus/test_read_HRSR.py +0 -0
- {TB2J-0.9.0.1 → tb2j-0.9.1rc0}/TB2J/abacus/test_read_stru.py +0 -0
- {TB2J-0.9.0.1 → tb2j-0.9.1rc0}/TB2J/basis.py +0 -0
- {TB2J-0.9.0.1 → tb2j-0.9.1rc0}/TB2J/citation.py +0 -0
- {TB2J-0.9.0.1 → tb2j-0.9.1rc0}/TB2J/contour.py +0 -0
- {TB2J-0.9.0.1 → tb2j-0.9.1rc0}/TB2J/density_matrix.py +0 -0
- {TB2J-0.9.0.1 → tb2j-0.9.1rc0}/TB2J/epc.py +0 -0
- {TB2J-0.9.0.1 → tb2j-0.9.1rc0}/TB2J/exchange.py +0 -0
- {TB2J-0.9.0.1 → tb2j-0.9.1rc0}/TB2J/exchangeCL2.py +0 -0
- {TB2J-0.9.0.1 → tb2j-0.9.1rc0}/TB2J/exchange_pert.py +0 -0
- {TB2J-0.9.0.1 → tb2j-0.9.1rc0}/TB2J/exchange_qspace.py +0 -0
- {TB2J-0.9.0.1 → tb2j-0.9.1rc0}/TB2J/external/__init__.py +0 -0
- {TB2J-0.9.0.1 → tb2j-0.9.1rc0}/TB2J/external/p_tqdm.py +0 -0
- {TB2J-0.9.0.1 → tb2j-0.9.1rc0}/TB2J/gpaw_wrapper.py +0 -0
- {TB2J-0.9.0.1 → tb2j-0.9.1rc0}/TB2J/greentest.py +0 -0
- {TB2J-0.9.0.1 → tb2j-0.9.1rc0}/TB2J/io_exchange/__init__.py +0 -0
- {TB2J-0.9.0.1 → tb2j-0.9.1rc0}/TB2J/io_exchange/io_exchange.py +0 -0
- {TB2J-0.9.0.1 → tb2j-0.9.1rc0}/TB2J/io_exchange/io_multibinit.py +0 -0
- {TB2J-0.9.0.1 → tb2j-0.9.1rc0}/TB2J/io_exchange/io_tomsasd.py +0 -0
- {TB2J-0.9.0.1 → tb2j-0.9.1rc0}/TB2J/io_exchange/io_txt.py +0 -0
- {TB2J-0.9.0.1 → tb2j-0.9.1rc0}/TB2J/io_exchange/io_uppasd.py +0 -0
- {TB2J-0.9.0.1 → tb2j-0.9.1rc0}/TB2J/io_exchange/io_vampire.py +0 -0
- {TB2J-0.9.0.1 → tb2j-0.9.1rc0}/TB2J/io_merge.py +0 -0
- {TB2J-0.9.0.1 → tb2j-0.9.1rc0}/TB2J/kpoints.py +0 -0
- {TB2J-0.9.0.1 → tb2j-0.9.1rc0}/TB2J/manager.py +0 -0
- {TB2J-0.9.0.1 → tb2j-0.9.1rc0}/TB2J/mathutils.py +0 -0
- {TB2J-0.9.0.1 → tb2j-0.9.1rc0}/TB2J/myTB.py +0 -0
- {TB2J-0.9.0.1 → tb2j-0.9.1rc0}/TB2J/orbmap.py +0 -0
- {TB2J-0.9.0.1 → tb2j-0.9.1rc0}/TB2J/pert.py +0 -0
- {TB2J-0.9.0.1 → tb2j-0.9.1rc0}/TB2J/plot.py +0 -0
- {TB2J-0.9.0.1 → tb2j-0.9.1rc0}/TB2J/rotate_atoms.py +0 -0
- {TB2J-0.9.0.1 → tb2j-0.9.1rc0}/TB2J/rotate_siestaDM.py +0 -0
- {TB2J-0.9.0.1 → tb2j-0.9.1rc0}/TB2J/sisl_wrapper.py +0 -0
- {TB2J-0.9.0.1 → tb2j-0.9.1rc0}/TB2J/spinham/__init__.py +0 -0
- {TB2J-0.9.0.1 → tb2j-0.9.1rc0}/TB2J/spinham/base_parser.py +0 -0
- {TB2J-0.9.0.1 → tb2j-0.9.1rc0}/TB2J/spinham/constants.py +0 -0
- {TB2J-0.9.0.1 → tb2j-0.9.1rc0}/TB2J/spinham/hamiltonian.py +0 -0
- {TB2J-0.9.0.1 → tb2j-0.9.1rc0}/TB2J/spinham/hamiltonian_terms.py +0 -0
- {TB2J-0.9.0.1 → tb2j-0.9.1rc0}/TB2J/spinham/plot.py +0 -0
- {TB2J-0.9.0.1 → tb2j-0.9.1rc0}/TB2J/spinham/qsolver.py +0 -0
- {TB2J-0.9.0.1 → tb2j-0.9.1rc0}/TB2J/spinham/spin_api.py +0 -0
- {TB2J-0.9.0.1 → tb2j-0.9.1rc0}/TB2J/spinham/spin_xml.py +0 -0
- {TB2J-0.9.0.1 → tb2j-0.9.1rc0}/TB2J/spinham/supercell.py +0 -0
- {TB2J-0.9.0.1 → tb2j-0.9.1rc0}/TB2J/tensor_rotate.py +0 -0
- {TB2J-0.9.0.1 → tb2j-0.9.1rc0}/TB2J/utest.py +0 -0
- {TB2J-0.9.0.1 → tb2j-0.9.1rc0}/TB2J/utils.py +0 -0
- {TB2J-0.9.0.1 → tb2j-0.9.1rc0}/TB2J/versioninfo.py +0 -0
- {TB2J-0.9.0.1 → tb2j-0.9.1rc0}/TB2J/wannier/__init__.py +0 -0
- {TB2J-0.9.0.1 → tb2j-0.9.1rc0}/TB2J/wannier/w90_parser.py +0 -0
- {TB2J-0.9.0.1 → tb2j-0.9.1rc0}/TB2J/wannier/w90_tb_parser.py +0 -0
- {TB2J-0.9.0.1 → tb2j-0.9.1rc0}/TB2J.egg-info/dependency_links.txt +0 -0
- {TB2J-0.9.0.1 → tb2j-0.9.1rc0}/TB2J.egg-info/requires.txt +0 -0
- {TB2J-0.9.0.1 → tb2j-0.9.1rc0}/TB2J.egg-info/top_level.txt +0 -0
- {TB2J-0.9.0.1 → tb2j-0.9.1rc0}/scripts/TB2J_downfold.py +0 -0
- {TB2J-0.9.0.1 → tb2j-0.9.1rc0}/scripts/TB2J_eigen.py +0 -0
- {TB2J-0.9.0.1 → tb2j-0.9.1rc0}/scripts/TB2J_magnon.py +0 -0
- {TB2J-0.9.0.1 → tb2j-0.9.1rc0}/scripts/TB2J_magnon_dos.py +0 -0
- {TB2J-0.9.0.1 → tb2j-0.9.1rc0}/scripts/TB2J_merge.py +0 -0
- {TB2J-0.9.0.1 → tb2j-0.9.1rc0}/scripts/TB2J_rotate.py +0 -0
- {TB2J-0.9.0.1 → tb2j-0.9.1rc0}/scripts/TB2J_rotateDM.py +0 -0
- {TB2J-0.9.0.1 → tb2j-0.9.1rc0}/scripts/abacus2J.py +0 -0
- {TB2J-0.9.0.1 → tb2j-0.9.1rc0}/scripts/siesta2J.py +0 -0
- {TB2J-0.9.0.1 → tb2j-0.9.1rc0}/scripts/wann2J.py +0 -0
- {TB2J-0.9.0.1 → tb2j-0.9.1rc0}/setup.cfg +0 -0
@@ -0,0 +1,320 @@
|
|
1
|
+
import numpy as np
|
2
|
+
from TB2J.abacus.abacus_wrapper import AbacusWrapper, AbacusParser
|
3
|
+
from TB2J.mathutils.rotate_spin import rotate_Matrix_from_z_to_axis
|
4
|
+
from TB2J.kpoints import monkhorst_pack
|
5
|
+
from TB2J.mathutils.fermi import fermi
|
6
|
+
from TB2J.mathutils.kR_convert import R_to_k
|
7
|
+
from scipy.linalg import eigh
|
8
|
+
from copy import deepcopy
|
9
|
+
from scipy.spatial.transform import Rotation
|
10
|
+
import matplotlib.pyplot as plt
|
11
|
+
from pathlib import Path
|
12
|
+
from TB2J.abacus.occupations import Occupations
|
13
|
+
|
14
|
+
# TODO List:
|
15
|
+
# - [x] Add the class AbacusSplitSOCWrapper
|
16
|
+
# - [x] Add the function to rotate the XC part
|
17
|
+
# - [x] Compute the band energy at arbitrary angle
|
18
|
+
|
19
|
+
|
20
|
+
def get_occupation(evals, kweights, nel, width=0.1):
|
21
|
+
occ = Occupations(nel=nel, width=width, wk=kweights, nspin=2)
|
22
|
+
return occ.occupy(evals)
|
23
|
+
|
24
|
+
|
25
|
+
def get_density_matrix(evals=None, evecs=None, kweights=None, nel=None, width=0.1):
|
26
|
+
occ = get_occupation(evals, kweights, nel, width=width)
|
27
|
+
rho = np.einsum("kib, kb, kjb -> kij", evecs, occ, evecs.conj())
|
28
|
+
return rho
|
29
|
+
|
30
|
+
|
31
|
+
def spherical_to_cartesian(theta, phi, normalize=True):
|
32
|
+
"""
|
33
|
+
Convert spherical coordinates to cartesian
|
34
|
+
"""
|
35
|
+
x = np.sin(theta) * np.cos(phi)
|
36
|
+
y = np.sin(theta) * np.sin(phi)
|
37
|
+
z = np.cos(theta)
|
38
|
+
vec = np.array([x, y, z])
|
39
|
+
if normalize:
|
40
|
+
vec = vec / np.linalg.norm(vec)
|
41
|
+
return vec
|
42
|
+
|
43
|
+
|
44
|
+
class AbacusSplitSOCWrapper(AbacusWrapper):
|
45
|
+
"""
|
46
|
+
Abacus wrapper with Hamiltonian split to SOC and non-SOC parts
|
47
|
+
"""
|
48
|
+
|
49
|
+
def __init__(self, *args, **kwargs):
|
50
|
+
HR_soc = kwargs.pop("HR_soc", None)
|
51
|
+
# nbasis = HR_soc.shape[1]
|
52
|
+
# kwargs["nbasis"] = nbasis
|
53
|
+
super().__init__(*args, **kwargs)
|
54
|
+
self._HR_copy = deepcopy(self._HR)
|
55
|
+
self.HR_soc = HR_soc
|
56
|
+
self.soc_lambda = 1.0
|
57
|
+
self.nel = 16
|
58
|
+
self.width = 0.1
|
59
|
+
|
60
|
+
@property
|
61
|
+
def HR(self):
|
62
|
+
return self._HR + self.HR_soc * self.soc_lambda
|
63
|
+
|
64
|
+
def rotate_HR_xc(self, axis):
|
65
|
+
"""
|
66
|
+
Rotate SOC part of Hamiltonian
|
67
|
+
"""
|
68
|
+
for iR, R in enumerate(self.Rlist):
|
69
|
+
self._HR[iR] = rotate_Matrix_from_z_to_axis(self._HR_copy[iR], axis)
|
70
|
+
|
71
|
+
def rotate_Hk_xc(self, axis):
|
72
|
+
"""
|
73
|
+
Rotate SOC part of Hamiltonian
|
74
|
+
"""
|
75
|
+
for ik in range(len(self._Hk)):
|
76
|
+
self._Hk[ik] = rotate_Matrix_from_z_to_axis(self._Hk_copy[ik], axis)
|
77
|
+
|
78
|
+
def get_density_matrix(self, kpts, kweights=None):
|
79
|
+
rho = np.zeros((len(kpts), self.nbasis, self.nbasis), dtype=complex)
|
80
|
+
evals, evecs = self.solve_all(kpts)
|
81
|
+
occ = get_occupation(evals, kweights, self.nel, width=self.width)
|
82
|
+
rho = np.einsum(
|
83
|
+
"kib, kb, kjb -> kij", evecs, occ, evecs.conj()
|
84
|
+
) # should multiply S to the the real DM.
|
85
|
+
return rho
|
86
|
+
|
87
|
+
def rotate_DM(self, rho, axis):
|
88
|
+
"""
|
89
|
+
Rotate the density matrix
|
90
|
+
"""
|
91
|
+
for ik in range(len(rho)):
|
92
|
+
rho[ik] = rotate_Matrix_from_z_to_axis(rho[ik], axis)
|
93
|
+
return rho
|
94
|
+
|
95
|
+
|
96
|
+
class RotateHam:
|
97
|
+
def __init__(self, model, kmesh, gamma=True):
|
98
|
+
self.model = model
|
99
|
+
self.kpts = monkhorst_pack(kmesh, gamma_center=gamma)
|
100
|
+
self.kweights = np.ones(len(self.kpts), dtype=float) / len(self.kpts)
|
101
|
+
|
102
|
+
def get_band_energy2(self):
|
103
|
+
for ik, kpt in enumerate(self.kpts):
|
104
|
+
Hk, Sk = self.model.gen_ham(kpt)
|
105
|
+
evals, evecs = eigh(Hk, Sk)
|
106
|
+
rho = np.einsum(
|
107
|
+
"ib, b, jb -> ij",
|
108
|
+
evecs,
|
109
|
+
fermi(evals, self.model.efermi, width=0.05),
|
110
|
+
evecs.conj(),
|
111
|
+
)
|
112
|
+
eband1 = np.sum(evals * fermi(evals, self.model.efermi, width=0.05))
|
113
|
+
eband2 = np.trace(Hk @ rho)
|
114
|
+
print(eband1, eband2)
|
115
|
+
|
116
|
+
def get_band_energy(self, dm=False):
|
117
|
+
evals, evecs = self.model.solve_all(self.kpts)
|
118
|
+
occ = get_occupation(
|
119
|
+
evals, self.kweights, self.model.nel, width=self.model.width
|
120
|
+
)
|
121
|
+
eband = np.sum(evals * occ * self.kweights[:, np.newaxis])
|
122
|
+
# * fermi(evals, self.model.efermi, width=0.05)
|
123
|
+
if dm:
|
124
|
+
density_matrix = self.model.get_density_matrix(evecs)
|
125
|
+
return eband, density_matrix
|
126
|
+
else:
|
127
|
+
return eband
|
128
|
+
|
129
|
+
def calc_ref(self):
|
130
|
+
# calculate the Hk_ref, Sk_ref, Hk_soc_ref, and rho_ref
|
131
|
+
self.Sk_ref = R_to_k(self.kpts, self.model.Rlist, self.model.SR)
|
132
|
+
self.Hk_xc_ref = R_to_k(self.kpts, self.model.Rlist, self.model._HR_copy)
|
133
|
+
self.Hk_soc_ref = R_to_k(self.kpts, self.model.Rlist, self.model.HR_soc)
|
134
|
+
self.rho_ref = np.zeros(
|
135
|
+
(len(self.kpts), self.model.nbasis, self.model.nbasis), dtype=complex
|
136
|
+
)
|
137
|
+
|
138
|
+
evals = np.zeros((len(self.kpts), self.model.nbasis), dtype=float)
|
139
|
+
evecs = np.zeros(
|
140
|
+
(len(self.kpts), self.model.nbasis, self.model.nbasis), dtype=complex
|
141
|
+
)
|
142
|
+
|
143
|
+
for ik, kpt in enumerate(self.kpts):
|
144
|
+
# evals, evecs = eigh(self.Hk_xc_ref[ik]+self.Hk_soc_ref[ik], self.Sk_ref[ik])
|
145
|
+
evals[ik], evecs[ik] = eigh(self.Hk_xc_ref[ik], self.Sk_ref[ik])
|
146
|
+
occ = get_occupation(
|
147
|
+
evals, self.kweights, self.model.nel, width=self.model.width
|
148
|
+
)
|
149
|
+
# occ = fermi(evals, self.model.efermi, width=self.model.width)
|
150
|
+
self.rho_ref = np.einsum("kib, kb, kjb -> kij", evecs, occ, evecs.conj())
|
151
|
+
|
152
|
+
def get_band_energy_from_rho(self, axis):
|
153
|
+
"""
|
154
|
+
This is wrong!! Should use second order perturbation theory to get the band energy instead.
|
155
|
+
"""
|
156
|
+
eband = 0.0
|
157
|
+
for ik, k in enumerate(self.kpts):
|
158
|
+
rho = rotate_Matrix_from_z_to_axis(self.rho_ref[ik], axis)
|
159
|
+
Hk_xc = rotate_Matrix_from_z_to_axis(self.Hk_xc_ref[ik], axis)
|
160
|
+
Hk_soc = self.Hk_soc_ref[ik]
|
161
|
+
Htot = Hk_xc + Hk_soc * self.model.soc_lambda
|
162
|
+
# Sk = self.Sk_ref[ik]
|
163
|
+
# evals, evecs = eigh(Htot, Sk)
|
164
|
+
# rho2= np.einsum("ib, b, jb -> ij", evecs, fermi(evals, self.model.efermi, width=0.05), evecs.conj())
|
165
|
+
if ik == 0 and False:
|
166
|
+
pass
|
167
|
+
# print(f"{evecs[:4,0:4].real=}")
|
168
|
+
# print(f"{evals[:4]=}")
|
169
|
+
# print(f"{Hk_xc[:4,0:4].real=}")
|
170
|
+
# print(f"{Htot[:4,0:4].real=}")
|
171
|
+
# print(f"{Sk[:4,0:4].real=}")
|
172
|
+
# print(f"{rho[:4,0:4].real=}")
|
173
|
+
# print(f"{rho2[:4,0:4].real=}")
|
174
|
+
# eband1 = np.sum(evals * fermi(evals, self.model.efermi, width=0.05))
|
175
|
+
# eband2 = np.trace(Htot @ rho2).real
|
176
|
+
# eband3 = np.trace(Htot @ rho).real
|
177
|
+
# print(eband1, eband2, eband3)
|
178
|
+
e_soc = np.trace(Hk_soc @ rho) * self.kweights[ik] * self.model.soc_lambda
|
179
|
+
eband += e_soc
|
180
|
+
return eband
|
181
|
+
|
182
|
+
def get_band_energy_vs_angles(
|
183
|
+
self,
|
184
|
+
thetas,
|
185
|
+
psis,
|
186
|
+
):
|
187
|
+
es = []
|
188
|
+
# es2 = []
|
189
|
+
# e,rho = self.model.get_band_energy(dm=True)
|
190
|
+
# self.calc_ref()
|
191
|
+
# thetas = np.linspace(*angle_range, npoints)
|
192
|
+
for i, theta, phi in enumerate(zip(thetas, psis)):
|
193
|
+
axis = spherical_to_cartesian(theta, phi)
|
194
|
+
self.model.rotate_HR_xc(axis)
|
195
|
+
# self.get_band_energy2()
|
196
|
+
e = self.get_band_energy()
|
197
|
+
es.append(e)
|
198
|
+
# es2.append(e2)
|
199
|
+
return es
|
200
|
+
|
201
|
+
|
202
|
+
def get_model_energy(model, kmesh, gamma=True):
|
203
|
+
ham = RotateHam(model, kmesh, gamma=gamma)
|
204
|
+
return ham.get_band_energy()
|
205
|
+
|
206
|
+
|
207
|
+
class AbacusSplitSOCParser:
|
208
|
+
"""
|
209
|
+
Abacus parser with Hamiltonian split to SOC and non-SOC parts
|
210
|
+
"""
|
211
|
+
|
212
|
+
def __init__(self, outpath_nosoc=None, outpath_soc=None, binary=False):
|
213
|
+
self.outpath_nosoc = outpath_nosoc
|
214
|
+
self.outpath_soc = outpath_soc
|
215
|
+
self.binary = binary
|
216
|
+
self.parser_nosoc = AbacusParser(outpath=outpath_nosoc, binary=binary)
|
217
|
+
self.parser_soc = AbacusParser(outpath=outpath_soc, binary=binary)
|
218
|
+
spin1 = self.parser_nosoc.read_spin()
|
219
|
+
spin2 = self.parser_soc.read_spin()
|
220
|
+
if spin1 != "noncollinear" or spin2 != "noncollinear":
|
221
|
+
raise ValueError("Spin should be noncollinear")
|
222
|
+
|
223
|
+
def parse(self):
|
224
|
+
nbasis, Rlist, HR, SR = self.parser_nosoc.Read_HSR_noncollinear()
|
225
|
+
nbasis2, Rlist2, HR2, SR2 = self.parser_soc.Read_HSR_noncollinear()
|
226
|
+
# print(HR[0])
|
227
|
+
HR_soc = HR2 - HR
|
228
|
+
model = AbacusSplitSOCWrapper(HR, SR, Rlist, nbasis, nspin=2, HR_soc=HR_soc)
|
229
|
+
model.efermi = self.parser_soc.efermi
|
230
|
+
model.basis = self.parser_nosoc.basis
|
231
|
+
model.atoms = self.parser_nosoc.atoms
|
232
|
+
return model
|
233
|
+
|
234
|
+
|
235
|
+
def abacus_get_MAE(
|
236
|
+
path_nosoc, path_soc, kmesh, thetas, psis, gamma=True, outfile="MAE.txt"
|
237
|
+
):
|
238
|
+
"""Get MAE from Abacus with magnetic force theorem. Two calculations are needed. First we do an calculation with SOC but the soc_lambda is set to 0. Save the density. The next calculatin we start with the density from the first calculation and set the SOC prefactor to 1. With the information from the two calcualtions, we can get the band energy with magnetic moments in the direction, specified in two list, thetas, and phis."""
|
239
|
+
parser = AbacusSplitSOCParser(
|
240
|
+
outpath_nosoc=path_nosoc, outpath_soc=path_soc, binary=False
|
241
|
+
)
|
242
|
+
model = parser.parse()
|
243
|
+
ham = RotateHam(model, kmesh, gamma=gamma)
|
244
|
+
es = []
|
245
|
+
for theta, psi in zip(thetas, psis):
|
246
|
+
axis = spherical_to_cartesian(theta, psi)
|
247
|
+
model.rotate_HR_xc(axis)
|
248
|
+
e = ham.get_band_energy()
|
249
|
+
es.append(ham.get_band_energy())
|
250
|
+
if outfile:
|
251
|
+
with open(outfile, "w") as f:
|
252
|
+
f.write("theta, psi, energy\n")
|
253
|
+
for theta, psi, e in zip(thetas, psis, es):
|
254
|
+
f.write(f"{theta}, {psi}, {e}\n")
|
255
|
+
return es
|
256
|
+
|
257
|
+
|
258
|
+
def test_AbacusSplitSOCWrapper():
|
259
|
+
# path = Path("~/projects/2D_Fe").expanduser()
|
260
|
+
path = Path("~/projects/TB2Jflows/examples/2D_Fe/Fe_z").expanduser()
|
261
|
+
outpath_nosoc = f"{path}/soc0/OUT.ABACUS"
|
262
|
+
outpath_soc = f"{path}/soc1/OUT.ABACUS"
|
263
|
+
parser = AbacusSplitSOCParser(
|
264
|
+
outpath_nosoc=outpath_nosoc, outpath_soc=outpath_soc, binary=False
|
265
|
+
)
|
266
|
+
model = parser.parse()
|
267
|
+
kmesh = [6, 6, 1]
|
268
|
+
|
269
|
+
r = RotateHam(model, kmesh)
|
270
|
+
# thetas, es = r.get_band_energy_vs_theta(angle_range=(0, np.pi*2), rotation_axis="z", initial_direction=(1,0,0), npoints=21)
|
271
|
+
thetas, es, es2 = r.get_band_energy_vs_theta(
|
272
|
+
angle_range=(0, np.pi * 2),
|
273
|
+
rotation_axis="y",
|
274
|
+
initial_direction=(0, 0, 1),
|
275
|
+
npoints=11,
|
276
|
+
)
|
277
|
+
# print the table of thetas and es, es2
|
278
|
+
print("theta, e, e2")
|
279
|
+
for theta, e, e2 in zip(thetas, es, es2):
|
280
|
+
print(f"{theta=}, {e=}, {e2=}")
|
281
|
+
|
282
|
+
plt.plot(thetas / np.pi, es - es[0], marker="o")
|
283
|
+
plt.plot(thetas / np.pi, es2 - es2[0], marker=".")
|
284
|
+
plt.savefig("E_along_z_x_z.png")
|
285
|
+
plt.show()
|
286
|
+
|
287
|
+
|
288
|
+
def abacus_get_MAE_cli():
|
289
|
+
import argparse
|
290
|
+
|
291
|
+
parser = argparse.ArgumentParser(
|
292
|
+
description="Get MAE from Abacus with magnetic force theorem. Two calculations are needed. First we do an calculation with SOC but the soc_lambda is set to 0. Save the density. The next calculatin we start with the density from the first calculation and set the SOC prefactor to 1. With the information from the two calcualtions, we can get the band energy with magnetic moments in the direction, specified in two list, thetas, and phis. "
|
293
|
+
)
|
294
|
+
parser.add_argument("path_nosoc", type=str, help="Path to the calculation with ")
|
295
|
+
parser.add_argument("path_soc", type=str, help="Path to the SOC calculation")
|
296
|
+
parser.add_argument("thetas", type=float, nargs="+", help="Thetas")
|
297
|
+
parser.add_argument("psis", type=float, nargs="+", help="Phis")
|
298
|
+
parser.add_argument("kmesh", type=int, nargs=3, help="K-mesh")
|
299
|
+
parser.add_argument(
|
300
|
+
"--gamma", action="store_true", help="Use Gamma centered kpoints"
|
301
|
+
)
|
302
|
+
parser.add_argument(
|
303
|
+
"--outfile",
|
304
|
+
type=str,
|
305
|
+
help="The angles and the energey will be saved in this file.",
|
306
|
+
)
|
307
|
+
args = parser.parse_args()
|
308
|
+
abacus_get_MAE(
|
309
|
+
args.path_nosoc,
|
310
|
+
args.path_soc,
|
311
|
+
args.kmesh,
|
312
|
+
args.thetas,
|
313
|
+
args.psis,
|
314
|
+
gamma=args.gamma,
|
315
|
+
outfile=args.outfile,
|
316
|
+
)
|
317
|
+
|
318
|
+
|
319
|
+
if __name__ == "__main__":
|
320
|
+
abacus_get_MAE_cli()
|
@@ -16,10 +16,10 @@ from TB2J.abacus.stru_api import read_abacus, read_abacus_out
|
|
16
16
|
|
17
17
|
class AbacusWrapper(AbstractTB):
|
18
18
|
def __init__(self, HR, SR, Rlist, nbasis, nspin=1):
|
19
|
-
self.R2kfactor =
|
19
|
+
self.R2kfactor = 2j * np.pi
|
20
20
|
self.is_orthogonal = False
|
21
21
|
self._name = "ABACUS"
|
22
|
-
self.
|
22
|
+
self._HR = HR
|
23
23
|
self.SR = SR
|
24
24
|
self.Rlist = Rlist
|
25
25
|
self.nbasis = nbasis
|
@@ -27,6 +27,14 @@ class AbacusWrapper(AbstractTB):
|
|
27
27
|
self.norb = nbasis * nspin
|
28
28
|
self._build_Rdict()
|
29
29
|
|
30
|
+
@property
|
31
|
+
def HR(self):
|
32
|
+
return self._HR
|
33
|
+
|
34
|
+
@HR.setter
|
35
|
+
def set_HR(self, HR):
|
36
|
+
self._HR = HR
|
37
|
+
|
30
38
|
def _build_Rdict(self):
|
31
39
|
if hasattr(self, "Rdict"):
|
32
40
|
pass
|
@@ -62,6 +70,8 @@ class AbacusWrapper(AbstractTB):
|
|
62
70
|
S = self.SR[iR] * phase
|
63
71
|
# Sk += S + S.conjugate().T
|
64
72
|
Sk += S
|
73
|
+
# Hk = (Hk + Hk.conj().T)/2
|
74
|
+
# Sk = (Sk + Sk.conj().T)/2
|
65
75
|
elif convention == 1:
|
66
76
|
# TODO: implement the first convention (the r convention)
|
67
77
|
raise NotImplementedError("convention 1 is not implemented yet.")
|
@@ -74,6 +84,14 @@ class AbacusWrapper(AbstractTB):
|
|
74
84
|
Hk, Sk = self.gen_ham(k, convention=convention)
|
75
85
|
return eigh(Hk, Sk)
|
76
86
|
|
87
|
+
def solve_all(self, kpts, convention=2):
|
88
|
+
nk = len(kpts)
|
89
|
+
evals = np.zeros((nk, self.nbasis), dtype=float)
|
90
|
+
evecs = np.zeros((nk, self.nbasis, self.nbasis), dtype=complex)
|
91
|
+
for ik, k in enumerate(kpts):
|
92
|
+
evals[ik], evecs[ik] = self.solve(k, convention=convention)
|
93
|
+
return evals, evecs
|
94
|
+
|
77
95
|
def HSE_k(self, kpt, convention=2):
|
78
96
|
H, S = self.gen_ham(tuple(kpt), convention=convention)
|
79
97
|
evals, evecs = eigh(H, S)
|
@@ -0,0 +1,278 @@
|
|
1
|
+
"""
|
2
|
+
This file is stolen from the hotbit programm, with some modification.
|
3
|
+
"""
|
4
|
+
|
5
|
+
import numpy as np
|
6
|
+
from scipy.optimize import brentq
|
7
|
+
import sys
|
8
|
+
|
9
|
+
from ase.dft.dos import DOS
|
10
|
+
from scipy import integrate
|
11
|
+
|
12
|
+
# import numba
|
13
|
+
|
14
|
+
# from numba import float64, int32
|
15
|
+
|
16
|
+
MAX_EXP_ARGUMENT = np.log(sys.float_info.max)
|
17
|
+
|
18
|
+
# @numba.vectorize(nopython=True)
|
19
|
+
# def myfermi(e, mu, width, nspin):
|
20
|
+
# x = (e - mu) / width
|
21
|
+
# if x < -10:
|
22
|
+
# ret = 2.0 / nspin
|
23
|
+
# elif x > 10:
|
24
|
+
# ret = 0.0
|
25
|
+
# else:
|
26
|
+
# ret = 2.0 / nspin / (math.exp(x) + 1)
|
27
|
+
# return ret
|
28
|
+
|
29
|
+
|
30
|
+
def myfermi(e, mu, width, nspin):
|
31
|
+
x = (e - mu) / width
|
32
|
+
return np.where(x < 10, 2.0 / (nspin * (np.exp(x) + 1.0)), 0.0)
|
33
|
+
|
34
|
+
|
35
|
+
class Occupations(object):
|
36
|
+
def __init__(self, nel, width, wk, nspin=1):
|
37
|
+
"""
|
38
|
+
Initialize parameters for occupations.
|
39
|
+
:param nel: Number of electrons
|
40
|
+
:param width: Fermi-broadening
|
41
|
+
:param wk: k-point weights. eg. If only gamma, [1.0]
|
42
|
+
:param nspin(optional): number of spin, if spin=1 multiplicity=2 else, multiplicity=1.
|
43
|
+
"""
|
44
|
+
self.nel = nel
|
45
|
+
self.width = width
|
46
|
+
self.wk = wk
|
47
|
+
self.nk = len(wk)
|
48
|
+
self.nspin = nspin
|
49
|
+
|
50
|
+
def get_mu(self):
|
51
|
+
"""Return the Fermi-level (or chemical potential)."""
|
52
|
+
return self.mu
|
53
|
+
|
54
|
+
def fermi(self, mu):
|
55
|
+
"""
|
56
|
+
Occupy states with given chemical potential.
|
57
|
+
Occupations are 0...2; without k-point weights
|
58
|
+
"""
|
59
|
+
return myfermi(self.e, mu, self.width, self.nspin)
|
60
|
+
|
61
|
+
def root_function(self, mu):
|
62
|
+
"""This function is exactly zero when mu is right."""
|
63
|
+
f = self.fermi(mu)
|
64
|
+
return np.einsum("i, ij->", self.wk, f) - self.nel
|
65
|
+
|
66
|
+
def occupy(self, e, xtol=1e-11):
|
67
|
+
"""
|
68
|
+
Calculate occupation numbers with given Fermi-broadening.
|
69
|
+
|
70
|
+
@param e: e[ind_k,ind_orb] energy of k-point, state a
|
71
|
+
Note added by hexu: With spin=2,e[k,a,sigma], it also work. only the *2 should be removed.
|
72
|
+
@param wk: wk[:] weights for k-points
|
73
|
+
@param width: The Fermi-broadening
|
74
|
+
|
75
|
+
Returns: fermi[ind_k, ind_orb]
|
76
|
+
"""
|
77
|
+
self.e = e
|
78
|
+
eflat = e.flatten()
|
79
|
+
ind = np.argsort(eflat)
|
80
|
+
e_sorted = eflat[ind]
|
81
|
+
if self.nspin == 1:
|
82
|
+
m = 2
|
83
|
+
elif self.nspin == 2:
|
84
|
+
m = 1
|
85
|
+
n_sorted = (self.wk[:, None, None] * np.ones_like(e) * m).flatten()[ind]
|
86
|
+
|
87
|
+
sum = n_sorted.cumsum()
|
88
|
+
if self.nel < sum[0]:
|
89
|
+
ifermi = 0
|
90
|
+
elif self.nel > sum[-1]:
|
91
|
+
raise ("number of electrons larger than number of orbital*spin")
|
92
|
+
else:
|
93
|
+
ifermi = np.searchsorted(sum, self.nel)
|
94
|
+
try:
|
95
|
+
if ifermi == 0:
|
96
|
+
elo = e_sorted[0]
|
97
|
+
else:
|
98
|
+
elo = e_sorted[ifermi - 1]
|
99
|
+
if ifermi == len(e_sorted) - 1:
|
100
|
+
ehi = e_sorted[-1]
|
101
|
+
else:
|
102
|
+
ehi = e_sorted[ifermi + 1]
|
103
|
+
guess = e_sorted[ifermi]
|
104
|
+
dmu = np.max((self.width, guess - elo, ehi - guess))
|
105
|
+
mu = brentq(self.root_function, guess - dmu, guess + dmu, xtol=xtol)
|
106
|
+
# mu = brent(
|
107
|
+
# self.root_function,
|
108
|
+
# brack=(guess - elo, guess, guess + dmu),
|
109
|
+
# tol=xtol)
|
110
|
+
except Exception as E:
|
111
|
+
# probably a bad guess
|
112
|
+
print("Error in finding Fermi level: ", E)
|
113
|
+
dmu = self.width
|
114
|
+
if self.nel < 1e-3:
|
115
|
+
mu = min(e_sorted) - dmu * 20
|
116
|
+
elif self.nel - sum[-1] > -1e-3:
|
117
|
+
mu = max(e_sorted) + dmu * 20
|
118
|
+
else:
|
119
|
+
# mu = brent(
|
120
|
+
# self.root_function,
|
121
|
+
# brack=(e_sorted[0] - dmu * 10,
|
122
|
+
# guess,
|
123
|
+
# e_sorted[-1] + dmu * 10),
|
124
|
+
# tol=xtol)
|
125
|
+
mu = brentq(
|
126
|
+
self.root_function,
|
127
|
+
e_sorted[0] - dmu * 20,
|
128
|
+
e_sorted[-1] + dmu * 20,
|
129
|
+
xtol=xtol,
|
130
|
+
)
|
131
|
+
|
132
|
+
if np.abs(self.root_function(mu)) > xtol * 1e4:
|
133
|
+
# raise RuntimeError(
|
134
|
+
# 'Fermi level could not be assigned reliably. Has the system fragmented?'
|
135
|
+
# )
|
136
|
+
print(
|
137
|
+
"Fermi level could not be assigned reliably. Has the system fragmented?"
|
138
|
+
)
|
139
|
+
|
140
|
+
f = self.fermi(mu)
|
141
|
+
# rho=(self.eigenvecs*f).dot(self.eigenvecs.transpose())
|
142
|
+
|
143
|
+
self.mu, self.f = mu, f
|
144
|
+
return f
|
145
|
+
|
146
|
+
def plot(self):
|
147
|
+
import pylab as pl
|
148
|
+
|
149
|
+
for ik in range(self.nk):
|
150
|
+
pl.plot(self.e[ik, :], self.f[ik, :])
|
151
|
+
pl.scatter(self.e[ik, :], self.f[ik, :])
|
152
|
+
pl.title("occupations")
|
153
|
+
pl.xlabel("energy (Ha)")
|
154
|
+
pl.ylabel("occupation")
|
155
|
+
pl.show()
|
156
|
+
|
157
|
+
|
158
|
+
class GaussOccupations(Occupations):
|
159
|
+
def get_mu(self):
|
160
|
+
return self.mu
|
161
|
+
|
162
|
+
def delta(self, energy):
|
163
|
+
"""Return a delta-function centered at 'energy'."""
|
164
|
+
x = -(((self.e - energy) / self.width) ** 2)
|
165
|
+
return np.exp(x) / (np.sqrt(np.pi) * self.width)
|
166
|
+
|
167
|
+
def get_dos(self, npts=500):
|
168
|
+
eflat = self.e.flatten()
|
169
|
+
ind = np.argsort(eflat)
|
170
|
+
##e_sorted = eflat[ind]
|
171
|
+
if self.nspin == 1:
|
172
|
+
m = 2
|
173
|
+
elif self.nspin == 2:
|
174
|
+
m = 1
|
175
|
+
# n_sorted = (self.wk * np.ones_like(self.e) * m).flatten()[ind]
|
176
|
+
dos = np.zeros(npts)
|
177
|
+
for w, e_n in zip(self.w_k, self.e_skn[0]):
|
178
|
+
for e in e_n:
|
179
|
+
dos += w * self.delta(e)
|
180
|
+
|
181
|
+
def root_function(self, mu):
|
182
|
+
pass
|
183
|
+
|
184
|
+
# @profile
|
185
|
+
def occupy(self, e, xtol=1e-8, guess=0.0):
|
186
|
+
self.e = e
|
187
|
+
dos = myDOS(kweights=self.wk, eigenvalues=e, width=self.width, npts=501)
|
188
|
+
edos = dos.get_energies()
|
189
|
+
d = dos.get_dos()
|
190
|
+
idos = integrate.cumtrapz(d, edos, initial=0) - self.nel
|
191
|
+
# f_idos = interpolate.interp1d(edos, idos)
|
192
|
+
# ret = optimize.fmin(f_idos, x0=edos[400], xtol=xtol, disp=True)
|
193
|
+
ifermi = np.searchsorted(idos, 0.0)
|
194
|
+
# self.mu = ret[0]
|
195
|
+
self.mu = edos[ifermi]
|
196
|
+
self.f = self.fermi(self.mu)
|
197
|
+
return self.f
|
198
|
+
|
199
|
+
|
200
|
+
class myDOS(DOS):
|
201
|
+
def __init__(
|
202
|
+
self, kweights, eigenvalues, nspin=1, width=0.1, window=None, npts=1001
|
203
|
+
):
|
204
|
+
"""Electronic Density Of States object.
|
205
|
+
|
206
|
+
calc: calculator object
|
207
|
+
Any ASE compliant calculator object.
|
208
|
+
width: float
|
209
|
+
Width of guassian smearing. Use width=0.0 for linear tetrahedron
|
210
|
+
interpolation.
|
211
|
+
window: tuple of two float
|
212
|
+
Use ``window=(emin, emax)``. If not specified, a window
|
213
|
+
big enough to hold all the eigenvalues will be used.
|
214
|
+
npts: int
|
215
|
+
Number of points.
|
216
|
+
|
217
|
+
"""
|
218
|
+
self.npts = npts
|
219
|
+
self.width = width
|
220
|
+
# self.w_k = calc.get_k_point_weights()
|
221
|
+
self.w_k = kweights
|
222
|
+
self.nspins = nspin
|
223
|
+
# self.e_skn = np.array([[calc.get_eigenvalues(kpt=k, spin=s)
|
224
|
+
# for k in range(len(self.w_k))]
|
225
|
+
# for s in range(self.nspins)])
|
226
|
+
# self.e_skn -= calc.get_fermi_level()
|
227
|
+
self.e_skn = np.array([eigenvalues.T]) # eigenvalues: iband, ikpt
|
228
|
+
|
229
|
+
if window is None:
|
230
|
+
emin = None
|
231
|
+
emax = None
|
232
|
+
else:
|
233
|
+
emin, emax = window
|
234
|
+
|
235
|
+
if emin is None:
|
236
|
+
emin = self.e_skn.min() - 10 * self.width
|
237
|
+
if emax is None:
|
238
|
+
emax = self.e_skn.max() + 10 * self.width
|
239
|
+
|
240
|
+
self.energies = np.linspace(emin, emax, npts)
|
241
|
+
|
242
|
+
# if width == 0.0: # To use tetrahedron method
|
243
|
+
# bzkpts = calc.get_bz_k_points()
|
244
|
+
# size, offset = get_monkhorst_pack_size_and_offset(bzkpts)
|
245
|
+
# bz2ibz = calc.get_bz_to_ibz_map()
|
246
|
+
# shape = (self.nspins,) + tuple(size) + (-1,)
|
247
|
+
# self.e_skn = self.e_skn[:, bz2ibz].reshape(shape)
|
248
|
+
# self.cell = calc.atoms.cell
|
249
|
+
|
250
|
+
def get_idos(self):
|
251
|
+
e, d = self.get_dos()
|
252
|
+
return np.trapz(d, e)
|
253
|
+
|
254
|
+
def delta(self, energy):
|
255
|
+
"""Return a delta-function centered at 'energy'."""
|
256
|
+
x = -(((self.energies - energy) / self.width) ** 2)
|
257
|
+
return np.exp(x) / (np.sqrt(np.pi) * self.width)
|
258
|
+
|
259
|
+
def get_dos(self, spin=None):
|
260
|
+
"""Get array of DOS values.
|
261
|
+
|
262
|
+
The *spin* argument can be 0 or 1 (spin up or down) - if not
|
263
|
+
specified, the total DOS is returned.
|
264
|
+
"""
|
265
|
+
|
266
|
+
if spin is None:
|
267
|
+
if self.nspins == 2:
|
268
|
+
# Spin-polarized calculation, but no spin specified -
|
269
|
+
# return the total DOS:
|
270
|
+
return self.get_dos(spin=0) + self.get_dos(spin=1)
|
271
|
+
else:
|
272
|
+
spin = 0
|
273
|
+
|
274
|
+
dos = np.zeros(self.npts)
|
275
|
+
for w, e_n in zip(self.w_k, self.e_skn[spin]):
|
276
|
+
for e in e_n:
|
277
|
+
dos += w * self.delta(e)
|
278
|
+
return dos
|
@@ -0,0 +1,38 @@
|
|
1
|
+
from scipy.linalg import eigh
|
2
|
+
import numpy as np
|
3
|
+
|
4
|
+
|
5
|
+
def gen_random_hermitean_matrix(n):
|
6
|
+
A = np.random.rand(n, n) + 1j * np.random.rand(n, n)
|
7
|
+
return A + A.conj().T
|
8
|
+
|
9
|
+
|
10
|
+
def gen_overlap_matrix(n):
|
11
|
+
A = np.random.rand(n, n) + 1j * np.random.rand(n, n)
|
12
|
+
return np.dot(A, A.conj().T)
|
13
|
+
|
14
|
+
|
15
|
+
def fermi_function(x, ef, beta):
|
16
|
+
return 1.0 / (np.exp(beta * (x - ef)) + 1)
|
17
|
+
|
18
|
+
|
19
|
+
def test():
|
20
|
+
n = 10
|
21
|
+
A = gen_random_hermitean_matrix(n)
|
22
|
+
S = gen_overlap_matrix(n)
|
23
|
+
beta = 0.1
|
24
|
+
ef = 0
|
25
|
+
|
26
|
+
evals, evecs = eigh(A, S)
|
27
|
+
|
28
|
+
etot = np.sum(evals * fermi_function(evals, ef, beta))
|
29
|
+
|
30
|
+
rho = np.einsum("ib,b,jb->ij", evecs, fermi_function(evals, ef, beta), evecs.conj())
|
31
|
+
|
32
|
+
etot2 = np.trace(np.dot(A, rho))
|
33
|
+
|
34
|
+
print(etot, etot2)
|
35
|
+
|
36
|
+
|
37
|
+
if __name__ == "__main__":
|
38
|
+
test()
|
@@ -7,6 +7,8 @@ import tempfile
|
|
7
7
|
from pathos.multiprocessing import ProcessPool
|
8
8
|
import sys
|
9
9
|
import pickle
|
10
|
+
import warnings
|
11
|
+
from TB2J.mathutils.fermi import fermi
|
10
12
|
|
11
13
|
MAX_EXP_ARGUMENT = np.log(sys.float_info.max)
|
12
14
|
|
@@ -26,19 +28,6 @@ def eigen_to_G(evals, evecs, efermi, energy):
|
|
26
28
|
)
|
27
29
|
|
28
30
|
|
29
|
-
def fermi(e, mu, width=0.01):
|
30
|
-
"""
|
31
|
-
the fermi function.
|
32
|
-
.. math::
|
33
|
-
f=\\frac{1}{\exp((e-\mu)/width)+1}
|
34
|
-
|
35
|
-
:param e,mu,width: e,\mu,width
|
36
|
-
"""
|
37
|
-
|
38
|
-
x = (e - mu) / width
|
39
|
-
return np.where(x < MAX_EXP_ARGUMENT, 1 / (1.0 + np.exp(x)), 0.0)
|
40
|
-
|
41
|
-
|
42
31
|
def find_energy_ingap(evals, rbound, gap=4.0):
|
43
32
|
"""
|
44
33
|
find a energy inside a gap below rbound (right bound),
|
@@ -0,0 +1 @@
|
|
1
|
+
from .lowdin import Lowdin
|
@@ -0,0 +1,22 @@
|
|
1
|
+
import numpy as np
|
2
|
+
import warnings
|
3
|
+
import sys
|
4
|
+
|
5
|
+
MAX_EXP_ARGUMENT = np.log(sys.float_info.max)
|
6
|
+
|
7
|
+
|
8
|
+
def fermi(e, mu, width=0.01):
|
9
|
+
"""
|
10
|
+
the fermi function.
|
11
|
+
.. math::
|
12
|
+
f=\\frac{1}{\exp((e-\mu)/width)+1}
|
13
|
+
|
14
|
+
:param e,mu,width: e,\mu,width
|
15
|
+
"""
|
16
|
+
x = (e - mu) / width
|
17
|
+
# disable overflow warning
|
18
|
+
with warnings.catch_warnings():
|
19
|
+
warnings.simplefilter("ignore")
|
20
|
+
ret = np.where(x < MAX_EXP_ARGUMENT, 1 / (1.0 + np.exp(x)), 0.0)
|
21
|
+
|
22
|
+
return ret
|
@@ -0,0 +1,90 @@
|
|
1
|
+
import numpy as np
|
2
|
+
|
3
|
+
|
4
|
+
def HR_to_k(HR, Rlist, kpts):
|
5
|
+
# Hk[k,:,:] = sum_R (H[R] exp(i2pi k.R))
|
6
|
+
phase = np.exp(2.0j * np.pi * np.tensordot(kpts, Rlist, axes=([1], [1])))
|
7
|
+
Hk = np.einsum("rlm, kr -> klm", HR, phase)
|
8
|
+
return Hk
|
9
|
+
|
10
|
+
|
11
|
+
def Hk_to_R(Hk, Rlist, kpts, kweights):
|
12
|
+
phase = np.exp(-2.0j * np.pi * np.tensordot(kpts, Rlist, axes=([1], [1])))
|
13
|
+
HR = np.einsum("klm, kr, k->rlm", Hk, phase, kweights)
|
14
|
+
return HR
|
15
|
+
|
16
|
+
|
17
|
+
def k_to_R(kpts, Rlist, Mk, kweights=None):
|
18
|
+
"""
|
19
|
+
Transform k-space wavefunctions to real space.
|
20
|
+
params:
|
21
|
+
kpts: k-points
|
22
|
+
Rlist: list of R vectors
|
23
|
+
Mk: matrix of shape [nkpt, n1, n2] in k-space.
|
24
|
+
|
25
|
+
return:
|
26
|
+
MR: matrix of shape [nR, n1, n2], the matrix in R-space.
|
27
|
+
|
28
|
+
"""
|
29
|
+
nkpt, n1, n2 = Mk.shape
|
30
|
+
if kweights is None:
|
31
|
+
kweights = np.ones(nkpt, dtype=float) / nkpt
|
32
|
+
phase = np.exp(-2.0j * np.pi * np.tensordot(kpts, Rlist, axes=([1], [1])))
|
33
|
+
MR = np.einsum("klm, kr, k -> rlm", Mk, phase, kweights)
|
34
|
+
return MR
|
35
|
+
|
36
|
+
# nkpt, n1, n2 = Mk.shape
|
37
|
+
# nR = Rlist.shape[0]
|
38
|
+
# MR = np.zeros((nR, n1, n2), dtype=complex)
|
39
|
+
# if kweights is None:
|
40
|
+
# kweights = np.ones(nkpt, dtype=float)/nkpt
|
41
|
+
# for iR, R in enumerate(Rlist):
|
42
|
+
# for ik in range(nkpt):
|
43
|
+
# MR[iR] += Mk[ik] * np.exp(-2.0j*np.pi * np.dot(kpts[ik], R)) * kweights[ik]
|
44
|
+
# return MR
|
45
|
+
|
46
|
+
|
47
|
+
def R_to_k(kpts, Rlist, MR):
|
48
|
+
"""
|
49
|
+
Transform real-space wavefunctions to k-space.
|
50
|
+
params:
|
51
|
+
kpts: k-points
|
52
|
+
Rlist: list of R vectors
|
53
|
+
MR: matrix of shape [nR, n1, n2] in R-space.
|
54
|
+
|
55
|
+
return:
|
56
|
+
Mk: matrix of shape [nkpt, n1, n2], the matrix in k-space.
|
57
|
+
|
58
|
+
"""
|
59
|
+
phase = np.exp(2.0 * np.pi * 1j * np.tensordot(kpts, Rlist, axes=([1], [1])))
|
60
|
+
Mk = np.einsum("rlm, kr -> klm", MR, phase)
|
61
|
+
|
62
|
+
# nkpt, n1, n2 = Mk.shape
|
63
|
+
# nR = Rlist.shape[0]
|
64
|
+
# Mk = np.zeros((nkpt, n1, n2), dtype=complex)
|
65
|
+
# for iR, R in enumerate(Rlist):
|
66
|
+
# for ik in range(nkpt):
|
67
|
+
# Mk[ik] += MR[iR] * np.exp(2.0 * np.pi * 1j * np.dot(kpts[ik], R))
|
68
|
+
return Mk
|
69
|
+
|
70
|
+
|
71
|
+
def R_to_onek(kpt, Rlist, MR):
|
72
|
+
"""
|
73
|
+
Transform real-space wavefunctions to k-space.
|
74
|
+
params:
|
75
|
+
kpt: k-point
|
76
|
+
Rlist: list of R vectors
|
77
|
+
MR: matrix of shape [nR, n1, n2] in R-space.
|
78
|
+
|
79
|
+
return:
|
80
|
+
Mk: matrix of shape [n1, n2], the matrix in k-space.
|
81
|
+
|
82
|
+
"""
|
83
|
+
phase = np.exp(2.0j * np.pi * np.dot(Rlist, kpt))
|
84
|
+
Mk = np.einsum("rlm, r -> lm", MR, phase)
|
85
|
+
return Mk
|
86
|
+
# n1, n2 = MR.shape[1:]
|
87
|
+
# Mk = np.zeros((n1, n2), dtype=complex)
|
88
|
+
# for iR, R in enumerate(Rlist):
|
89
|
+
# Mk += MR[iR] * np.exp(2.0j*np.pi * np.dot(kpt, R))
|
90
|
+
# return Mk
|
@@ -0,0 +1,12 @@
|
|
1
|
+
import numpy as np
|
2
|
+
from scipy.linalg import inv, eigh
|
3
|
+
|
4
|
+
|
5
|
+
def Lowdin(S):
|
6
|
+
"""
|
7
|
+
Calculate S^(-1/2).
|
8
|
+
Which is used in lowind's symmetric orthonormalization.
|
9
|
+
psi_prime = S^(-1/2) psi
|
10
|
+
"""
|
11
|
+
eigval, eigvec = eigh(S)
|
12
|
+
return eigvec @ np.diag(np.sqrt(1.0 / eigval)) @ (eigvec.T.conj())
|
@@ -0,0 +1,35 @@
|
|
1
|
+
import numpy as np
|
2
|
+
from TB2J.pauli import pauli_block_all, s0, s1, s2, s3, gather_pauli_blocks
|
3
|
+
|
4
|
+
|
5
|
+
def rotate_Matrix_from_z_to_axis(M, axis, normalize=True):
|
6
|
+
"""
|
7
|
+
Given a spinor matrix M, rotate it from z-axis to axis.
|
8
|
+
The spinor matrix M is a 2x2 matrix, which can be decomposed as I, x, y, z components using Pauli matrices.
|
9
|
+
"""
|
10
|
+
MI, Mx, My, Mz = pauli_block_all(M)
|
11
|
+
axis = axis / np.linalg.norm(axis)
|
12
|
+
# M_new = s0* MI + Mz * (axis[0] * s1 + axis[1] * s2 + axis[2] * s3) *2
|
13
|
+
M_new = gather_pauli_blocks(MI, Mz * axis[0], Mz * axis[1], Mz * axis[2])
|
14
|
+
return M_new
|
15
|
+
|
16
|
+
|
17
|
+
def test_rotate_Matrix_from_z_to_axis():
|
18
|
+
M = np.array([[1.1, 0], [0, 0.9]])
|
19
|
+
print(pauli_block_all(M))
|
20
|
+
Mnew = rotate_Matrix_from_z_to_axis(M, [1, 1, 1])
|
21
|
+
print(pauli_block_all(Mnew))
|
22
|
+
print(Mnew)
|
23
|
+
|
24
|
+
M = np.array(
|
25
|
+
[
|
26
|
+
[-9.90532976e-06 + 0.0j, 0.00000000e00 + 0.0j],
|
27
|
+
[0.00000000e00 + 0.0j, -9.88431291e-06 + 0.0j],
|
28
|
+
]
|
29
|
+
)
|
30
|
+
print(M)
|
31
|
+
print(rotate_Matrix_from_z_to_axis(M, [0, 0, 1]))
|
32
|
+
|
33
|
+
|
34
|
+
if __name__ == "__main__":
|
35
|
+
test_rotate_Matrix_from_z_to_axis()
|
@@ -143,7 +143,24 @@ def pauli_block_all(M):
|
|
143
143
|
return MI, Mx, My, Mz
|
144
144
|
|
145
145
|
|
146
|
+
def gather_pauli_blocks(MI, Mx, My, Mz):
|
147
|
+
"""
|
148
|
+
Gather the I, x, y, z component of a matrix.
|
149
|
+
"""
|
150
|
+
return np.kron(MI, s0) + np.kron(Mx, s1) + np.kron(My, s2) + np.kron(Mz, s3)
|
151
|
+
|
152
|
+
|
153
|
+
def test_gather_pauli_blocks():
|
154
|
+
M = np.random.rand(4, 4)
|
155
|
+
MI, Mx, My, Mz = pauli_block_all(M)
|
156
|
+
M2 = gather_pauli_blocks(MI, Mx, My, Mz)
|
157
|
+
assert np.allclose(M, M2)
|
158
|
+
|
159
|
+
|
146
160
|
def op_norm(M):
|
161
|
+
"""
|
162
|
+
Return the operator norm of a matrix.
|
163
|
+
"""
|
147
164
|
return max(svd(M)[1])
|
148
165
|
|
149
166
|
|
@@ -39,12 +39,15 @@ TB2J.egg-info/SOURCES.txt
|
|
39
39
|
TB2J.egg-info/dependency_links.txt
|
40
40
|
TB2J.egg-info/requires.txt
|
41
41
|
TB2J.egg-info/top_level.txt
|
42
|
+
TB2J/abacus/MAE.py
|
42
43
|
TB2J/abacus/__init__.py
|
43
44
|
TB2J/abacus/abacus_api.py
|
44
45
|
TB2J/abacus/abacus_wrapper.py
|
45
46
|
TB2J/abacus/gen_exchange_abacus.py
|
47
|
+
TB2J/abacus/occupations.py
|
46
48
|
TB2J/abacus/orbital_api.py
|
47
49
|
TB2J/abacus/stru_api.py
|
50
|
+
TB2J/abacus/test_density_matrix.py
|
48
51
|
TB2J/abacus/test_read_HRSR.py
|
49
52
|
TB2J/abacus/test_read_stru.py
|
50
53
|
TB2J/external/__init__.py
|
@@ -56,6 +59,11 @@ TB2J/io_exchange/io_tomsasd.py
|
|
56
59
|
TB2J/io_exchange/io_txt.py
|
57
60
|
TB2J/io_exchange/io_uppasd.py
|
58
61
|
TB2J/io_exchange/io_vampire.py
|
62
|
+
TB2J/mathutils/__init__.py
|
63
|
+
TB2J/mathutils/fermi.py
|
64
|
+
TB2J/mathutils/kR_convert.py
|
65
|
+
TB2J/mathutils/lowdin.py
|
66
|
+
TB2J/mathutils/rotate_spin.py
|
59
67
|
TB2J/spinham/__init__.py
|
60
68
|
TB2J/spinham/base_parser.py
|
61
69
|
TB2J/spinham/constants.py
|
@@ -1,7 +1,7 @@
|
|
1
1
|
#!/usr/bin/env python
|
2
2
|
from setuptools import setup, find_packages
|
3
3
|
|
4
|
-
__version__ = "0.9.
|
4
|
+
__version__ = "0.9.1_pre"
|
5
5
|
|
6
6
|
long_description = """TB2J is a Python package aimed to compute automatically the magnetic interactions (superexchange and Dzyaloshinskii-Moriya) between atoms of magnetic crystals from DFT Hamiltonian based on Wannier functions or Linear combination of atomic orbitals. It uses the Green's function method and take the local rigid spin rotation as a perturbation. The package can take the output from Wannier90, which is interfaced with many density functional theory codes or from codes based on localised orbitals. A minimal user input is needed, which allows for an easily integration into a high-throughput workflows. """
|
7
7
|
|
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
|
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
|
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
|
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
|
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
|
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
|