TB2J 0.9.0.3__py3-none-any.whl → 0.9.0.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
TB2J/Jdownfolder.py CHANGED
@@ -92,7 +92,7 @@ class PWFDownfolder:
92
92
  # anchors={(0, 0, 0): (-1, -2, -3, -4)},
93
93
  # anchors={(0, 0, 0): ()},
94
94
  # use_proj=True,
95
- enhance_Amn=2.0,
95
+ # enhance_Amn=2.0,
96
96
  )
97
97
  params.update(kwargs)
98
98
  wann.set_parameters(**params)
@@ -134,7 +134,7 @@ class JDownfolder_pickle:
134
134
  outpath,
135
135
  qmesh=[7, 7, 7],
136
136
  iso_only=False,
137
- method="pwf",
137
+ method="lowdin",
138
138
  **kwargs
139
139
  ):
140
140
  self.exc = SpinIO.load_pickle(path=inpath, fname="TB2J.pickle")
TB2J/MAE.py ADDED
@@ -0,0 +1,188 @@
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.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
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
+ class MAE:
32
+ def __init__(self, model, kmesh, gamma=True, width=0.1, nel=None):
33
+ self.model = model
34
+ if nel is not None:
35
+ 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)
38
+ self.width = width
39
+
40
+ def get_band_energy(self):
41
+ evals, evecs = self.model.solve_all(self.kpts)
42
+ occ = get_occupation(evals, self.kweights, self.model.nel, width=self.width)
43
+ eband = np.sum(evals * occ * self.kweights[:, np.newaxis])
44
+ return eband
45
+
46
+ def calc_ref(self):
47
+ # calculate the Hk_ref, Sk_ref, Hk_soc_ref, and rho_ref
48
+ self.Sk_ref = R_to_k(self.kpts, self.model.Rlist, self.model.SR)
49
+ self.Hk_xc_ref = R_to_k(self.kpts, self.model.Rlist, self.model._HR_copy)
50
+ self.Hk_soc_ref = R_to_k(self.kpts, self.model.Rlist, self.model.HR_soc)
51
+ self.rho_ref = np.zeros(
52
+ (len(self.kpts), self.model.nbasis, self.model.nbasis), dtype=complex
53
+ )
54
+
55
+ evals = np.zeros((len(self.kpts), self.model.nbasis), dtype=float)
56
+ evecs = np.zeros(
57
+ (len(self.kpts), self.model.nbasis, self.model.nbasis), dtype=complex
58
+ )
59
+
60
+ for ik, kpt in enumerate(self.kpts):
61
+ # evals, evecs = eigh(self.Hk_xc_ref[ik]+self.Hk_soc_ref[ik], self.Sk_ref[ik])
62
+ evals[ik], evecs[ik] = eigh(self.Hk_xc_ref[ik], self.Sk_ref[ik])
63
+ occ = get_occupation(
64
+ evals, self.kweights, self.model.nel, width=self.model.width
65
+ )
66
+ # occ = fermi(evals, self.model.efermi, width=self.model.width)
67
+ self.rho_ref = np.einsum("kib, kb, kjb -> kij", evecs, occ, evecs.conj())
68
+
69
+ def get_band_energy_vs_angles(
70
+ self,
71
+ thetas,
72
+ phis,
73
+ ):
74
+ es = []
75
+ # es2 = []
76
+ # e,rho = self.model.get_band_energy(dm=True)
77
+ # self.calc_ref()
78
+ # thetas = np.linspace(*angle_range, npoints)
79
+ nangles = len(thetas)
80
+ for i in tqdm.trange(nangles):
81
+ theta = thetas[i]
82
+ phi = phis[i]
83
+ self.model.set_Hsoc_rotation_angle([theta, phi])
84
+ e = self.get_band_energy()
85
+ es.append(e)
86
+ # es2.append(e2)
87
+ return es
88
+
89
+
90
+ def get_model_energy(model, kmesh, gamma=True):
91
+ ham = MAE(model, kmesh, gamma=gamma)
92
+ return ham.get_band_energy()
93
+
94
+
95
+ def abacus_get_MAE(
96
+ path_nosoc, path_soc, kmesh, thetas, psis, gamma=True, outfile="MAE.txt", nel=None
97
+ ):
98
+ """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
+ parser = AbacusSplitSOCParser(
100
+ outpath_nosoc=path_nosoc, outpath_soc=path_soc, binary=False
101
+ )
102
+ model = parser.parse()
103
+ ham = MAE(model, kmesh, gamma=gamma)
104
+ es = ham.get_band_energy_vs_angles(thetas, psis)
105
+ if outfile:
106
+ 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")
110
+ return es
111
+
112
+
113
+ def siesta_get_MAE(fdf_fname, kmesh, thetas, phis, gamma=True, outfile="MAE.txt"):
114
+ """ """
115
+ model= SislParser(fdf_fname=fdf_fname, read_H_soc=True).get_model()
116
+ ham = MAE(model, kmesh, gamma=gamma)
117
+ es = ham.get_band_energy_vs_angles(thetas, phis)
118
+ if outfile:
119
+ with open(outfile, "w") as f:
120
+ f.write("#theta, psi, energy\n")
121
+ for theta, psi, e in zip(thetas, phis, es):
122
+ #f.write(f"{theta}, {psi}, {e}\n")
123
+ f.write(f"{theta:5.3f}, {psi:5.3f}, {e:10.9f}\n")
124
+ return es
125
+
126
+
127
+ def test_AbacusSplitSOCWrapper():
128
+ # path = Path("~/projects/2D_Fe").expanduser()
129
+ path = Path("~/projects/TB2Jflows/examples/2D_Fe/Fe_z").expanduser()
130
+ outpath_nosoc = f"{path}/soc0/OUT.ABACUS"
131
+ outpath_soc = f"{path}/soc1/OUT.ABACUS"
132
+ parser = AbacusSplitSOCParser(
133
+ outpath_nosoc=outpath_nosoc, outpath_soc=outpath_soc, binary=False
134
+ )
135
+ model = parser.parse()
136
+ kmesh = [6, 6, 1]
137
+
138
+ r = MAE(model, kmesh, gamma=True)
139
+ # thetas, es = r.get_band_energy_vs_theta(angle_range=(0, np.pi*2), rotation_axis="z", initial_direction=(1,0,0), npoints=21)
140
+ thetas, es, es2 = r.get_band_energy_vs_theta(
141
+ angle_range=(0, np.pi),
142
+ rotation_axis="y",
143
+ initial_direction=(0, 0, 1),
144
+ npoints=11,
145
+ )
146
+ # print the table of thetas and es, es2
147
+ for theta, e, e2 in zip(thetas, es, es2):
148
+ print(f"{theta=}, {e=}, {e2=}")
149
+
150
+ plt.plot(thetas / np.pi, es - es[0], marker="o")
151
+ plt.plot(thetas / np.pi, es2 - es2[0], marker=".")
152
+ plt.savefig("E_along_z_x_z.png")
153
+ plt.show()
154
+
155
+
156
+ def abacus_get_MAE_cli():
157
+ import argparse
158
+
159
+ parser = argparse.ArgumentParser(
160
+ 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. "
161
+ )
162
+ parser.add_argument("path_nosoc", type=str, help="Path to the calculation with ")
163
+ parser.add_argument("path_soc", type=str, help="Path to the SOC calculation")
164
+ parser.add_argument("thetas", type=float, nargs="+", help="Thetas")
165
+ parser.add_argument("psis", type=float, nargs="+", help="Phis")
166
+ parser.add_argument("kmesh", type=int, nargs=3, help="K-mesh")
167
+ parser.add_argument(
168
+ "--gamma", action="store_true", help="Use Gamma centered kpoints"
169
+ )
170
+ parser.add_argument(
171
+ "--outfile",
172
+ type=str,
173
+ help="The angles and the energey will be saved in this file.",
174
+ )
175
+ args = parser.parse_args()
176
+ abacus_get_MAE(
177
+ args.path_nosoc,
178
+ args.path_soc,
179
+ args.kmesh,
180
+ args.thetas,
181
+ args.psis,
182
+ gamma=args.gamma,
183
+ outfile=args.outfile,
184
+ )
185
+
186
+
187
+ if __name__ == "__main__":
188
+ abacus_get_MAE_cli()
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()
@@ -238,6 +238,18 @@ Generation time: {now.strftime("%y/%m/%d %H:%M:%S")}
238
238
  def get_charge_iatom(self, iatom):
239
239
  return self.charges[iatom]
240
240
 
241
+ def ijR_index_spin_to_atom(self, i, j, R):
242
+ return (self.iatom(i), self.iatom(j), R)
243
+
244
+ def ijR_index_atom_to_spin(self, iatom, jatom, R):
245
+ return (self.index_spin[iatom], self.index_spin[jatom], R)
246
+
247
+ def ijR_list(self):
248
+ return [(i, j, R) for R, i, j in self.exchange_Jdict]
249
+
250
+ def ijR_list_index_atom(self):
251
+ return [self.ijR_index_spin_to_atom(i, j, R) for R, i, j in self.exchange_Jdict]
252
+
241
253
  def get_J(self, i, j, R, default=None):
242
254
  i = self.i_spin(i)
243
255
  j = self.i_spin(j)
File without changes
TB2J/lawaf/__init__.py ADDED
File without changes