TB2J 0.9.5rc0__tar.gz → 0.9.7rc0__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.
Files changed (105) hide show
  1. {tb2j-0.9.5rc0 → tb2j-0.9.7rc0}/PKG-INFO +5 -3
  2. {tb2j-0.9.5rc0 → tb2j-0.9.7rc0}/TB2J/MAE.py +144 -27
  3. tb2j-0.9.7rc0/TB2J/MAEGreen.py +84 -0
  4. tb2j-0.9.7rc0/TB2J/anisotropy.py +672 -0
  5. {tb2j-0.9.5rc0 → tb2j-0.9.7rc0}/TB2J/contour.py +8 -0
  6. {tb2j-0.9.5rc0 → tb2j-0.9.7rc0}/TB2J/exchange.py +38 -172
  7. {tb2j-0.9.5rc0 → tb2j-0.9.7rc0}/TB2J/exchangeCL2.py +11 -13
  8. tb2j-0.9.7rc0/TB2J/exchange_params.py +213 -0
  9. {tb2j-0.9.5rc0 → tb2j-0.9.7rc0}/TB2J/green.py +62 -27
  10. tb2j-0.9.7rc0/TB2J/interfaces/__init__.py +12 -0
  11. tb2j-0.9.7rc0/TB2J/interfaces/abacus/__init__.py +4 -0
  12. {tb2j-0.9.5rc0/TB2J → tb2j-0.9.7rc0/TB2J/interfaces}/abacus/abacus_api.py +3 -3
  13. {tb2j-0.9.5rc0/TB2J → tb2j-0.9.7rc0/TB2J/interfaces}/abacus/abacus_wrapper.py +11 -8
  14. {tb2j-0.9.5rc0/TB2J → tb2j-0.9.7rc0/TB2J/interfaces}/abacus/gen_exchange_abacus.py +5 -3
  15. {tb2j-0.9.5rc0/TB2J → tb2j-0.9.7rc0/TB2J/interfaces}/abacus/orbital_api.py +4 -4
  16. {tb2j-0.9.5rc0/TB2J → tb2j-0.9.7rc0/TB2J/interfaces}/abacus/stru_api.py +11 -11
  17. {tb2j-0.9.5rc0/TB2J → tb2j-0.9.7rc0/TB2J/interfaces}/abacus/test_read_HRSR.py +0 -1
  18. {tb2j-0.9.5rc0/TB2J → tb2j-0.9.7rc0/TB2J/interfaces}/abacus/test_read_stru.py +5 -3
  19. tb2j-0.9.7rc0/TB2J/interfaces/gpaw_interface.py +54 -0
  20. tb2j-0.9.7rc0/TB2J/interfaces/lawaf_interface.py +129 -0
  21. tb2j-0.9.7rc0/TB2J/interfaces/manager.py +23 -0
  22. tb2j-0.9.7rc0/TB2J/interfaces/siesta_interface.py +202 -0
  23. tb2j-0.9.7rc0/TB2J/interfaces/wannier90_interface.py +115 -0
  24. {tb2j-0.9.5rc0 → tb2j-0.9.7rc0}/TB2J/io_exchange/io_exchange.py +21 -7
  25. {tb2j-0.9.5rc0 → tb2j-0.9.7rc0}/TB2J/io_merge.py +2 -1
  26. tb2j-0.9.7rc0/TB2J/mathutils/fermi.py +27 -0
  27. tb2j-0.9.7rc0/TB2J/mathutils/lowdin.py +22 -0
  28. tb2j-0.9.7rc0/TB2J/mathutils/rotate_spin.py +274 -0
  29. {tb2j-0.9.5rc0 → tb2j-0.9.7rc0}/TB2J/pauli.py +11 -3
  30. tb2j-0.9.7rc0/TB2J/symmetrize_J.py +120 -0
  31. {tb2j-0.9.5rc0 → tb2j-0.9.7rc0}/TB2J/utils.py +82 -1
  32. {tb2j-0.9.5rc0 → tb2j-0.9.7rc0}/TB2J.egg-info/PKG-INFO +5 -3
  33. {tb2j-0.9.5rc0 → tb2j-0.9.7rc0}/TB2J.egg-info/SOURCES.txt +20 -10
  34. tb2j-0.9.7rc0/TB2J.egg-info/entry_points.txt +3 -0
  35. {tb2j-0.9.5rc0 → tb2j-0.9.7rc0}/TB2J.egg-info/requires.txt +4 -2
  36. {tb2j-0.9.5rc0 → tb2j-0.9.7rc0}/scripts/abacus2J.py +5 -4
  37. {tb2j-0.9.5rc0 → tb2j-0.9.7rc0}/scripts/siesta2J.py +21 -4
  38. tb2j-0.9.7rc0/scripts/wann2J.py +96 -0
  39. {tb2j-0.9.5rc0 → tb2j-0.9.7rc0}/setup.py +12 -5
  40. tb2j-0.9.5rc0/TB2J/abacus/__init__.py +0 -1
  41. tb2j-0.9.5rc0/TB2J/manager.py +0 -445
  42. tb2j-0.9.5rc0/TB2J/mathutils/fermi.py +0 -22
  43. tb2j-0.9.5rc0/TB2J/mathutils/lowdin.py +0 -12
  44. tb2j-0.9.5rc0/TB2J/mathutils/rotate_spin.py +0 -56
  45. tb2j-0.9.5rc0/scripts/wann2J.py +0 -194
  46. {tb2j-0.9.5rc0 → tb2j-0.9.7rc0}/LICENSE +0 -0
  47. {tb2j-0.9.5rc0 → tb2j-0.9.7rc0}/README.md +0 -0
  48. {tb2j-0.9.5rc0 → tb2j-0.9.7rc0}/TB2J/Jdownfolder.py +0 -0
  49. {tb2j-0.9.5rc0 → tb2j-0.9.7rc0}/TB2J/Jtensor.py +0 -0
  50. {tb2j-0.9.5rc0 → tb2j-0.9.7rc0}/TB2J/Oiju.py +0 -0
  51. {tb2j-0.9.5rc0 → tb2j-0.9.7rc0}/TB2J/Oiju_epc.py +0 -0
  52. {tb2j-0.9.5rc0 → tb2j-0.9.7rc0}/TB2J/__init__.py +0 -0
  53. {tb2j-0.9.5rc0 → tb2j-0.9.7rc0}/TB2J/basis.py +0 -0
  54. {tb2j-0.9.5rc0 → tb2j-0.9.7rc0}/TB2J/citation.py +0 -0
  55. {tb2j-0.9.5rc0 → tb2j-0.9.7rc0}/TB2J/density_matrix.py +0 -0
  56. {tb2j-0.9.5rc0 → tb2j-0.9.7rc0}/TB2J/epc.py +0 -0
  57. {tb2j-0.9.5rc0 → tb2j-0.9.7rc0}/TB2J/exchange_pert.py +0 -0
  58. {tb2j-0.9.5rc0 → tb2j-0.9.7rc0}/TB2J/exchange_qspace.py +0 -0
  59. {tb2j-0.9.5rc0 → tb2j-0.9.7rc0}/TB2J/external/__init__.py +0 -0
  60. {tb2j-0.9.5rc0 → tb2j-0.9.7rc0}/TB2J/external/p_tqdm.py +0 -0
  61. {tb2j-0.9.5rc0 → tb2j-0.9.7rc0}/TB2J/gpaw_wrapper.py +0 -0
  62. {tb2j-0.9.5rc0 → tb2j-0.9.7rc0}/TB2J/greentest.py +0 -0
  63. {tb2j-0.9.5rc0/TB2J → tb2j-0.9.7rc0/TB2J/interfaces}/abacus/test_density_matrix.py +1 -1
  64. {tb2j-0.9.5rc0 → tb2j-0.9.7rc0}/TB2J/io_exchange/__init__.py +0 -0
  65. {tb2j-0.9.5rc0 → tb2j-0.9.7rc0}/TB2J/io_exchange/io_multibinit.py +0 -0
  66. {tb2j-0.9.5rc0 → tb2j-0.9.7rc0}/TB2J/io_exchange/io_tomsasd.py +0 -0
  67. {tb2j-0.9.5rc0 → tb2j-0.9.7rc0}/TB2J/io_exchange/io_txt.py +0 -0
  68. {tb2j-0.9.5rc0 → tb2j-0.9.7rc0}/TB2J/io_exchange/io_uppasd.py +0 -0
  69. {tb2j-0.9.5rc0 → tb2j-0.9.7rc0}/TB2J/io_exchange/io_vampire.py +0 -0
  70. {tb2j-0.9.5rc0 → tb2j-0.9.7rc0}/TB2J/kpoints.py +0 -0
  71. {tb2j-0.9.5rc0 → tb2j-0.9.7rc0}/TB2J/mathutils/__init__.py +0 -0
  72. {tb2j-0.9.5rc0 → tb2j-0.9.7rc0}/TB2J/mathutils/kR_convert.py +0 -0
  73. {tb2j-0.9.5rc0 → tb2j-0.9.7rc0}/TB2J/myTB.py +0 -0
  74. {tb2j-0.9.5rc0 → tb2j-0.9.7rc0}/TB2J/orbmap.py +0 -0
  75. {tb2j-0.9.5rc0 → tb2j-0.9.7rc0}/TB2J/pert.py +0 -0
  76. {tb2j-0.9.5rc0 → tb2j-0.9.7rc0}/TB2J/plot.py +0 -0
  77. {tb2j-0.9.5rc0 → tb2j-0.9.7rc0}/TB2J/rotate_atoms.py +0 -0
  78. {tb2j-0.9.5rc0 → tb2j-0.9.7rc0}/TB2J/rotate_siestaDM.py +0 -0
  79. {tb2j-0.9.5rc0 → tb2j-0.9.7rc0}/TB2J/sisl_wrapper.py +0 -0
  80. {tb2j-0.9.5rc0 → tb2j-0.9.7rc0}/TB2J/spinham/__init__.py +0 -0
  81. {tb2j-0.9.5rc0 → tb2j-0.9.7rc0}/TB2J/spinham/base_parser.py +0 -0
  82. {tb2j-0.9.5rc0 → tb2j-0.9.7rc0}/TB2J/spinham/constants.py +0 -0
  83. {tb2j-0.9.5rc0 → tb2j-0.9.7rc0}/TB2J/spinham/hamiltonian.py +0 -0
  84. {tb2j-0.9.5rc0 → tb2j-0.9.7rc0}/TB2J/spinham/hamiltonian_terms.py +0 -0
  85. {tb2j-0.9.5rc0 → tb2j-0.9.7rc0}/TB2J/spinham/plot.py +0 -0
  86. {tb2j-0.9.5rc0 → tb2j-0.9.7rc0}/TB2J/spinham/qsolver.py +0 -0
  87. {tb2j-0.9.5rc0 → tb2j-0.9.7rc0}/TB2J/spinham/spin_api.py +0 -0
  88. {tb2j-0.9.5rc0 → tb2j-0.9.7rc0}/TB2J/spinham/spin_xml.py +0 -0
  89. {tb2j-0.9.5rc0 → tb2j-0.9.7rc0}/TB2J/spinham/supercell.py +0 -0
  90. {tb2j-0.9.5rc0 → tb2j-0.9.7rc0}/TB2J/tensor_rotate.py +0 -0
  91. {tb2j-0.9.5rc0 → tb2j-0.9.7rc0}/TB2J/utest.py +0 -0
  92. {tb2j-0.9.5rc0 → tb2j-0.9.7rc0}/TB2J/versioninfo.py +0 -0
  93. {tb2j-0.9.5rc0 → tb2j-0.9.7rc0}/TB2J/wannier/__init__.py +0 -0
  94. {tb2j-0.9.5rc0 → tb2j-0.9.7rc0}/TB2J/wannier/w90_parser.py +0 -0
  95. {tb2j-0.9.5rc0 → tb2j-0.9.7rc0}/TB2J/wannier/w90_tb_parser.py +0 -0
  96. {tb2j-0.9.5rc0 → tb2j-0.9.7rc0}/TB2J.egg-info/dependency_links.txt +0 -0
  97. {tb2j-0.9.5rc0 → tb2j-0.9.7rc0}/TB2J.egg-info/top_level.txt +0 -0
  98. {tb2j-0.9.5rc0 → tb2j-0.9.7rc0}/scripts/TB2J_downfold.py +0 -0
  99. {tb2j-0.9.5rc0 → tb2j-0.9.7rc0}/scripts/TB2J_eigen.py +0 -0
  100. {tb2j-0.9.5rc0 → tb2j-0.9.7rc0}/scripts/TB2J_magnon.py +0 -0
  101. {tb2j-0.9.5rc0 → tb2j-0.9.7rc0}/scripts/TB2J_magnon_dos.py +0 -0
  102. {tb2j-0.9.5rc0 → tb2j-0.9.7rc0}/scripts/TB2J_merge.py +0 -0
  103. {tb2j-0.9.5rc0 → tb2j-0.9.7rc0}/scripts/TB2J_rotate.py +0 -0
  104. {tb2j-0.9.5rc0 → tb2j-0.9.7rc0}/scripts/TB2J_rotateDM.py +0 -0
  105. {tb2j-0.9.5rc0 → tb2j-0.9.7rc0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: TB2J
3
- Version: 0.9.5rc0
3
+ Version: 0.9.7rc0
4
4
  Summary: TB2J: First principle to Heisenberg exchange J using tight-binding Green function method
5
5
  Author: Xu He
6
6
  Author-email: mailhexu@gmail.com
@@ -14,13 +14,15 @@ Classifier: Topic :: Scientific/Engineering :: Physics
14
14
  Classifier: License :: OSI Approved :: BSD License
15
15
  Requires-Python: >=3.6
16
16
  License-File: LICENSE
17
- Requires-Dist: numpy>1.16.5
17
+ Requires-Dist: numpy<2.0
18
18
  Requires-Dist: scipy
19
19
  Requires-Dist: matplotlib
20
20
  Requires-Dist: ase>=3.19
21
21
  Requires-Dist: tqdm
22
22
  Requires-Dist: pathos
23
23
  Requires-Dist: packaging>=20.0
24
- Requires-Dist: HamiltonIO>=0.1.5
24
+ Requires-Dist: HamiltonIO>=0.1.7
25
+ Requires-Dist: pre-commit
26
+ Requires-Dist: sympair>0.1.0
25
27
 
26
28
  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.
@@ -1,20 +1,26 @@
1
+ from pathlib import Path
2
+
3
+ import matplotlib.pyplot as plt
1
4
  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
5
+ import tqdm
6
+
7
+ # from TB2J.abacus.abacus_wrapper import AbacusSplitSOCParser
8
+ from HamiltonIO.abacus.abacus_wrapper import AbacusSplitSOCParser
9
+ from HamiltonIO.model.occupations import Occupations
10
+ from HamiltonIO.siesta import SislParser
11
+ from scipy.linalg import eigh
12
+
13
+ from TB2J.contour import Contour
14
+ from TB2J.green import TBGreen
15
+
16
+ # from HamiltonIO.model.rotate_spin import rotate_Matrix_from_z_to_axis, rotate_Matrix_from_z_to_sperical
4
17
  from TB2J.kpoints import monkhorst_pack
5
- from TB2J.mathutils.fermi import fermi
6
18
  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.mathutils.rotate_spin import spherical_to_cartesian
13
- from HamiltonIO.model.occupations import Occupations
14
- #from TB2J.abacus.abacus_wrapper import AbacusSplitSOCParser
15
- from HamiltonIO.abacus.abacus_wrapper import AbacusSplitSOCParser
16
- from HamiltonIO.siesta import SislParser, SiestaHamiltonian
17
- import tqdm
19
+
20
+ # from TB2J.abacus.abacus_wrapper import AbacusWrapper, AbacusParser
21
+ from TB2J.mathutils.rotate_spin import (
22
+ rotate_spinor_matrix,
23
+ )
18
24
 
19
25
 
20
26
  def get_occupation(evals, kweights, nel, width=0.1):
@@ -29,12 +35,25 @@ def get_density_matrix(evals=None, evecs=None, kweights=None, nel=None, width=0.
29
35
 
30
36
 
31
37
  class MAE:
32
- def __init__(self, model, kmesh, gamma=True, width=0.1, nel=None):
38
+ def __init__(
39
+ self,
40
+ model,
41
+ kmesh=None,
42
+ gamma=True,
43
+ kpts=None,
44
+ kweights=None,
45
+ width=0.1,
46
+ nel=None,
47
+ ):
33
48
  self.model = model
34
49
  if nel is not None:
35
50
  self.model.nel = nel
36
- self.kpts = monkhorst_pack(kmesh, gamma_center=gamma)
37
- self.kweights = np.ones(len(self.kpts), dtype=float) / len(self.kpts)
51
+ if kpts is None:
52
+ self.kpts = monkhorst_pack(kmesh, gamma_center=gamma)
53
+ self.kweights = np.ones(len(self.kpts), dtype=float) / len(self.kpts)
54
+ else:
55
+ self.kpts = kpts
56
+ self.kweights = kweights
38
57
  self.width = width
39
58
 
40
59
  def get_band_energy(self):
@@ -87,39 +106,137 @@ class MAE:
87
106
  return es
88
107
 
89
108
 
109
+ class MAEGreen(MAE):
110
+ def __init__(
111
+ self,
112
+ model,
113
+ kmesh=None,
114
+ gamma=True,
115
+ kpts=None,
116
+ kweights=None,
117
+ nel=None,
118
+ width=0.1,
119
+ **kwargs,
120
+ ):
121
+ self.model = model
122
+ if nel is not None:
123
+ self.model.nel = nel
124
+ if kpts is None:
125
+ self.kpts = monkhorst_pack(kmesh, gamma_center=gamma)
126
+ self.kweights = np.ones(len(self.kpts), dtype=float) / len(self.kpts)
127
+ else:
128
+ self.kpts = kpts
129
+ self.kweights = kweights
130
+ self.width = width
131
+ model.set_so_strength(0.0)
132
+ evals, evecs = model.solve_all(self.kpts)
133
+ occ = Occupations(
134
+ nel=self.model.nel, width=self.width, wk=self.kweights, nspin=2
135
+ )
136
+ # occ.occupy(evals)
137
+ efermi = occ.efermi(evals)
138
+ print(f"{efermi=}")
139
+ self.G = TBGreen(model, kmesh, efermi=efermi, gamma=gamma, **kwargs)
140
+ self.emin = -12
141
+ self.emax = 0
142
+ self.nz = 50
143
+ self._prepare_elist()
144
+
145
+ def _prepare_elist(self, method="legendre"):
146
+ """
147
+ prepare list of energy for integration.
148
+ The path has three segments:
149
+ emin --1-> emin + 1j*height --2-> emax+1j*height --3-> emax
150
+ """
151
+ self.contour = Contour(self.emin, self.emax)
152
+ # if method.lower() == "rectangle":
153
+ # self.contour.build_path_rectangle(
154
+ # height=self.height, nz1=self.nz1, nz2=self.nz2, nz3=self.nz3
155
+ # )
156
+ if method.lower() == "semicircle":
157
+ self.contour.build_path_semicircle(npoints=self.nz, endpoint=True)
158
+ elif method.lower() == "legendre":
159
+ self.contour.build_path_legendre(npoints=self.nz, endpoint=True)
160
+ else:
161
+ raise ValueError(f"The path cannot be of type {method}.")
162
+
163
+ def get_efermi(self):
164
+ evals, evecs = self.model.solve_all(self.kpts)
165
+ occ = Occupations(
166
+ nel=self.model.nel, width=self.model.width, wk=self.kweights, nspin=2
167
+ )
168
+ occ.get_efermi(evals, self.kweights)
169
+
170
+ def get_perturbed(self, e, thetas, phis):
171
+ G0K = self.G.get_Gk_all(e)
172
+ Hsoc_k = self.model.get_Hk_soc(self.kpts)
173
+ dE_ang = []
174
+ for theta, phi in zip(thetas, phis):
175
+ dE = 0.0
176
+ for i, dHk in enumerate(Hsoc_k):
177
+ dHi = rotate_spinor_matrix(dHk, theta, phi)
178
+ GdH = G0K[i] @ dHi
179
+ # dE += np.trace(GdH @ G0K[i].T.conj() @ dHi) * self.kweights[i]
180
+ dE += np.trace(GdH @ GdH) * self.kweights[i]
181
+ dE_ang.append(dE)
182
+ return np.array(dE_ang)
183
+
184
+ def get_band_energy_vs_angles(self, thetas, phis):
185
+ es = np.zeros(len(thetas))
186
+ for ie, e in enumerate(tqdm.tqdm(self.contour.path)):
187
+ dE_angle = self.get_perturbed(e, thetas, phis)
188
+ es += np.imag(dE_angle * self.contour.weights[ie])
189
+ return -es / np.pi
190
+
191
+
90
192
  def get_model_energy(model, kmesh, gamma=True):
91
193
  ham = MAE(model, kmesh, gamma=gamma)
92
194
  return ham.get_band_energy()
93
195
 
94
196
 
95
197
  def abacus_get_MAE(
96
- path_nosoc, path_soc, kmesh, thetas, psis, gamma=True, outfile="MAE.txt", nel=None
198
+ path_nosoc,
199
+ path_soc,
200
+ kmesh,
201
+ thetas,
202
+ phis,
203
+ gamma=True,
204
+ outfile="MAE.txt",
205
+ nel=None,
206
+ width=0.1,
97
207
  ):
98
208
  """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."""
99
209
  parser = AbacusSplitSOCParser(
100
210
  outpath_nosoc=path_nosoc, outpath_soc=path_soc, binary=False
101
211
  )
102
212
  model = parser.parse()
103
- ham = MAE(model, kmesh, gamma=gamma)
104
- es = ham.get_band_energy_vs_angles(thetas, psis)
213
+ if nel is not None:
214
+ model.nel = nel
215
+ ham = MAEGreen(model, kmesh, gamma=gamma, width=width)
216
+ es = ham.get_band_energy_vs_angles(thetas, phis)
105
217
  if outfile:
106
218
  with open(outfile, "w") as f:
107
- f.write("#theta, psi, energy\n")
108
- for theta, psi, e in zip(thetas, psis, es):
109
- f.write(f"{theta:5.3f}, {psi:5.3f}, {e:10.9f}\n")
219
+ f.write("#theta, phi, energy\n")
220
+ for theta, phi, e in zip(thetas, phis, es):
221
+ f.write(f"{theta:5.3f}, {phi:5.3f}, {e:10.9f}\n")
110
222
  return es
111
223
 
112
224
 
113
- def siesta_get_MAE(fdf_fname, kmesh, thetas, phis, gamma=True, outfile="MAE.txt"):
225
+ def siesta_get_MAE(
226
+ fdf_fname, kmesh, thetas, phis, gamma=True, outfile="MAE.txt", nel=None, width=0.1
227
+ ):
114
228
  """ """
115
- model= SislParser(fdf_fname=fdf_fname, read_H_soc=True).get_model()
116
- ham = MAE(model, kmesh, gamma=gamma)
229
+ model = SislParser(fdf_fname=fdf_fname, read_H_soc=True).get_model()
230
+ if nel is not None:
231
+ model.nel = nel
232
+ ham = MAEGreen(model, kmesh, gamma=gamma, width=width)
233
+ # es = ham.get_band_energy_vs_angles(thetas, phis)
117
234
  es = ham.get_band_energy_vs_angles(thetas, phis)
118
235
  if outfile:
119
236
  with open(outfile, "w") as f:
120
237
  f.write("#theta, psi, energy\n")
121
238
  for theta, psi, e in zip(thetas, phis, es):
122
- #f.write(f"{theta}, {psi}, {e}\n")
239
+ # f.write(f"{theta}, {psi}, {e}\n")
123
240
  f.write(f"{theta:5.3f}, {psi:5.3f}, {e:10.9f}\n")
124
241
  return es
125
242
 
@@ -0,0 +1,84 @@
1
+ from pathlib import Path
2
+
3
+ import numpy as np
4
+ import tqdm
5
+
6
+ # from TB2J.abacus.abacus_wrapper import AbacusSplitSOCParser
7
+ from TB2J.exchange import ExchangeNCL
8
+
9
+ # from HamiltonIO.model.rotate_spin import rotate_Matrix_from_z_to_axis, rotate_Matrix_from_z_to_sperical
10
+ # from TB2J.abacus.abacus_wrapper import AbacusWrapper, AbacusParser
11
+ from TB2J.mathutils.rotate_spin import (
12
+ rotate_spinor_matrix,
13
+ )
14
+
15
+
16
+ class MAEGreen(ExchangeNCL):
17
+ def __init__(self, angles=None, **kwargs):
18
+ super().__init__(**kwargs)
19
+ self.es = None
20
+ self.natoms = len(self.atoms)
21
+ if angles is None or angles == "axis":
22
+ self.set_angles_axis()
23
+ elif angles == "scan":
24
+ self.set_angles_scan()
25
+
26
+ def set_angles_axis(self):
27
+ """theta and phi are defined as the x, y, z, axis."""
28
+ self.thetas = [0, np.pi / 2, np.pi / 2, np.pi / 2]
29
+ self.phis = [0, 0, np.pi / 2, np.pi / 4]
30
+
31
+ def set_angles_scan(self, step=15):
32
+ self.thetas = []
33
+ self.phis = []
34
+ for i in range(0, 181, step):
35
+ for j in range(0, 181, step):
36
+ self.thetas.append(i * np.pi / 180)
37
+ self.phis.append(j * np.pi / 180)
38
+
39
+ def get_perturbed(self, e, thetas, phis):
40
+ self.tbmodel.set_so_strength(0.0)
41
+ G0K = self.G.get_Gk_all(e)
42
+ Hsoc_k = self.tbmodel.get_Hk_soc(self.G.kpts)
43
+ na = len(thetas)
44
+ dE_angle = np.zeros(na, dtype=complex)
45
+ dE_angle_atoms = np.zeros((na, self.natoms), dtype=complex)
46
+ for iangle, (theta, phi) in enumerate(zip(thetas, phis)):
47
+ for ik, dHk in enumerate(Hsoc_k):
48
+ dHi = rotate_spinor_matrix(dHk, theta, phi)
49
+ GdH = G0K[ik] @ dHi
50
+ # dE += np.trace(GdH @ G0K[i].T.conj() @ dHi) * self.kweights[i]
51
+ # diagonal of second order perturbation.
52
+ dG2diag = np.diag(GdH @ GdH)
53
+ # dG2diag = np.diag(GdH @G0K[i].T.conj() @ dHi)
54
+ dE_angle[iangle] += np.sum(dG2diag) * self.G.kweights[ik]
55
+ for iatom in range(self.natoms):
56
+ dE_atom = np.sum(dG2diag[self.iorb(iatom)]) * self.G.kweights[ik]
57
+ dE_angle_atoms[iangle, iatom] += dE_atom
58
+ return dE_angle, dE_angle_atoms
59
+
60
+ def get_band_energy_vs_angles(self, thetas, phis):
61
+ nangles = len(thetas)
62
+ self.es = np.zeros(nangles, dtype=float)
63
+ self.es_atoms = np.zeros((nangles, self.natoms), dtype=float)
64
+ for ie, e in enumerate(tqdm.tqdm(self.contour.path)):
65
+ dE_angle, dE_angle_atoms = self.get_perturbed(e, thetas, phis)
66
+ self.es -= np.imag(dE_angle * self.contour.weights[ie]) / np.pi
67
+ self.es_atoms -= np.imag(dE_angle_atoms * self.contour.weights[ie]) / np.pi
68
+
69
+ def output(self, output_path="TB2J_anisotropy"):
70
+ Path(output_path).mkdir(exist_ok=True)
71
+ fname = f"{output_path}/MAE.dat"
72
+ with open(fname, "w") as f:
73
+ f.write("# theta phi MAE, MAEatom1, atom2, ...\n")
74
+ for i, (theta, phi, e, es) in enumerate(
75
+ zip(self.thetas, self.phis, self.es, self.es_atoms)
76
+ ):
77
+ f.write(f"{theta:.5f} {phi:.5f} {e:.8f} ")
78
+ for ea in es:
79
+ f.write(f"{ea:.8f} ")
80
+ f.write("\n")
81
+
82
+ def run(self, output_path="TB2J_anisotropy"):
83
+ self.get_band_energy_vs_angles(self.thetas, self.phis)
84
+ self.output()