TB2J 0.9.0.1__py3-none-any.whl → 0.9.2rc0__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.
Files changed (38) hide show
  1. TB2J/Jdownfolder.py +110 -24
  2. TB2J/Jtensor.py +1 -1
  3. TB2J/MAE.py +185 -0
  4. TB2J/abacus/MAE.py +320 -0
  5. TB2J/abacus/abacus_wrapper.py +103 -4
  6. TB2J/abacus/occupations.py +278 -0
  7. TB2J/abacus/test_density_matrix.py +38 -0
  8. TB2J/cut_cell.py +82 -0
  9. TB2J/exchange.py +99 -73
  10. TB2J/exchangeCL2.py +10 -5
  11. TB2J/green.py +14 -15
  12. TB2J/io_exchange/io_pickle.py +0 -0
  13. TB2J/manager.py +10 -4
  14. TB2J/mathutils/__init__.py +1 -0
  15. TB2J/mathutils/fermi.py +22 -0
  16. TB2J/mathutils/kR_convert.py +90 -0
  17. TB2J/mathutils/lowdin.py +12 -0
  18. TB2J/mathutils/rotate_spin.py +56 -0
  19. TB2J/patch.py +50 -0
  20. TB2J/pauli.py +17 -0
  21. TB2J/spinham/h_matrix.py +68 -0
  22. TB2J/spinham/obtain_J.py +79 -0
  23. TB2J/supercell.py +532 -0
  24. {TB2J-0.9.0.1.data → TB2J-0.9.2rc0.data}/scripts/TB2J_downfold.py +8 -0
  25. {TB2J-0.9.0.1.dist-info → TB2J-0.9.2rc0.dist-info}/METADATA +1 -1
  26. {TB2J-0.9.0.1.dist-info → TB2J-0.9.2rc0.dist-info}/RECORD +38 -23
  27. {TB2J-0.9.0.1.dist-info → TB2J-0.9.2rc0.dist-info}/WHEEL +1 -1
  28. {TB2J-0.9.0.1.data → TB2J-0.9.2rc0.data}/scripts/TB2J_eigen.py +0 -0
  29. {TB2J-0.9.0.1.data → TB2J-0.9.2rc0.data}/scripts/TB2J_magnon.py +0 -0
  30. {TB2J-0.9.0.1.data → TB2J-0.9.2rc0.data}/scripts/TB2J_magnon_dos.py +0 -0
  31. {TB2J-0.9.0.1.data → TB2J-0.9.2rc0.data}/scripts/TB2J_merge.py +0 -0
  32. {TB2J-0.9.0.1.data → TB2J-0.9.2rc0.data}/scripts/TB2J_rotate.py +0 -0
  33. {TB2J-0.9.0.1.data → TB2J-0.9.2rc0.data}/scripts/TB2J_rotateDM.py +0 -0
  34. {TB2J-0.9.0.1.data → TB2J-0.9.2rc0.data}/scripts/abacus2J.py +0 -0
  35. {TB2J-0.9.0.1.data → TB2J-0.9.2rc0.data}/scripts/siesta2J.py +0 -0
  36. {TB2J-0.9.0.1.data → TB2J-0.9.2rc0.data}/scripts/wann2J.py +0 -0
  37. {TB2J-0.9.0.1.dist-info → TB2J-0.9.2rc0.dist-info}/LICENSE +0 -0
  38. {TB2J-0.9.0.1.dist-info → TB2J-0.9.2rc0.dist-info}/top_level.txt +0 -0
