TB2J 0.7.7.2__py3-none-any.whl → 0.8.0__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/__init__.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.7.7.2"
1
+ __version__ = "0.8.0"
@@ -0,0 +1 @@
1
+ from .abacus_wrapper import AbacusWrapper, AbacusParser
@@ -0,0 +1,191 @@
1
+ # -*- coding: utf-8 -*-
2
+ import numpy as np
3
+ from scipy import sparse
4
+ from scipy.sparse import csr_matrix
5
+ import re
6
+ import struct
7
+
8
+
9
+ class XR_matrix:
10
+ def __init__(self, nspin, XR_fileName):
11
+ self.nspin = nspin
12
+ self.XR_fileName = XR_fileName
13
+
14
+ def read_file(self):
15
+ with open(self.XR_fileName, "r") as fread:
16
+ while True:
17
+ line = fread.readline().split()
18
+ if line[0] == "Matrix":
19
+ break
20
+
21
+ self.basis_num = int(line[-1])
22
+ line = fread.readline()
23
+ self.R_num = int(line.split()[-1])
24
+ self.R_direct_coor = np.zeros([self.R_num, 3], dtype=int)
25
+ if self.nspin != 4:
26
+ self.XR = np.zeros(
27
+ [self.R_num, self.basis_num, self.basis_num], dtype=float
28
+ )
29
+ else:
30
+ self.XR = np.zeros(
31
+ [self.R_num, self.basis_num, self.basis_num], dtype=complex
32
+ )
33
+
34
+ for iR in range(self.R_num):
35
+ line = fread.readline().split()
36
+ self.R_direct_coor[iR, 0] = int(line[0])
37
+ self.R_direct_coor[iR, 1] = int(line[1])
38
+ self.R_direct_coor[iR, 2] = int(line[2])
39
+ data_size = int(line[3])
40
+
41
+ if self.nspin != 4:
42
+ data = np.zeros((data_size,), dtype=float)
43
+ else:
44
+ data = np.zeros((data_size,), dtype=complex)
45
+
46
+ indices = np.zeros((data_size,), dtype=int)
47
+ indptr = np.zeros((self.basis_num + 1,), dtype=int)
48
+
49
+ if data_size != 0:
50
+ if self.nspin != 4:
51
+ line = fread.readline().split()
52
+ if len(line) != data_size:
53
+ print("size = ", len(line), " data_size = ", data_size)
54
+ for index in range(data_size):
55
+ data[index] = float(line[index])
56
+ else:
57
+ line = re.findall("[(](.*?)[])]", fread.readline())
58
+ for index in range(data_size):
59
+ value = line[index].split(",")
60
+ data[index] = complex(float(value[0]), float(value[1]))
61
+
62
+ line = fread.readline().split()
63
+ for index in range(data_size):
64
+ indices[index] = int(line[index])
65
+
66
+ line = fread.readline().split()
67
+ for index in range(self.basis_num + 1):
68
+ indptr[index] = int(line[index])
69
+
70
+ self.XR[iR] = csr_matrix(
71
+ (data, indices, indptr), shape=(self.basis_num, self.basis_num)
72
+ ).toarray()
73
+
74
+ def read_file_binary(self):
75
+ with open(self.XR_fileName, "rb") as fread:
76
+ self.basis_num = struct.unpack("i", fread.read(4))[0]
77
+ self.R_num = struct.unpack("i", fread.read(4))[0]
78
+ self.R_direct_coor = np.zeros([self.R_num, 3], dtype=int)
79
+ if self.nspin != 4:
80
+ self.XR = np.zeros(
81
+ [self.R_num, self.basis_num, self.basis_num], dtype=float
82
+ )
83
+ else:
84
+ self.XR = np.zeros(
85
+ [self.R_num, self.basis_num, self.basis_num], dtype=complex
86
+ )
87
+
88
+ for iR in range(self.R_num):
89
+ self.R_direct_coor[iR, 0] = struct.unpack("i", fread.read(4))[0]
90
+ self.R_direct_coor[iR, 1] = struct.unpack("i", fread.read(4))[0]
91
+ self.R_direct_coor[iR, 2] = struct.unpack("i", fread.read(4))[0]
92
+ data_size = struct.unpack("i", fread.read(4))[0]
93
+
94
+ if self.nspin != 4:
95
+ data = np.zeros((data_size,), dtype=float)
96
+ else:
97
+ data = np.zeros((data_size,), dtype=complex)
98
+
99
+ indices = np.zeros((data_size,), dtype=int)
100
+ indptr = np.zeros((self.basis_num + 1,), dtype=int)
101
+
102
+ if data_size != 0:
103
+ if self.nspin != 4:
104
+ for index in range(data_size):
105
+ data[index] = struct.unpack("d", fread.read(8))[0]
106
+ else:
107
+ for index in range(data_size):
108
+ real = struct.unpack("d", fread.read(8))[0]
109
+ imag = struct.unpack("d", fread.read(8))[0]
110
+ data[index] = complex(real, imag)
111
+
112
+ for index in range(data_size):
113
+ indices[index] = struct.unpack("i", fread.read(4))[0]
114
+
115
+ for index in range(self.basis_num + 1):
116
+ indptr[index] = struct.unpack("i", fread.read(4))[0]
117
+
118
+ self.XR[iR] = csr_matrix(
119
+ (data, indices, indptr), shape=(self.basis_num, self.basis_num)
120
+ ).toarray()
121
+
122
+
123
+ def read_HR_SR(
124
+ nspin=4,
125
+ binary=False,
126
+ HR_fileName="data-HR-sparse_SPIN0.csr",
127
+ SR_fileName="data-SR-sparse_SPIN0.csr",
128
+ ):
129
+ """
130
+ IN:
131
+ nspin: int, different spins.
132
+ binary: bool, whether the HR and SR matrices are binary files.
133
+ HR_fileName: if nspin=1 or 4, str, HR file name;
134
+ if nspin=2, list or tuple, size=2, [HR_up_fileName, HR_dn_fileName].
135
+ SR_fileName: str, SR file name.
136
+
137
+ OUT:
138
+ if nspin = 1 or 4:
139
+ return basis_num, R_direct_coor, HR, SR
140
+ elif nspin = 2:
141
+ return basis_num, R_direct_coor, HR_up, HR_dn, SR
142
+
143
+ basis_num: int, number of atomic orbital basis.
144
+ R_direct_coor: numpy ndarray, shape=[R_num, 3], fractional coordinates (x, y, z) of the R-th primitive cell.
145
+ HR or HR_up or HR_dn: numpy ndarray, shape=[R_num, basis_num, basis_num], HR matrix and unit is eV.
146
+ SR: numpy ndarray, shape=[R_num, basis_num, basis_num], SR matrix.
147
+ """
148
+ Ry_to_eV = 13.605698066
149
+
150
+ if nspin == 1 or nspin == 4:
151
+ if not isinstance(HR_fileName, str):
152
+ raise ValueError("The HR_fileName must be a str for nspin=1 or 4.")
153
+
154
+ HR = XR_matrix(nspin, HR_fileName)
155
+ SR = XR_matrix(nspin, SR_fileName)
156
+
157
+ if binary:
158
+ HR.read_file_binary()
159
+ SR.read_file_binary()
160
+ else:
161
+ HR.read_file()
162
+ SR.read_file()
163
+
164
+ return HR.basis_num, HR.R_direct_coor, HR.XR * Ry_to_eV, SR.XR
165
+ else:
166
+ if not isinstance(HR_fileName, (list, tuple)):
167
+ raise ValueError("The HR_fileName must be a list or a tuple for nspin=2.")
168
+
169
+ if len(HR_fileName) != 2:
170
+ raise ValueError("The size of the HR_fileName must be 2 for nspin=2.")
171
+
172
+ HR_up = XR_matrix(nspin, HR_fileName[0])
173
+ HR_dn = XR_matrix(nspin, HR_fileName[1])
174
+ SR = XR_matrix(nspin, SR_fileName)
175
+
176
+ if binary:
177
+ HR_up.read_file_binary()
178
+ HR_dn.read_file_binary()
179
+ SR.read_file_binary()
180
+ else:
181
+ HR_up.read_file()
182
+ HR_dn.read_file()
183
+ SR.read_file()
184
+
185
+ return (
186
+ HR_up.basis_num,
187
+ HR_up.R_direct_coor,
188
+ HR_up.XR * Ry_to_eV,
189
+ HR_dn.XR * Ry_to_eV,
190
+ SR.XR,
191
+ )
@@ -0,0 +1,223 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ The abacus wrapper
5
+ """
6
+ from pathlib import Path
7
+ import numpy as np
8
+ from scipy.linalg import eigh
9
+ from TB2J.utils import symbol_number_list
10
+ from TB2J.myTB import AbstractTB
11
+ from TB2J.abacus.abacus_api import read_HR_SR
12
+ from TB2J.abacus.orbital_api import parse_abacus_orbital
13
+ from TB2J.abacus.stru_api import read_abacus, read_abacus_out
14
+
15
+
16
+ class AbacusWrapper(AbstractTB):
17
+ def __init__(self, HR, SR, Rlist, nbasis, nspin=1):
18
+ self.R2kfactor = -2j * np.pi
19
+ self.is_orthogonal = False
20
+ self.is_siesta = False
21
+ self._name = "Abacus"
22
+ self.HR = HR
23
+ self.SR = SR
24
+ self.Rlist = Rlist
25
+ self.nbasis = nbasis
26
+ self.nspin = nspin
27
+ self.norb = nbasis * nspin
28
+ self._build_Rdict()
29
+
30
+ def _build_Rdict(self):
31
+ if hasattr(self, "Rdict"):
32
+ pass
33
+ else:
34
+ self.Rdict = {}
35
+ for iR, R in enumerate(self.Rlist):
36
+ self.Rdict[tuple(R)] = iR
37
+
38
+ def get_hamR(self, R):
39
+ return self.HR[self.Rdict[tuple(R)]]
40
+
41
+ def gen_ham(self, k, convention=2):
42
+ """
43
+ generate hamiltonian matrix at k point.
44
+ H_k( i, j)=\sum_R H_R(i, j)^phase.
45
+ There are two conventions,
46
+ first:
47
+ phase =e^{ik(R+rj-ri)}. often better used for berry phase.
48
+ second:
49
+ phase= e^{ikR}. We use the first convention here.
50
+
51
+ :param k: kpoint
52
+ :param convention: 1 or 2.
53
+ """
54
+ Hk = np.zeros((self.nbasis, self.nbasis), dtype="complex")
55
+ Sk = np.zeros((self.nbasis, self.nbasis), dtype="complex")
56
+ if convention == 2:
57
+ for iR, R in enumerate(self.Rlist):
58
+ phase = np.exp(self.R2kfactor * np.dot(k, R))
59
+ H = self.HR[iR] * phase
60
+ # Hk += H + H.conjugate().T
61
+ Hk += H
62
+ S = self.SR[iR] * phase
63
+ # Sk += S + S.conjugate().T
64
+ Sk += S
65
+ elif convention == 1:
66
+ # TODO: implement the first convention (the r convention)
67
+ raise NotImplementedError("convention 1 is not implemented yet.")
68
+ pass
69
+ else:
70
+ raise ValueError("convention should be either 1 or 2.")
71
+ return Hk, Sk
72
+
73
+ def solve(self, k, convention=2):
74
+ Hk, Sk = self.gen_ham(k, convention=convention)
75
+ return eigh(Hk, Sk)
76
+
77
+ def HSE_k(self, kpt, convention=2):
78
+ H, S = self.gen_ham(tuple(kpt), convention=convention)
79
+ evals, evecs = eigh(H, S)
80
+ return H, S, evals, evecs
81
+
82
+ def HS_and_eigen(self, kpts, convention=2):
83
+ """
84
+ calculate eigens for all kpoints.
85
+ :param kpts: list of k points.
86
+ """
87
+ nk = len(kpts)
88
+ hams = np.zeros((nk, self.nbasis, self.nbasis), dtype=complex)
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
+ hams[ik], S, evals[ik], evecs[ik] = self.HSE_k(
93
+ tuple(k), convention=convention
94
+ )
95
+ return hams, None, evals, evecs
96
+
97
+
98
+ class AbacusParser:
99
+ def __init__(self, spin=None, outpath=None, binary=False):
100
+ self.outpath = outpath
101
+ if spin is None:
102
+ self.spin = self.read_spin()
103
+ else:
104
+ self.spin = spin
105
+ self.binary = binary
106
+ # read the information
107
+ self.read_atoms()
108
+ self.efermi = self.read_efermi()
109
+ self.read_basis()
110
+
111
+ def read_spin(self):
112
+ with open(str(Path(self.outpath) / "running_scf.log")) as myfile:
113
+ for line in myfile:
114
+ if line.strip().startswith("nspin"):
115
+ nspin = int(line.strip().split()[-1])
116
+ if nspin == 1:
117
+ return "non-polarized"
118
+ elif nspin == 2:
119
+ return "collinear"
120
+ elif nspin == 2:
121
+ return "noncollinear"
122
+ else:
123
+ raise ValueError("nspin should be either 1 or 4.")
124
+
125
+ def read_atoms(self):
126
+ self.atoms = read_abacus(str(Path(self.outpath) / "../Stru"))
127
+ return self.atoms
128
+
129
+ def read_basis(self):
130
+ fname = str(Path(self.outpath) / "Orbital")
131
+ self.basis = parse_abacus_orbital(fname)
132
+ return self.basis
133
+
134
+ def read_HSR_collinear(self, binary=None):
135
+ p = Path(self.outpath)
136
+ SR_filename = p / "data-SR-sparse_SPIN0.csr"
137
+ HR_filename = [p / "data-HR-sparse_SPIN0.csr", p / "data-HR-sparse_SPIN1.csr"]
138
+ nbasis, Rlist, HR_up, HR_dn, SR = read_HR_SR(
139
+ nspin=2,
140
+ binary=self.binary,
141
+ HR_fileName=HR_filename,
142
+ SR_fileName=SR_filename,
143
+ )
144
+ return nbasis, Rlist, HR_up, HR_dn, SR
145
+
146
+ def Read_HSR_noncollinear(self, binary=None):
147
+ p = Path(self.outpath)
148
+ SR_filename = (p / "data-SR-sparse_SPIN0.csr").as_posix()
149
+ HR_filename = p / "data-HR-sparse_SPIN0.csr".as_posix()
150
+ self.nbasis, self.Rlist, self.HR, self.SR = read_HR_SR(
151
+ nspin=4,
152
+ binary=self.binary,
153
+ HR_fileName=HR_filename,
154
+ SR_fileName=SR_filename,
155
+ )
156
+
157
+ def get_models(self):
158
+ if self.spin == "collinear":
159
+ nbasis, Rlist, HR_up, HR_dn, SR = self.read_HSR_collinear()
160
+ model_up = AbacusWrapper(HR_up, SR, Rlist, nbasis, nspin=1)
161
+ model_dn = AbacusWrapper(HR_dn, SR, Rlist, nbasis, nspin=1)
162
+ model_up.efermi = self.efermi
163
+ model_dn.efermi = self.efermi
164
+ model_up.basis, model_dn.basis = self.get_basis()
165
+ model_up.atoms = self.atoms
166
+ model_dn.atoms = self.atoms
167
+ return model_up, model_dn
168
+ elif self.spin == "noncollinear":
169
+ nbasis, Rlist, HR, SR = self.Read_HSR_noncollinear()
170
+ model = AbacusWrapper(HR, SR, Rlist, nbasis, nspin=2)
171
+ model.efermi = self.efermi
172
+ model.basis = self.get_basis()
173
+ model.atoms = self.atoms
174
+ return model
175
+
176
+ def read_efermi(self):
177
+ """
178
+ Reading the efermi from the scf log file.
179
+ Search for the line EFERMI = xxxxx eV
180
+ """
181
+ fname = str(Path(self.outpath) / "running_scf.log")
182
+ efermi = None
183
+ with open(fname, "r") as myfile:
184
+ for line in myfile:
185
+ if "EFERMI" in line:
186
+ efermi = float(line.split()[2])
187
+ if efermi is None:
188
+ raise ValueError(f"EFERMI not found in the {str(fname)} file.")
189
+ return efermi
190
+
191
+ def get_basis(self):
192
+ slist = symbol_number_list(self.atoms)
193
+ if self.spin == "collinear":
194
+ basis_up = []
195
+ basis_dn = []
196
+ for b in self.basis:
197
+ basis_up.append((slist[b.iatom], b.sym, "up"))
198
+ basis_dn.append((slist[b.iatom], b.sym, "down"))
199
+ return basis_up, basis_dn
200
+ elif self.spin == "noncollinear":
201
+ basis = []
202
+ for b in self.basis:
203
+ basis.append((slist[b.iatom], b.sym, "up"))
204
+ basis.append((slist[b.iatom], b.sym, "down"))
205
+ return basis
206
+
207
+
208
+ def test_abacus_wrapper():
209
+ outpath = "/Users/hexu/projects/TB2J_abacus/abacus-tb2j-master/abacus_example/case_Fe/1_no_soc/OUT.Fe"
210
+ parser = AbacusParser(outpath=outpath, spin=None, binary=False)
211
+ atoms = parser.read_atoms()
212
+ # atoms=parser.read_atoms_out()
213
+ # parser.read_HSR_collinear()
214
+ model_up, model_dn = parser.get_models()
215
+ H, S, E, V = model_up.HSE_k([0, 0, 0])
216
+ # print(H.shape)
217
+ # print(H.diagonal().real)
218
+ # print(model_up.get_HR0().diagonal().real)
219
+ print(parser.get_efermi())
220
+
221
+
222
+ if __name__ == "__main__":
223
+ test_abacus_wrapper()
@@ -0,0 +1,102 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ The main function to compute exchange interaction from abacus data
5
+ """
6
+
7
+ import os
8
+ from pathlib import Path
9
+ from TB2J.abacus.abacus_wrapper import AbacusParser
10
+ from TB2J.exchange import ExchangeNCL, ExchangeCL
11
+ from TB2J.exchangeCL2 import ExchangeCL2
12
+
13
+
14
+ def gen_exchange_abacus(
15
+ path,
16
+ suffix="Abacus",
17
+ binary=False,
18
+ magnetic_elements=[],
19
+ include_orbs=[],
20
+ kmesh=[7, 7, 7],
21
+ emin=-13.0,
22
+ emax=0.00,
23
+ nz=100,
24
+ exclude_orbs=[],
25
+ Rcut=None,
26
+ use_cache=False,
27
+ np=1,
28
+ output_path="TB2J_results",
29
+ orb_decomposition=False,
30
+ description=None,
31
+ ):
32
+ outpath = Path(path) / f"OUT.{suffix}"
33
+
34
+ if not os.path.exists(outpath):
35
+ raise ValueError(
36
+ f"The path {outpath} does not exist. Please check if the path and the suffix is correct"
37
+ )
38
+ parser = AbacusParser(outpath=outpath, spin=None, binary=binary)
39
+ spin = parser.read_spin()
40
+ if spin == "collinear":
41
+ tbmodel_up, tbmodel_dn = parser.get_models()
42
+ efermi = parser.read_efermi()
43
+ print("Starting to calculate exchange.")
44
+ description = f""" Input from collinear Abacus data.
45
+ data directory: {outpath}
46
+ \n"""
47
+ exchange = ExchangeCL2(
48
+ tbmodels=(tbmodel_up, tbmodel_dn),
49
+ atoms=tbmodel_up.atoms,
50
+ basis=tbmodel_up.basis,
51
+ efermi=efermi,
52
+ magnetic_elements=magnetic_elements,
53
+ include_orbs=include_orbs,
54
+ kmesh=kmesh,
55
+ emin=emin,
56
+ emax=emax,
57
+ nz=nz,
58
+ exclude_orbs=exclude_orbs,
59
+ Rcut=Rcut,
60
+ np=np,
61
+ use_cache=use_cache,
62
+ output_path=output_path,
63
+ description=description,
64
+ )
65
+ exchange.run(path=output_path)
66
+ print("\n")
67
+ print(f"All calculation finsihed. The results are in {output_path} directory.")
68
+ else:
69
+ tbmodel = parser.get_models()
70
+ print("Starting to calculate exchange.")
71
+ description = f""" Input from collinear Abacus data.
72
+ data directory: {outpath}
73
+ \n"""
74
+ exchange = ExchangeNCL(
75
+ tbmodels=tbmodel,
76
+ atoms=tbmodel.atoms,
77
+ basis=tbmodel.basis,
78
+ efermi=tbmodel.efermi,
79
+ magnetic_elements=magnetic_elements,
80
+ kmesh=kmesh,
81
+ emin=emin,
82
+ emax=emax,
83
+ nz=nz,
84
+ exclude_orbs=exclude_orbs,
85
+ Rcut=Rcut,
86
+ use_cache=use_cache,
87
+ description=description,
88
+ )
89
+ exchange.run()
90
+ print("\n")
91
+ print("All calculation finsihed. The results are in TB2J_results directory.")
92
+
93
+
94
+ if __name__ == "__main__":
95
+ gen_exchange_abacus(
96
+ path="/Users/hexu/projects/TB2J_abacus/abacus-tb2j-master/abacus_example/case_Fe/1_no_soc",
97
+ label="Fe",
98
+ magnetic_elements=["Fe"],
99
+ nz=50,
100
+ Rcut=8,
101
+ kmesh=[7, 7, 7],
102
+ )
@@ -0,0 +1,70 @@
1
+ #!/usr/vin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ """
4
+ Parser for the abacus orbital file
5
+ """
6
+ from pathlib import Path
7
+ import numpy as np
8
+ from dataclasses import dataclass
9
+ from collections import namedtuple
10
+ from TB2J.utils import symbol_number, symbol_number_list
11
+
12
+
13
+ @dataclass
14
+ class AbacusOrbital:
15
+ """
16
+ Orbital class
17
+ """
18
+
19
+ iatom: int
20
+ sym: str
21
+ spin: int
22
+ element: str
23
+ l: int
24
+ m: int
25
+ z: int
26
+
27
+
28
+ def parse_abacus_orbital(fname):
29
+ """
30
+ parse the abacus orbital file
31
+ """
32
+ orbs = []
33
+ with open(fname, "r") as myfile:
34
+ line = myfile.readline()
35
+ line = myfile.readline()
36
+ while line.strip() != "":
37
+ seg = line.split()
38
+ iatom, element, l, m, z, sym = seg
39
+ iatom = int(iatom)
40
+ ispin = 0
41
+ l = int(l)
42
+ m = int(m)
43
+ z = int(z)
44
+ orbs.append(AbacusOrbital(iatom, sym, ispin, element, l, m, z))
45
+ line = myfile.readline()
46
+ return orbs
47
+
48
+
49
+ def bset_to_symnum_type(bset, atoms):
50
+ """
51
+ convert the basis set to symbol number type
52
+ """
53
+ slist = symbol_number_list(atoms)
54
+ result = []
55
+ for b in bset:
56
+ result.append((slist[b.iatom], b.sym, b.spin))
57
+ return result
58
+
59
+
60
+ def test_parse_abacus_orbital():
61
+ """
62
+ test the parser
63
+ """
64
+ outpath = "/Users/hexu/projects/TB2J_abacus/abacus-tb2j-master/abacus_example/case_Sr2Mn2O6/1_no_soc/OUT.Sr2Mn2O6"
65
+ orbs = parse_abacus_orbital(Path(outpath) / "Orbital")
66
+ print(orbs)
67
+
68
+
69
+ if __name__ == "__main__":
70
+ test_parse_abacus_orbital()