TB2J 0.9.0.1__tar.gz → 0.9.2rc0__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 (90) hide show
  1. {TB2J-0.9.0.1 → tb2j-0.9.2rc0}/PKG-INFO +1 -1
  2. {TB2J-0.9.0.1 → tb2j-0.9.2rc0}/TB2J/Jdownfolder.py +110 -24
  3. {TB2J-0.9.0.1 → tb2j-0.9.2rc0}/TB2J/Jtensor.py +1 -1
  4. tb2j-0.9.2rc0/TB2J/MAE.py +185 -0
  5. {TB2J-0.9.0.1 → tb2j-0.9.2rc0}/TB2J/abacus/abacus_wrapper.py +103 -4
  6. tb2j-0.9.2rc0/TB2J/abacus/test_density_matrix.py +38 -0
  7. {TB2J-0.9.0.1 → tb2j-0.9.2rc0}/TB2J/exchange.py +99 -73
  8. {TB2J-0.9.0.1 → tb2j-0.9.2rc0}/TB2J/exchangeCL2.py +10 -5
  9. {TB2J-0.9.0.1 → tb2j-0.9.2rc0}/TB2J/green.py +14 -15
  10. {TB2J-0.9.0.1 → tb2j-0.9.2rc0}/TB2J/manager.py +10 -4
  11. tb2j-0.9.2rc0/TB2J/mathutils/__init__.py +1 -0
  12. tb2j-0.9.2rc0/TB2J/mathutils/fermi.py +22 -0
  13. tb2j-0.9.2rc0/TB2J/mathutils/kR_convert.py +90 -0
  14. tb2j-0.9.2rc0/TB2J/mathutils/rotate_spin.py +56 -0
  15. tb2j-0.9.2rc0/TB2J/patch.py +50 -0
  16. {TB2J-0.9.0.1 → tb2j-0.9.2rc0}/TB2J/pauli.py +17 -0
  17. {TB2J-0.9.0.1 → tb2j-0.9.2rc0}/TB2J.egg-info/PKG-INFO +1 -1
  18. {TB2J-0.9.0.1 → tb2j-0.9.2rc0}/TB2J.egg-info/SOURCES.txt +8 -1
  19. {TB2J-0.9.0.1 → tb2j-0.9.2rc0}/scripts/TB2J_downfold.py +8 -0
  20. {TB2J-0.9.0.1 → tb2j-0.9.2rc0}/setup.py +1 -1
  21. {TB2J-0.9.0.1 → tb2j-0.9.2rc0}/LICENSE +0 -0
  22. {TB2J-0.9.0.1 → tb2j-0.9.2rc0}/README.md +0 -0
  23. {TB2J-0.9.0.1 → tb2j-0.9.2rc0}/TB2J/Oiju.py +0 -0
  24. {TB2J-0.9.0.1 → tb2j-0.9.2rc0}/TB2J/Oiju_epc.py +0 -0
  25. {TB2J-0.9.0.1 → tb2j-0.9.2rc0}/TB2J/__init__.py +0 -0
  26. {TB2J-0.9.0.1 → tb2j-0.9.2rc0}/TB2J/abacus/__init__.py +0 -0
  27. {TB2J-0.9.0.1 → tb2j-0.9.2rc0}/TB2J/abacus/abacus_api.py +0 -0
  28. {TB2J-0.9.0.1 → tb2j-0.9.2rc0}/TB2J/abacus/gen_exchange_abacus.py +0 -0
  29. {TB2J-0.9.0.1 → tb2j-0.9.2rc0}/TB2J/abacus/orbital_api.py +0 -0
  30. {TB2J-0.9.0.1 → tb2j-0.9.2rc0}/TB2J/abacus/stru_api.py +0 -0
  31. {TB2J-0.9.0.1 → tb2j-0.9.2rc0}/TB2J/abacus/test_read_HRSR.py +0 -0
  32. {TB2J-0.9.0.1 → tb2j-0.9.2rc0}/TB2J/abacus/test_read_stru.py +0 -0
  33. {TB2J-0.9.0.1 → tb2j-0.9.2rc0}/TB2J/basis.py +0 -0
  34. {TB2J-0.9.0.1 → tb2j-0.9.2rc0}/TB2J/citation.py +0 -0
  35. {TB2J-0.9.0.1 → tb2j-0.9.2rc0}/TB2J/contour.py +0 -0
  36. {TB2J-0.9.0.1 → tb2j-0.9.2rc0}/TB2J/density_matrix.py +0 -0
  37. {TB2J-0.9.0.1 → tb2j-0.9.2rc0}/TB2J/epc.py +0 -0
  38. {TB2J-0.9.0.1 → tb2j-0.9.2rc0}/TB2J/exchange_pert.py +0 -0
  39. {TB2J-0.9.0.1 → tb2j-0.9.2rc0}/TB2J/exchange_qspace.py +0 -0
  40. {TB2J-0.9.0.1 → tb2j-0.9.2rc0}/TB2J/external/__init__.py +0 -0
  41. {TB2J-0.9.0.1 → tb2j-0.9.2rc0}/TB2J/external/p_tqdm.py +0 -0
  42. {TB2J-0.9.0.1 → tb2j-0.9.2rc0}/TB2J/gpaw_wrapper.py +0 -0
  43. {TB2J-0.9.0.1 → tb2j-0.9.2rc0}/TB2J/greentest.py +0 -0
  44. {TB2J-0.9.0.1 → tb2j-0.9.2rc0}/TB2J/io_exchange/__init__.py +0 -0
  45. {TB2J-0.9.0.1 → tb2j-0.9.2rc0}/TB2J/io_exchange/io_exchange.py +0 -0
  46. {TB2J-0.9.0.1 → tb2j-0.9.2rc0}/TB2J/io_exchange/io_multibinit.py +0 -0
  47. {TB2J-0.9.0.1 → tb2j-0.9.2rc0}/TB2J/io_exchange/io_tomsasd.py +0 -0
  48. {TB2J-0.9.0.1 → tb2j-0.9.2rc0}/TB2J/io_exchange/io_txt.py +0 -0
  49. {TB2J-0.9.0.1 → tb2j-0.9.2rc0}/TB2J/io_exchange/io_uppasd.py +0 -0
  50. {TB2J-0.9.0.1 → tb2j-0.9.2rc0}/TB2J/io_exchange/io_vampire.py +0 -0
  51. {TB2J-0.9.0.1 → tb2j-0.9.2rc0}/TB2J/io_merge.py +0 -0
  52. {TB2J-0.9.0.1 → tb2j-0.9.2rc0}/TB2J/kpoints.py +0 -0
  53. /TB2J-0.9.0.1/TB2J/mathutils.py → /tb2j-0.9.2rc0/TB2J/mathutils/lowdin.py +0 -0
  54. {TB2J-0.9.0.1 → tb2j-0.9.2rc0}/TB2J/myTB.py +0 -0
  55. {TB2J-0.9.0.1 → tb2j-0.9.2rc0}/TB2J/orbmap.py +0 -0
  56. {TB2J-0.9.0.1 → tb2j-0.9.2rc0}/TB2J/pert.py +0 -0
  57. {TB2J-0.9.0.1 → tb2j-0.9.2rc0}/TB2J/plot.py +0 -0
  58. {TB2J-0.9.0.1 → tb2j-0.9.2rc0}/TB2J/rotate_atoms.py +0 -0
  59. {TB2J-0.9.0.1 → tb2j-0.9.2rc0}/TB2J/rotate_siestaDM.py +0 -0
  60. {TB2J-0.9.0.1 → tb2j-0.9.2rc0}/TB2J/sisl_wrapper.py +0 -0
  61. {TB2J-0.9.0.1 → tb2j-0.9.2rc0}/TB2J/spinham/__init__.py +0 -0
  62. {TB2J-0.9.0.1 → tb2j-0.9.2rc0}/TB2J/spinham/base_parser.py +0 -0
  63. {TB2J-0.9.0.1 → tb2j-0.9.2rc0}/TB2J/spinham/constants.py +0 -0
  64. {TB2J-0.9.0.1 → tb2j-0.9.2rc0}/TB2J/spinham/hamiltonian.py +0 -0
  65. {TB2J-0.9.0.1 → tb2j-0.9.2rc0}/TB2J/spinham/hamiltonian_terms.py +0 -0
  66. {TB2J-0.9.0.1 → tb2j-0.9.2rc0}/TB2J/spinham/plot.py +0 -0
  67. {TB2J-0.9.0.1 → tb2j-0.9.2rc0}/TB2J/spinham/qsolver.py +0 -0
  68. {TB2J-0.9.0.1 → tb2j-0.9.2rc0}/TB2J/spinham/spin_api.py +0 -0
  69. {TB2J-0.9.0.1 → tb2j-0.9.2rc0}/TB2J/spinham/spin_xml.py +0 -0
  70. {TB2J-0.9.0.1 → tb2j-0.9.2rc0}/TB2J/spinham/supercell.py +0 -0
  71. {TB2J-0.9.0.1 → tb2j-0.9.2rc0}/TB2J/tensor_rotate.py +0 -0
  72. {TB2J-0.9.0.1 → tb2j-0.9.2rc0}/TB2J/utest.py +0 -0
  73. {TB2J-0.9.0.1 → tb2j-0.9.2rc0}/TB2J/utils.py +0 -0
  74. {TB2J-0.9.0.1 → tb2j-0.9.2rc0}/TB2J/versioninfo.py +0 -0
  75. {TB2J-0.9.0.1 → tb2j-0.9.2rc0}/TB2J/wannier/__init__.py +0 -0
  76. {TB2J-0.9.0.1 → tb2j-0.9.2rc0}/TB2J/wannier/w90_parser.py +0 -0
  77. {TB2J-0.9.0.1 → tb2j-0.9.2rc0}/TB2J/wannier/w90_tb_parser.py +0 -0
  78. {TB2J-0.9.0.1 → tb2j-0.9.2rc0}/TB2J.egg-info/dependency_links.txt +0 -0
  79. {TB2J-0.9.0.1 → tb2j-0.9.2rc0}/TB2J.egg-info/requires.txt +0 -0
  80. {TB2J-0.9.0.1 → tb2j-0.9.2rc0}/TB2J.egg-info/top_level.txt +0 -0
  81. {TB2J-0.9.0.1 → tb2j-0.9.2rc0}/scripts/TB2J_eigen.py +0 -0
  82. {TB2J-0.9.0.1 → tb2j-0.9.2rc0}/scripts/TB2J_magnon.py +0 -0
  83. {TB2J-0.9.0.1 → tb2j-0.9.2rc0}/scripts/TB2J_magnon_dos.py +0 -0
  84. {TB2J-0.9.0.1 → tb2j-0.9.2rc0}/scripts/TB2J_merge.py +0 -0
  85. {TB2J-0.9.0.1 → tb2j-0.9.2rc0}/scripts/TB2J_rotate.py +0 -0
  86. {TB2J-0.9.0.1 → tb2j-0.9.2rc0}/scripts/TB2J_rotateDM.py +0 -0
  87. {TB2J-0.9.0.1 → tb2j-0.9.2rc0}/scripts/abacus2J.py +0 -0
  88. {TB2J-0.9.0.1 → tb2j-0.9.2rc0}/scripts/siesta2J.py +0 -0
  89. {TB2J-0.9.0.1 → tb2j-0.9.2rc0}/scripts/wann2J.py +0 -0
  90. {TB2J-0.9.0.1 → tb2j-0.9.2rc0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: TB2J
3
- Version: 0.9.0.1
3
+ Version: 0.9.2rc0
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
@@ -17,9 +17,23 @@ def ind_to_indn(ind, n=3):
17
17
  return indn
18
18
 
19
19
 
20
+ class JR_model:
21
+ def __init__(self, JR, Rlist):
22
+ self.JR = JR
23
+ self.Rlist = Rlist
24
+ self.nR = len(Rlist)
25
+
26
+ def get_Jq(self, q):
27
+ Jq = np.zeros(self.JR[0].shape, dtype=complex)
28
+ for iR, R in enumerate(self.Rlist):
29
+ phase = np.exp(2.0j * np.pi * np.dot(q, R))
30
+ Jq += self.JR[iR] * phase
31
+ return Jq
32
+
33
+
20
34
  class JDownfolder:
21
35
  def __init__(self, JR, Rlist, iM, iL, qmesh, iso_only=False):
22
- self.JR = JR
36
+ self.model = JR_model(JR, Rlist)
23
37
  self.Rlist = Rlist
24
38
  self.nR = len(Rlist)
25
39
  self.nM = len(iM)
@@ -34,25 +48,18 @@ class JDownfolder:
34
48
  self.nLn = self.nL * 3
35
49
  self.iso_only = iso_only
36
50
 
37
- def get_Jq(self, q):
38
- Jq = np.zeros(self.JR[0].shape, dtype=complex)
39
- for iR, R in enumerate(self.Rlist):
40
- phase = np.exp(2.0j * np.pi * np.dot(q, R))
41
- Jq += self.JR[iR] * phase
42
- return Jq
43
-
44
51
  def get_JR(self):
45
52
  JR_downfolded = np.zeros((self.nR, self.nMn, self.nMn), dtype=float)
46
53
  Jq_downfolded = np.zeros((self.nqpt, self.nMn, self.nMn), dtype=complex)
47
54
  self.iMn = ind_to_indn(self.iM, n=3)
48
55
  self.iLn = ind_to_indn(self.iL, n=3)
49
56
  for iq, q in enumerate(self.qpts):
50
- Jq = self.get_Jq(q)
57
+ Jq = self.model.get_Jq(q)
51
58
  Jq_downfolded[iq] = self.downfold_oneq(Jq)
52
59
  for iR, R in enumerate(self.Rlist):
53
60
  phase = np.exp(-2.0j * np.pi * np.dot(q, R))
54
61
  JR_downfolded[iR] += np.real(Jq_downfolded[iq] * phase / self.nqpt)
55
- return JR_downfolded
62
+ return JR_downfolded, self.Rlist
56
63
 
57
64
  def downfold_oneq(self, J):
58
65
  JMM = J[np.ix_(self.iMn, self.iMn)]
@@ -63,17 +70,80 @@ class JDownfolder:
63
70
  return Jn
64
71
 
65
72
 
73
+ class PWFDownfolder:
74
+ def __init__(self, JR, Rlist, iM, iL, qmesh, atoms=None, iso_only=False, **kwargs):
75
+ from lawaf.interfaces.magnon.magnon_downfolder import (
76
+ MagnonWrapper,
77
+ MagnonDownfolder,
78
+ )
79
+
80
+ model = MagnonWrapper(JR, Rlist, atoms)
81
+ wann = MagnonDownfolder(model)
82
+ # Downfold the band structure.
83
+ index_basis = []
84
+ for i in iM:
85
+ index_basis += list(range(i * 3, i * 3 + 3))
86
+ params = dict(
87
+ method="projected",
88
+ # method="maxprojected",
89
+ kmesh=qmesh,
90
+ nwann=len(index_basis),
91
+ selected_basis=index_basis,
92
+ # anchors={(0, 0, 0): (-1, -2, -3, -4)},
93
+ # anchors={(0, 0, 0): ()},
94
+ # use_proj=True,
95
+ enhance_Amn=2.0,
96
+ )
97
+ params.update(kwargs)
98
+ wann.set_parameters(**params)
99
+ print("begin downfold")
100
+ ewf = wann.downfold()
101
+ ewf.save_hr_pickle("downfolded_JR.pickle")
102
+
103
+ # Plot the band structure.
104
+ wann.plot_band_fitting(
105
+ # kvectors=np.array([[0, 0, 0], [0.5, 0, 0],
106
+ # [0.5, 0.5, 0], [0, 0, 0],
107
+ # [.5, .5, .5]]),
108
+ # knames=['$\Gamma$', 'X', 'M', '$\Gamma$', 'R'],
109
+ cell=model.atoms.cell,
110
+ supercell_matrix=None,
111
+ npoints=100,
112
+ efermi=None,
113
+ erange=None,
114
+ fullband_color="blue",
115
+ downfolded_band_color="green",
116
+ marker="o",
117
+ ax=None,
118
+ savefig="downfold_band.png",
119
+ show=True,
120
+ )
121
+ self.JR_downfolded = ewf.HwannR
122
+ self.Rlist = ewf.Rlist
123
+
124
+ def get_JR(self):
125
+ return self.JR_downfolded, self.Rlist
126
+
127
+
66
128
  class JDownfolder_pickle:
67
129
  def __init__(
68
- self, inpath, metals, ligands, outpath, qmesh=[7, 7, 7], iso_only=False
130
+ self,
131
+ inpath,
132
+ metals,
133
+ ligands,
134
+ outpath,
135
+ qmesh=[7, 7, 7],
136
+ iso_only=False,
137
+ method="pwf",
138
+ **kwargs
69
139
  ):
70
140
  self.exc = SpinIO.load_pickle(path=inpath, fname="TB2J.pickle")
71
141
 
72
142
  self.iso_only = (self.exc.dmi_ddict is None) or iso_only
73
-
74
143
  self.metals = metals
75
144
  self.ligands = ligands
76
145
  self.outpath = outpath
146
+ self.method = method
77
147
 
78
148
  # read atomic structure
79
149
  self.atoms = self.exc.atoms
@@ -83,7 +153,8 @@ class JDownfolder_pickle:
83
153
  self.Rcut = None
84
154
  self._build_atom_index()
85
155
  self._prepare_distance()
86
- self._downfold()
156
+ Jd, Rlist = self._downfold(**kwargs)
157
+ self._Jd_to_exchange(Jd, Rlist)
87
158
 
88
159
  def _build_atom_index(self):
89
160
  self.magnetic_elements = self.metals
@@ -101,18 +172,33 @@ class JDownfolder_pickle:
101
172
  self.nL = len(self.iL)
102
173
  self.nsite = self.nM + self.nL
103
174
 
104
- def _downfold(self):
175
+ def _downfold(self, **kwargs):
105
176
  JR2 = self.exc.get_full_Jtensor_for_Rlist(asr=True)
106
- d = JDownfolder(
107
- JR2,
108
- self.exc.Rlist,
109
- iM=self.iM,
110
- iL=self.iL,
111
- qmesh=self.qmesh,
112
- iso_only=self.iso_only,
113
- )
114
- Jd = d.get_JR()
177
+ if self.method == "lowdin":
178
+ d = JDownfolder(
179
+ JR2,
180
+ self.exc.Rlist,
181
+ iM=self.iM,
182
+ iL=self.iL,
183
+ qmesh=self.qmesh,
184
+ iso_only=self.iso_only,
185
+ )
186
+ Jd, Rlist = d.get_JR()
187
+ else:
188
+ d = PWFDownfolder(
189
+ JR2,
190
+ self.exc.Rlist,
191
+ iM=self.iM,
192
+ iL=self.iL,
193
+ qmesh=self.qmesh,
194
+ atoms=self.atoms,
195
+ iso_only=self.iso_only,
196
+ **kwargs
197
+ )
198
+ Jd, Rlist = d.get_JR()
199
+ return Jd, Rlist
115
200
 
201
+ def _Jd_to_exchange(self, Jd, Rlist):
116
202
  self._prepare_distance()
117
203
  self._prepare_index_spin()
118
204
  self.Jdict = {}
@@ -123,7 +209,7 @@ class JDownfolder_pickle:
123
209
  self.DMIdict = {}
124
210
  self.Janidict = {}
125
211
 
126
- for iR, R in enumerate(d.Rlist):
212
+ for iR, R in enumerate(Rlist):
127
213
  for i, ispin in enumerate(self.index_spin):
128
214
  for j, jspin in enumerate(self.index_spin):
129
215
  if ispin >= 0 and jspin >= 0:
@@ -79,7 +79,7 @@ def combine_J_tensor(Jiso=0.0, D=np.zeros(3), Jani=np.zeros((3, 3), dtype=float)
79
79
  :param Jani: 3x3 matrix anisotropic exchange
80
80
  :returns: A 3x3 matrix, the exchange paraemter in tensor form.
81
81
  """
82
- Jtensor = np.zeros((3, 3), dtype=float)
82
+ Jtensor = np.zeros((3, 3), dtype=complex)
83
83
  if Jiso is not None:
84
84
  Jtensor += np.eye(3, dtype=float) * Jiso
85
85
  if Jani is not None:
@@ -0,0 +1,185 @@
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.mathutils.rotate_spin import spherical_to_cartesian
13
+ from HamiltonIO.occupations import Occupations
14
+ from TB2J.abacus.abacus_wrapper import AbacusSplitSOCParser
15
+ from HamiltonIO.siesta import SislWrapper
16
+ import tqdm
17
+
18
+
19
+ def get_occupation(evals, kweights, nel, width=0.1):
20
+ occ = Occupations(nel=nel, width=width, wk=kweights, nspin=2)
21
+ return occ.occupy(evals)
22
+
23
+
24
+ def get_density_matrix(evals=None, evecs=None, kweights=None, nel=None, width=0.1):
25
+ occ = get_occupation(evals, kweights, nel, width=width)
26
+ rho = np.einsum("kib, kb, kjb -> kij", evecs, occ, evecs.conj())
27
+ return rho
28
+
29
+
30
+ class MAE:
31
+ def __init__(self, model, kmesh, gamma=True, width=0.1, nel=None):
32
+ self.model = model
33
+ if nel is not None:
34
+ self.model.nel = nel
35
+ self.kpts = monkhorst_pack(kmesh, gamma_center=gamma)
36
+ self.kweights = np.ones(len(self.kpts), dtype=float) / len(self.kpts)
37
+ self.width = width
38
+
39
+ def get_band_energy(self):
40
+ evals, evecs = self.model.solve_all(self.kpts)
41
+ occ = get_occupation(evals, self.kweights, self.model.nel, width=self.width)
42
+ eband = np.sum(evals * occ * self.kweights[:, np.newaxis])
43
+ return eband
44
+
45
+ def calc_ref(self):
46
+ # calculate the Hk_ref, Sk_ref, Hk_soc_ref, and rho_ref
47
+ self.Sk_ref = R_to_k(self.kpts, self.model.Rlist, self.model.SR)
48
+ self.Hk_xc_ref = R_to_k(self.kpts, self.model.Rlist, self.model._HR_copy)
49
+ self.Hk_soc_ref = R_to_k(self.kpts, self.model.Rlist, self.model.HR_soc)
50
+ self.rho_ref = np.zeros(
51
+ (len(self.kpts), self.model.nbasis, self.model.nbasis), dtype=complex
52
+ )
53
+
54
+ evals = np.zeros((len(self.kpts), self.model.nbasis), dtype=float)
55
+ evecs = np.zeros(
56
+ (len(self.kpts), self.model.nbasis, self.model.nbasis), dtype=complex
57
+ )
58
+
59
+ for ik, kpt in enumerate(self.kpts):
60
+ # evals, evecs = eigh(self.Hk_xc_ref[ik]+self.Hk_soc_ref[ik], self.Sk_ref[ik])
61
+ evals[ik], evecs[ik] = eigh(self.Hk_xc_ref[ik], self.Sk_ref[ik])
62
+ occ = get_occupation(
63
+ evals, self.kweights, self.model.nel, width=self.model.width
64
+ )
65
+ # occ = fermi(evals, self.model.efermi, width=self.model.width)
66
+ self.rho_ref = np.einsum("kib, kb, kjb -> kij", evecs, occ, evecs.conj())
67
+
68
+ def get_band_energy_vs_angles(
69
+ self,
70
+ thetas,
71
+ phis,
72
+ ):
73
+ es = []
74
+ # es2 = []
75
+ # e,rho = self.model.get_band_energy(dm=True)
76
+ # self.calc_ref()
77
+ # thetas = np.linspace(*angle_range, npoints)
78
+ for i, (theta, phi) in tqdm.tqdm(enumerate(zip(thetas, phis))):
79
+ self.model.set_Hsoc_rotation_angle([theta, phi])
80
+ e = self.get_band_energy()
81
+ es.append(e)
82
+ # es2.append(e2)
83
+ return es
84
+
85
+
86
+ def get_model_energy(model, kmesh, gamma=True):
87
+ ham = MAE(model, kmesh, gamma=gamma)
88
+ return ham.get_band_energy()
89
+
90
+
91
+ def abacus_get_MAE(
92
+ path_nosoc, path_soc, kmesh, thetas, psis, gamma=True, outfile="MAE.txt", nel=None
93
+ ):
94
+ """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."""
95
+ parser = AbacusSplitSOCParser(
96
+ outpath_nosoc=path_nosoc, outpath_soc=path_soc, binary=False
97
+ )
98
+ model = parser.parse()
99
+ model.nel = 16
100
+ ham = MAE(model, kmesh, gamma=gamma)
101
+ es = ham.get_band_energy_vs_angles(thetas, psis)
102
+ if outfile:
103
+ with open(outfile, "w") as f:
104
+ f.write("#theta, psi, energy\n")
105
+ for theta, psi, e in zip(thetas, psis, es):
106
+ f.write(f"{theta}, {psi}, {e}\n")
107
+ return es
108
+
109
+
110
+ def siesta_get_MAE(fdf_fname, kmesh, thetas, phis, gamma=True, outfile="MAE.txt"):
111
+ """ """
112
+ parser = SiestaParser(fdf_fname=fdf_fname, read_H_soc=True)
113
+ model = parser.parse()
114
+ ham = MAE(model, kmesh, gamma=gamma)
115
+ es = ham.get_band_energy_vs_angles(thetas, phis)
116
+ if outfile:
117
+ with open(outfile, "w") as f:
118
+ f.write("#theta, psi, energy\n")
119
+ for theta, psi, e in zip(thetas, phis, es):
120
+ f.write(f"{theta}, {psi}, {e}\n")
121
+ return es
122
+
123
+
124
+ def test_AbacusSplitSOCWrapper():
125
+ # path = Path("~/projects/2D_Fe").expanduser()
126
+ path = Path("~/projects/TB2Jflows/examples/2D_Fe/Fe_z").expanduser()
127
+ outpath_nosoc = f"{path}/soc0/OUT.ABACUS"
128
+ outpath_soc = f"{path}/soc1/OUT.ABACUS"
129
+ parser = AbacusSplitSOCParser(
130
+ outpath_nosoc=outpath_nosoc, outpath_soc=outpath_soc, binary=False
131
+ )
132
+ model = parser.parse()
133
+ kmesh = [6, 6, 1]
134
+
135
+ r = MAE(model, kmesh, gamma=True)
136
+ # thetas, es = r.get_band_energy_vs_theta(angle_range=(0, np.pi*2), rotation_axis="z", initial_direction=(1,0,0), npoints=21)
137
+ thetas, es, es2 = r.get_band_energy_vs_theta(
138
+ angle_range=(0, np.pi),
139
+ rotation_axis="y",
140
+ initial_direction=(0, 0, 1),
141
+ npoints=11,
142
+ )
143
+ # print the table of thetas and es, es2
144
+ for theta, e, e2 in zip(thetas, es, es2):
145
+ print(f"{theta=}, {e=}, {e2=}")
146
+
147
+ plt.plot(thetas / np.pi, es - es[0], marker="o")
148
+ plt.plot(thetas / np.pi, es2 - es2[0], marker=".")
149
+ plt.savefig("E_along_z_x_z.png")
150
+ plt.show()
151
+
152
+
153
+ def abacus_get_MAE_cli():
154
+ import argparse
155
+
156
+ parser = argparse.ArgumentParser(
157
+ 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. "
158
+ )
159
+ parser.add_argument("path_nosoc", type=str, help="Path to the calculation with ")
160
+ parser.add_argument("path_soc", type=str, help="Path to the SOC calculation")
161
+ parser.add_argument("thetas", type=float, nargs="+", help="Thetas")
162
+ parser.add_argument("psis", type=float, nargs="+", help="Phis")
163
+ parser.add_argument("kmesh", type=int, nargs=3, help="K-mesh")
164
+ parser.add_argument(
165
+ "--gamma", action="store_true", help="Use Gamma centered kpoints"
166
+ )
167
+ parser.add_argument(
168
+ "--outfile",
169
+ type=str,
170
+ help="The angles and the energey will be saved in this file.",
171
+ )
172
+ args = parser.parse_args()
173
+ abacus_get_MAE(
174
+ args.path_nosoc,
175
+ args.path_soc,
176
+ args.kmesh,
177
+ args.thetas,
178
+ args.psis,
179
+ gamma=args.gamma,
180
+ outfile=args.outfile,
181
+ )
182
+
183
+
184
+ if __name__ == "__main__":
185
+ abacus_get_MAE_cli()
@@ -7,6 +7,8 @@ from pathlib import Path
7
7
  import os
8
8
  import numpy as np
9
9
  from scipy.linalg import eigh
10
+ from copy import deepcopy
11
+ from TB2J.mathutils.rotate_spin import rotate_Matrix_from_z_to_spherical
10
12
  from TB2J.utils import symbol_number_list
11
13
  from TB2J.myTB import AbstractTB
12
14
  from TB2J.abacus.abacus_api import read_HR_SR
@@ -15,17 +17,55 @@ from TB2J.abacus.stru_api import read_abacus, read_abacus_out
15
17
 
16
18
 
17
19
  class AbacusWrapper(AbstractTB):
18
- def __init__(self, HR, SR, Rlist, nbasis, nspin=1):
19
- self.R2kfactor = -2j * np.pi
20
+ def __init__(
21
+ self, HR, SR, Rlist, nbasis, nspin=1, HR_soc=None, HR_nosoc=None, nel=None
22
+ ):
23
+ self.R2kfactor = 2j * np.pi
20
24
  self.is_orthogonal = False
25
+ self.split_soc = False
21
26
  self._name = "ABACUS"
22
- self.HR = HR
27
+ self._HR = HR
23
28
  self.SR = SR
24
29
  self.Rlist = Rlist
25
30
  self.nbasis = nbasis
26
31
  self.nspin = nspin
27
32
  self.norb = nbasis * nspin
33
+ self.nel = nel
28
34
  self._build_Rdict()
35
+ if HR_soc is not None:
36
+ self.set_HR_soc(HR_soc=HR_soc, HR_nosoc=HR_nosoc, HR_full=HR)
37
+ self.soc_rotation_angle = 0.0
38
+
39
+ def set_HR_soc(self, HR_soc=None, HR_nosoc=None, HR_full=None):
40
+ self.split_soc = True
41
+ self.HR_soc = HR_soc
42
+ if HR_nosoc is not None:
43
+ self.HR_nosoc = HR_nosoc
44
+ if HR_full is not None:
45
+ self.HR_nosoc = HR_full - HR_soc
46
+
47
+ def set_Hsoc_rotation_angle(self, angle):
48
+ """
49
+ Set the rotation angle for SOC part of Hamiltonian
50
+ """
51
+ self.soc_rotation_angle = angle
52
+
53
+ @property
54
+ def HR(self):
55
+ if self.split_soc:
56
+ _HR = np.zeros_like(self.HR_soc)
57
+ for iR, _ in enumerate(self.Rlist):
58
+ theta, phi = self.soc_rotation_angle
59
+ _HR[iR] = self.HR_nosoc[iR] + rotate_Matrix_from_z_to_spherical(
60
+ self.HR_soc[iR], theta, phi
61
+ )
62
+ return _HR
63
+ else:
64
+ return self._HR
65
+
66
+ @HR.setter
67
+ def set_HR(self, HR):
68
+ self._HR = HR
29
69
 
30
70
  def _build_Rdict(self):
31
71
  if hasattr(self, "Rdict"):
@@ -62,6 +102,8 @@ class AbacusWrapper(AbstractTB):
62
102
  S = self.SR[iR] * phase
63
103
  # Sk += S + S.conjugate().T
64
104
  Sk += S
105
+ # Hk = (Hk + Hk.conj().T)/2
106
+ # Sk = (Sk + Sk.conj().T)/2
65
107
  elif convention == 1:
66
108
  # TODO: implement the first convention (the r convention)
67
109
  raise NotImplementedError("convention 1 is not implemented yet.")
@@ -74,6 +116,14 @@ class AbacusWrapper(AbstractTB):
74
116
  Hk, Sk = self.gen_ham(k, convention=convention)
75
117
  return eigh(Hk, Sk)
76
118
 
119
+ def solve_all(self, kpts, convention=2):
120
+ nk = len(kpts)
121
+ evals = np.zeros((nk, self.nbasis), dtype=float)
122
+ evecs = np.zeros((nk, self.nbasis, self.nbasis), dtype=complex)
123
+ for ik, k in enumerate(kpts):
124
+ evals[ik], evecs[ik] = self.solve(k, convention=convention)
125
+ return evals, evecs
126
+
77
127
  def HSE_k(self, kpt, convention=2):
78
128
  H, S = self.gen_ham(tuple(kpt), convention=convention)
79
129
  evals, evecs = eigh(H, S)
@@ -196,6 +246,20 @@ class AbacusParser:
196
246
  raise ValueError(f"EFERMI not found in the {str(fname)} file.")
197
247
  return efermi
198
248
 
249
+ def read_nel(self):
250
+ """
251
+ Reading the number of electrons from the scf log file.
252
+ """
253
+ fname = str(Path(self.outpath) / "running_scf.log")
254
+ nel = None
255
+ with open(fname, "r") as myfile:
256
+ for line in myfile:
257
+ if "NELECT" in line:
258
+ nel = float(line.split()[2])
259
+ if nel is None:
260
+ raise ValueError(f"NELECT not found in the {str(fname)} file.")
261
+ return nel
262
+
199
263
  def get_basis(self):
200
264
  slist = symbol_number_list(self.atoms)
201
265
  if self.spin == "collinear":
@@ -213,6 +277,42 @@ class AbacusParser:
213
277
  return basis
214
278
 
215
279
 
280
+ class AbacusSplitSOCParser:
281
+ """
282
+ Abacus parser with Hamiltonian split to SOC and non-SOC parts
283
+ """
284
+
285
+ def __init__(self, outpath_nosoc=None, outpath_soc=None, binary=False):
286
+ self.outpath_nosoc = outpath_nosoc
287
+ self.outpath_soc = outpath_soc
288
+ self.binary = binary
289
+ self.parser_nosoc = AbacusParser(outpath=outpath_nosoc, binary=binary)
290
+ self.parser_soc = AbacusParser(outpath=outpath_soc, binary=binary)
291
+ spin1 = self.parser_nosoc.read_spin()
292
+ spin2 = self.parser_soc.read_spin()
293
+ if spin1 != "noncollinear" or spin2 != "noncollinear":
294
+ raise ValueError("Spin should be noncollinear")
295
+
296
+ def parse(self):
297
+ nbasis, Rlist, HR_nosoc, SR = self.parser_nosoc.Read_HSR_noncollinear()
298
+ nbasis2, Rlist2, HR2, SR2 = self.parser_soc.Read_HSR_noncollinear()
299
+ # print(HR[0])
300
+ HR_soc = HR2 - HR_nosoc
301
+ model = AbacusWrapper(
302
+ HR=None,
303
+ SR=SR,
304
+ Rlist=Rlist,
305
+ nbasis=nbasis,
306
+ nspin=2,
307
+ HR_soc=HR_soc,
308
+ HR_nosoc=HR_nosoc,
309
+ )
310
+ model.efermi = self.parser_soc.efermi
311
+ model.basis = self.parser_nosoc.basis
312
+ model.atoms = self.parser_nosoc.atoms
313
+ return model
314
+
315
+
216
316
  def test_abacus_wrapper_collinear():
217
317
  outpath = "/Users/hexu/projects/TB2J_abacus/abacus-tb2j-master/abacus_example/case_Fe/1_no_soc/OUT.Fe"
218
318
  parser = AbacusParser(outpath=outpath, spin=None, binary=False)
@@ -221,7 +321,6 @@ def test_abacus_wrapper_collinear():
221
321
  # parser.read_HSR_collinear()
222
322
  model_up, model_dn = parser.get_models()
223
323
  H, S, E, V = model_up.HSE_k([0, 0, 0])
224
- # print(H.shape)
225
324
  # print(H.diagonal().real)
226
325
  # print(model_up.get_HR0().diagonal().real)
227
326
  print(parser.efermi)
@@ -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()