@@ -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()
TB2J/cut_cell.py ADDED
@@ -0,0 +1,82 @@
1
+ import numpy as np
2
+ from TB2J.supercell import find_primitive_cell, map_to_primitive
3
+ from TB2J.io_exchange.io_exchange import SpinIO, gen_distance_dict
4
+
5
+
6
+ def cut_cell(path, output_path, sc_matrix, origin_atom_id, thr=1e-5):
7
+ """
8
+ Cut the exchange parameters
9
+ :param path: the original TB2J_results path
10
+ :param output_path: the output path.
11
+ :param sc_matrix: the matrix which maps the primitive cell to supercell.
12
+ :param origin: the origin of the primitive cell.
13
+ :param thr: the atoms which the reduced position is within -thr to 1.0+thr are considered as inside the primitive atoms
14
+ :returns:
15
+ """
16
+ sc_matrix = np.asarray(sc_matrix, dtype=int)
17
+ sc_excparams = SpinIO.load_pickle(path=path, fname='TB2J.pickle')
18
+ sc_atoms = sc_excparams.atoms
19
+ uc_atoms, ids = find_primitive_cell(sc_atoms,
20
+ sc_matrix,
21
+ origin_atom_id=origin_atom_id,
22
+ thr=thr,
23
+ perfect=False)
24
+ uc_charges = sc_excparams.charges[ids]
25
+ uc_spinat = sc_excparams.spinat[ids]
26
+ indmap, Rmap = map_to_primitive(sc_atoms, uc_atoms)
27
+
28
+ # TODO index_spin: {iatom: ispin}
29
+
30
+ # list of iatom for each spin index.
31
+ uc_index_spin = [sc_excparams.index_spin[i] for i in ids]
32
+
33
+ uc_ind_atoms = {}
34
+ for iatom, ispin in enumerate(uc_index_spin):
35
+ if ispin >= 0:
36
+ uc_ind_atoms[ispin] = iatom
37
+
38
+ uc_Jdict = {}
39
+ uc_Rset = set()
40
+ for key, val in sc_excparams.exchange_Jdict.items():
41
+ R, ispin, jspin = key
42
+ iatom = sc_excparams.ind_atoms[ispin]
43
+ jatom = sc_excparams.ind_atoms[jspin]
44
+ uc_ispin = uc_index_spin[indmap[iatom]]
45
+ uc_jspin = uc_index_spin[indmap[jatom]]
46
+ uc_R = R @ sc_matrix + Rmap[jatom] - Rmap[iatom]
47
+ uc_R = tuple(uc_R)
48
+ if iatom in ids:
49
+ #print(f"{iatom=}, {indmap[iatom]=},{uc_ispin=}, {uc_jspin=}, {uc_R=} ")
50
+ uc_Jdict[(uc_R, uc_ispin, uc_jspin)] = val
51
+ uc_Rset.add(uc_R)
52
+
53
+ uc_distance_dict = gen_distance_dict(uc_ind_atoms, uc_atoms, list(uc_Rset))
54
+
55
+ assert sc_excparams.colinear, "Cut supercell for non-collinear spin is not yet implemented."
56
+
57
+ uc_exc = SpinIO(uc_atoms,
58
+ spinat=uc_spinat,
59
+ charges=uc_charges,
60
+ index_spin=uc_index_spin,
61
+ colinear=sc_excparams.colinear,
62
+ distance_dict=uc_distance_dict,
63
+ exchange_Jdict=uc_Jdict,
64
+ description="Cutted")
65
+
66
+ uc_exc.write_all(output_path)
67
+
68
+
69
+ def run_cut_cell(
70
+ path="TB2J_results",
71
+ output_path="./TB2J_cutted",
72
+ sc_matrix=np.array([[1, 1, 0], [-1, 1, 0], [0, 0, 2]]),
73
+ ):
74
+ cut_cell(path,
75
+ output_path,
76
+ np.array(sc_matrix),
77
+ origin_atom_id=0,
78
+ thr=1e-19)
79
+
80
+
81
+ if __name__ == "__main__":
82
+ run_cut_cell()
TB2J/exchange.py CHANGED
@@ -1,6 +1,9 @@
1
1
  from collections import defaultdict, OrderedDict
2
2
  import os
3
3
  import numpy as np
4
+ import pickle
5
+ from dataclasses import dataclass
6
+ import yaml
4
7
  from TB2J.green import TBGreen
5
8
  from TB2J.pauli import pauli_block_all, pauli_block_sigma_norm, pauli_mat
6
9
  from TB2J.utils import symbol_number, kmesh_to_R
@@ -10,25 +13,47 @@ from TB2J.external import p_map
10
13
  from TB2J.contour import Contour
11
14
  from TB2J.utils import simpson_nonuniform, trapezoidal_nonuniform, split_symbol_number
12
15
  from TB2J.orbmap import map_orbs_matrix
13
- import pickle
16
+ from typing import List, Tuple
14
17
 
15
18
 
19
+ @dataclass
16
20
  class ExchangeParams:
21
+ """
22
+ A class to store the parameters for exchange calculation.
23
+ """
24
+
25
+ efermi: float
26
+ basis: list = None
27
+ magnetic_elements: list = None
28
+ include_orbs = {}
29
+ _kmesh = [4, 4, 4]
30
+ emin: float = -15
31
+ emax: float = 0.05
32
+ nz: int = 100
33
+ exclude_orbs = []
34
+ ne: int = None
35
+ Rcut: float = None
36
+ _use_cache: bool = False
37
+ np: int = 1
38
+ description: str = ""
39
+ write_density_matrix: bool = False
40
+ orb_decomposition: bool = False
41
+ output_path: str = "TB2J_results"
42
+ mae_angles = None
43
+
17
44
  def __init__(
18
45
  self,
19
- efermi,
46
+ efermi=-10.0,
20
47
  basis=None,
21
- magnetic_elements=[],
22
- include_orbs={},
48
+ magnetic_elements=None,
49
+ include_orbs=None,
23
50
  kmesh=[4, 4, 4],
24
- emin=-15, # integration lower bound, relative to fermi energy
25
- # integration upper bound. Should be 0 (fermi energy). But DFT codes define Fermi energy in various ways.
51
+ emin=-15,
26
52
  emax=0.05,
27
53
  nz=100,
28
- # the delta in the (i delta) in green's function to prevent divergence
29
- exclude_orbs=[], #
30
- ne=None, # number of electrons in Wannier function.
31
- Rcut=None, # Rcut.
54
+ exclude_orbs=[],
55
+ ne=None,
56
+ Rcut=None,
32
57
  use_cache=False,
33
58
  np=1,
34
59
  description="",
@@ -37,70 +62,41 @@ class ExchangeParams:
37
62
  output_path="TB2J_results",
38
63
  ):
39
64
  self.efermi = efermi
40
- self.emin = emin
41
- self.emax = emax
42
- self.nz = nz
43
- self.Rcut = Rcut
44
65
  self.basis = basis
45
66
  self.magnetic_elements = magnetic_elements
46
67
  self.include_orbs = include_orbs
47
-
68
+ self._kmesh = kmesh
69
+ self.emin = emin
70
+ self.emax = emax
71
+ self.nz = nz
48
72
  self.exclude_orbs = exclude_orbs
49
73
  self.ne = ne
74
+ self.Rcut = Rcut
50
75
  self._use_cache = use_cache
51
76
  self.np = np
52
- self._kmesh = kmesh
53
- self.orb_decomposition = orb_decomposition
54
- self.write_density_matrix = write_density_matrix
55
77
  self.description = description
78
+ self.write_density_matrix = write_density_matrix
79
+ self.orb_decomposition = orb_decomposition
56
80
  self.output_path = output_path
57
81
 
82
+ def set_params(self, **kwargs):
83
+ for key, val in kwargs.items():
84
+ setattr(self, key, val)
85
+
86
+ def save_to_yaml(self, fname):
87
+ with open(fname, "w") as myfile:
88
+ yaml.dump(self.__dict__, myfile)
89
+
58
90
 
59
91
  class Exchange(ExchangeParams):
60
92
  def __init__(
61
93
  self,
62
94
  tbmodels,
63
95
  atoms,
64
- efermi,
65
- basis=None,
66
- magnetic_elements=[],
67
- include_orbs={},
68
- kmesh=[4, 4, 4],
69
- emin=-15, # integration lower bound, relative to fermi energy
70
- # integration upper bound. Should be 0 (fermi energy). But DFT codes define Fermi energy in various ways.
71
- emax=0.05,
72
- nz=100,
73
- # the delta in the (i delta) in green's function to prevent divergence
74
- exclude_orbs=[], #
75
- ne=None, # number of electrons in Wannier function.
76
- Rcut=None, # Rcut.
77
- use_cache=False,
78
- np=1,
79
- description="",
80
- write_density_matrix=False,
81
- output_path="TB2J_results",
82
- orb_decomposition=False,
96
+ **params: ExchangeParams,
83
97
  ):
84
98
  self.atoms = atoms
85
- super().__init__(
86
- efermi=efermi,
87
- basis=basis,
88
- magnetic_elements=magnetic_elements,
89
- include_orbs=include_orbs,
90
- kmesh=kmesh,
91
- emin=emin,
92
- emax=emax,
93
- nz=nz,
94
- exclude_orbs=exclude_orbs,
95
- ne=ne,
96
- Rcut=Rcut,
97
- use_cache=use_cache,
98
- np=np,
99
- description=description,
100
- write_density_matrix=write_density_matrix,
101
- orb_decomposition=orb_decomposition,
102
- output_path=output_path,
103
- )
99
+ super().__init__(**params)
104
100
  self._prepare_kmesh(self._kmesh)
105
101
  self._prepare_Rlist()
106
102
  self.set_tbmodels(tbmodels)
@@ -334,7 +330,6 @@ class ExchangeNCL(Exchange):
334
330
  """
335
331
  self.tbmodel = tbmodels
336
332
  self.backend_name = self.tbmodel.name
337
- # TODO: check if tbmodels are really a tbmodel with SOC.
338
333
  self.G = TBGreen(
339
334
  self.tbmodel,
340
335
  self.kmesh,
@@ -355,6 +350,12 @@ class ExchangeNCL(Exchange):
355
350
  if self.write_density_matrix:
356
351
  self.G.write_rho_R()
357
352
 
353
+ def get_MAE(self, thetas, phis):
354
+ """
355
+ Calculate the magnetic anisotropy energy.
356
+ """
357
+ pass
358
+
358
359
  def _prepare_NijR(self):
359
360
  self.N = {}
360
361
  for R in self.Rlist:
@@ -615,7 +616,7 @@ class ExchangeNCL(Exchange):
615
616
  def calculate_DMI_NJT(self):
616
617
  """
617
618
  calculate exchange and DMI with the
618
- D(i,j) =
619
+ This did not work and should be removed.
619
620
  """
620
621
  Ddict_NJT = {}
621
622
  Jdict_NJT = {}
@@ -679,10 +680,36 @@ class ExchangeNCL(Exchange):
679
680
  self.contour.path, AijRs_orb[(R, iatom, jatom)]
680
681
  )
681
682
 
682
- def get_AijR(self, e):
683
- GR = self.G.get_GR(self.short_Rlist, energy=e, get_rho=False)
683
+ def get_quantities_per_e(self, e):
684
+ Gk_all = self.G.get_Gk_all(e)
685
+ mae = self.get_mae_kspace(Gk_all)
686
+ # TODO: get the MAE from Gk_all
687
+ GR = self.G.get_GR(self.short_Rlist, energy=e, get_rho=False, Gk_all=Gk_all)
688
+ # TODO: define the quantities for one energy.
684
689
  AijR, AijR_orb = self.get_all_A(GR)
685
- return AijR, AijR_orb
690
+ return dict(AijR=AijR, AijR_orb=AijR_orb, mae=mae)
691
+
692
+ def get_mae_kspace(self, Gk_all):
693
+ """
694
+ get the MAE from Gk_all
695
+ TODO: This is only a place holder.
696
+ """
697
+ return None
698
+ Hso_k = self.model.get_Hso_k(k)
699
+ mae_e = np.zeros(len(self.mae_angles), dtype=complex)
700
+ # rotate the Hso_k to angles
701
+ for ik, k in enumerate(self.G.kpoints):
702
+ Gk = Gk_all[ik]
703
+ for i_angle, angle in enumerate(self.mae_angles):
704
+ # TODO: implementa rotate_H
705
+ Hso_k_rot = rotate_H(Hso_k, angle)
706
+ mae_e[i_angle] = Gk @ Hso_k @ Gk @ Hso_k
707
+ return mae_e
708
+
709
+ def get_mae_rspace(self, GR):
710
+ """
711
+ get the MAE from GR
712
+ """
686
713
 
687
714
  def save_AijR(self, AijRs, fname):
688
715
  result = dict(path=self.contour.path, AijRs=AijRs)
@@ -693,6 +720,7 @@ class ExchangeNCL(Exchange):
693
720
  """
694
721
  Do some sanity check before proceding.
695
722
  """
723
+ pass
696
724
 
697
725
  def calculate_all(self):
698
726
  """
@@ -701,40 +729,38 @@ class ExchangeNCL(Exchange):
701
729
  print("Green's function Calculation started.")
702
730
 
703
731
  AijRs = {}
704
-
705
732
  AijRs_orb = {}
706
733
 
707
734
  self.validate()
708
735
 
709
736
  npole = len(self.contour.path)
710
737
  if self.np > 1:
711
- results = p_map(self.get_AijR, self.contour.path, num_cpus=self.np)
738
+ results = p_map(
739
+ self.get_quantities_per_e, self.contour.path, num_cpus=self.np
740
+ )
712
741
  else:
713
- results = map(self.get_AijR, tqdm(self.contour.path, total=npole))
742
+ results = map(
743
+ self.get_quantities_per_e, tqdm(self.contour.path, total=npole)
744
+ )
714
745
 
715
746
  for i, result in enumerate(results):
716
747
  for iR, R in enumerate(self.R_ijatom_dict):
717
748
  for iatom, jatom in self.R_ijatom_dict[R]:
718
749
  if (R, iatom, jatom) in AijRs:
719
- AijRs[(R, iatom, jatom)].append(result[0][R, iatom, jatom])
750
+ AijRs[(R, iatom, jatom)].append(result["AijR"][R, iatom, jatom])
720
751
  if self.orb_decomposition:
721
752
  AijRs_orb[(R, iatom, jatom)].append(
722
- result[1][R, iatom, jatom]
753
+ result["AijR_orb"][R, iatom, jatom]
723
754
  )
724
755
 
725
756
  else:
726
757
  AijRs[(R, iatom, jatom)] = []
727
- AijRs[(R, iatom, jatom)].append(result[0][R, iatom, jatom])
758
+ AijRs[(R, iatom, jatom)].append(result["AijR"][R, iatom, jatom])
728
759
  if self.orb_decomposition:
729
760
  AijRs_orb[(R, iatom, jatom)] = []
730
761
  AijRs_orb[(R, iatom, jatom)].append(
731
- result[1][R, iatom, jatom]
762
+ result["AijR_orb"][R, iatom, jatom]
732
763
  )
733
- if self.np > 1:
734
- # executor.close()
735
- # executor.join()
736
- # executor.clear()
737
- pass
738
764
 
739
765
  # self.save_AijRs(AijRs)
740
766
  self.integrate(AijRs, AijRs_orb)