bandu 1.3.6__py3-none-any.whl → 1.3.7__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.
- bandu/abinit_reader.py +1094 -1094
- bandu/bandu.py +320 -313
- bandu/brillouin_zone.py +185 -185
- bandu/colors.py +46 -46
- bandu/isosurface_class.py +235 -235
- bandu/plotter.py +599 -599
- bandu/translate.py +37 -37
- bandu/wfk_class.py +557 -556
- bandu/xsf_reader.py +91 -91
- {bandu-1.3.6.dist-info → bandu-1.3.7.dist-info}/METADATA +192 -192
- bandu-1.3.7.dist-info/RECORD +14 -0
- {bandu-1.3.6.dist-info → bandu-1.3.7.dist-info}/WHEEL +1 -1
- {bandu-1.3.6.dist-info → bandu-1.3.7.dist-info}/licenses/LICENSE +21 -21
- bandu-1.3.6.dist-info/RECORD +0 -14
- {bandu-1.3.6.dist-info → bandu-1.3.7.dist-info}/top_level.txt +0 -0
bandu/abinit_reader.py
CHANGED
|
@@ -1,1095 +1,1095 @@
|
|
|
1
|
-
import struct
|
|
2
|
-
import numpy as np
|
|
3
|
-
from typing import Generator, Union
|
|
4
|
-
import sys
|
|
5
|
-
from . import wfk_class as wc
|
|
6
|
-
np.set_printoptions(threshold=sys.maxsize)
|
|
7
|
-
|
|
8
|
-
def bytes2float(bin_data)->float:
|
|
9
|
-
return struct.unpack('<d', bin_data)[0]
|
|
10
|
-
|
|
11
|
-
def bytes2int(bin_data)->int:
|
|
12
|
-
return int.from_bytes(bin_data, 'little', signed=True)
|
|
13
|
-
|
|
14
|
-
class Abinit7WFK():
|
|
15
|
-
def __init__(
|
|
16
|
-
self, filename:str
|
|
17
|
-
):
|
|
18
|
-
self.filename = filename
|
|
19
|
-
self._ReadHeader()
|
|
20
|
-
#---------------------------------------------------------------------------------------------------------------------#
|
|
21
|
-
#------------------------------------------------------ METHODS ------------------------------------------------------#
|
|
22
|
-
#---------------------------------------------------------------------------------------------------------------------#
|
|
23
|
-
# method to read wavefunction header for abinit version 7
|
|
24
|
-
def _ReadHeader(
|
|
25
|
-
self
|
|
26
|
-
)->None:
|
|
27
|
-
wfk = open(self.filename, 'rb')
|
|
28
|
-
print('Reading WFK header')
|
|
29
|
-
#---------------#
|
|
30
|
-
# unpack integers
|
|
31
|
-
self.header = bytes2int(wfk.read(4))
|
|
32
|
-
self.version = wfk.read(6).decode()
|
|
33
|
-
self.headform = bytes2int(wfk.read(4))
|
|
34
|
-
self.fform = bytes2int(wfk.read(4))
|
|
35
|
-
wfk.read(8)
|
|
36
|
-
self.bandtot = bytes2int(wfk.read(4))
|
|
37
|
-
self.date = bytes2int(wfk.read(4))
|
|
38
|
-
self.intxc = bytes2int(wfk.read(4))
|
|
39
|
-
self.ixc = bytes2int(wfk.read(4))
|
|
40
|
-
self.natom = bytes2int(wfk.read(4))
|
|
41
|
-
self.ngfftx = bytes2int(wfk.read(4))
|
|
42
|
-
self.ngffty = bytes2int(wfk.read(4))
|
|
43
|
-
self.ngfftz = bytes2int(wfk.read(4))
|
|
44
|
-
self.nkpt = bytes2int(wfk.read(4))
|
|
45
|
-
self.nspden = bytes2int(wfk.read(4))
|
|
46
|
-
self.nspinor = bytes2int(wfk.read(4))
|
|
47
|
-
self.nsppol = bytes2int(wfk.read(4))
|
|
48
|
-
self.nsym = bytes2int(wfk.read(4))
|
|
49
|
-
self.npsp = bytes2int(wfk.read(4))
|
|
50
|
-
self.ntypat = bytes2int(wfk.read(4))
|
|
51
|
-
self.occopt = bytes2int(wfk.read(4))
|
|
52
|
-
self.pertcase = bytes2int(wfk.read(4))
|
|
53
|
-
self.usepaw = bytes2int(wfk.read(4))
|
|
54
|
-
if self.usepaw == 1:
|
|
55
|
-
raise NotImplementedError(
|
|
56
|
-
f'''PAW potentials are not supported by this program for Abinit v{self.version}.
|
|
57
|
-
This program supports PAW potentials in Abinit v10, consider upgrading to the latest version.
|
|
58
|
-
'''
|
|
59
|
-
)
|
|
60
|
-
#--------------#
|
|
61
|
-
# unpack doubles
|
|
62
|
-
self.ecut = bytes2float(wfk.read(8))
|
|
63
|
-
self.ecutdg = bytes2float(wfk.read(8))
|
|
64
|
-
self.ecutsm = bytes2float(wfk.read(8))
|
|
65
|
-
self.ecut_eff = bytes2float(wfk.read(8))
|
|
66
|
-
self.qptnx = bytes2float(wfk.read(8))
|
|
67
|
-
self.qptny = bytes2float(wfk.read(8))
|
|
68
|
-
self.qptnz = bytes2float(wfk.read(8))
|
|
69
|
-
rprimd_ax = bytes2float(wfk.read(8))
|
|
70
|
-
rprimd_ay = bytes2float(wfk.read(8))
|
|
71
|
-
rprimd_az = bytes2float(wfk.read(8))
|
|
72
|
-
rprimd_bx = bytes2float(wfk.read(8))
|
|
73
|
-
rprimd_by = bytes2float(wfk.read(8))
|
|
74
|
-
rprimd_bz = bytes2float(wfk.read(8))
|
|
75
|
-
rprimd_cx = bytes2float(wfk.read(8))
|
|
76
|
-
rprimd_cy = bytes2float(wfk.read(8))
|
|
77
|
-
rprimd_cz = bytes2float(wfk.read(8))
|
|
78
|
-
#------------------------#
|
|
79
|
-
# convert Bohr to Angstrom
|
|
80
|
-
self.real_lattice = 0.529177*np.array([[rprimd_ax, rprimd_ay, rprimd_az],
|
|
81
|
-
[rprimd_bx, rprimd_by, rprimd_bz],
|
|
82
|
-
[rprimd_cx, rprimd_cy, rprimd_cz]]).reshape((3,3))
|
|
83
|
-
self.stmbias = bytes2float(wfk.read(8))
|
|
84
|
-
self.tphysel = bytes2float(wfk.read(8))
|
|
85
|
-
self.tsmear = bytes2float(wfk.read(8))
|
|
86
|
-
#---------------#
|
|
87
|
-
# unpack integers
|
|
88
|
-
self.usewvl = bytes2int(wfk.read(4))
|
|
89
|
-
wfk.read(8)
|
|
90
|
-
self.istwfk:list[int] = []
|
|
91
|
-
for i in range(self.nkpt):
|
|
92
|
-
val = bytes2int(wfk.read(4))
|
|
93
|
-
self.istwfk.append(val)
|
|
94
|
-
self.bands:list[int] = []
|
|
95
|
-
for i in range(self.nkpt*self.nsppol):
|
|
96
|
-
val = bytes2int(wfk.read(4))
|
|
97
|
-
self.bands.append(val)
|
|
98
|
-
self.npwarr:list[int] = []
|
|
99
|
-
for i in range(self.nkpt):
|
|
100
|
-
val = bytes2int(wfk.read(4))
|
|
101
|
-
self.npwarr.append(val)
|
|
102
|
-
self.so_psp:list[int] = []
|
|
103
|
-
for i in range(self.npsp):
|
|
104
|
-
val = bytes2int(wfk.read(4))
|
|
105
|
-
self.so_psp.append(val)
|
|
106
|
-
self.symafm:list[int] = []
|
|
107
|
-
for i in range(self.nsym):
|
|
108
|
-
val = bytes2int(wfk.read(4))
|
|
109
|
-
self.symafm.append(val)
|
|
110
|
-
self.symrel:list[np.ndarray] = []
|
|
111
|
-
for i in range(self.nsym):
|
|
112
|
-
arr = np.zeros((3,3))
|
|
113
|
-
arr[0,0] = bytes2int(wfk.read(4))
|
|
114
|
-
arr[1,0] = bytes2int(wfk.read(4))
|
|
115
|
-
arr[2,0] = bytes2int(wfk.read(4))
|
|
116
|
-
arr[0,1] = bytes2int(wfk.read(4))
|
|
117
|
-
arr[1,1] = bytes2int(wfk.read(4))
|
|
118
|
-
arr[2,1] = bytes2int(wfk.read(4))
|
|
119
|
-
arr[0,2] = bytes2int(wfk.read(4))
|
|
120
|
-
arr[1,2] = bytes2int(wfk.read(4))
|
|
121
|
-
arr[2,2] = bytes2int(wfk.read(4))
|
|
122
|
-
self.symrel.append(arr)
|
|
123
|
-
self.typat:list[int] = []
|
|
124
|
-
for i in range(self.natom):
|
|
125
|
-
val = bytes2int(wfk.read(4))
|
|
126
|
-
self.typat.append(val)
|
|
127
|
-
#--------------#
|
|
128
|
-
# unpack doubles
|
|
129
|
-
self.kpts:list[np.ndarray] = []
|
|
130
|
-
for i in range(self.nkpt):
|
|
131
|
-
vec = np.zeros(3)
|
|
132
|
-
vec[0] = bytes2float(wfk.read(8))
|
|
133
|
-
vec[1] = bytes2float(wfk.read(8))
|
|
134
|
-
vec[2] = bytes2float(wfk.read(8))
|
|
135
|
-
self.kpts.append(vec)
|
|
136
|
-
self.occ:list[float] = []
|
|
137
|
-
for i in range(self.bandtot):
|
|
138
|
-
val = bytes2float(wfk.read(8))
|
|
139
|
-
self.occ.append(val)
|
|
140
|
-
self.tnons:list[np.ndarray] = []
|
|
141
|
-
for i in range(self.nsym):
|
|
142
|
-
vec = np.zeros(3)
|
|
143
|
-
vec[0] = bytes2float(wfk.read(8))
|
|
144
|
-
vec[1] = bytes2float(wfk.read(8))
|
|
145
|
-
vec[2] = bytes2float(wfk.read(8))
|
|
146
|
-
self.tnons.append(vec)
|
|
147
|
-
self.znucltypat:list[float] = []
|
|
148
|
-
for i in range(self.ntypat):
|
|
149
|
-
val = bytes2float(wfk.read(8))
|
|
150
|
-
self.znucltypat.append(val)
|
|
151
|
-
self.wtk:list[float] = []
|
|
152
|
-
for i in range(self.nkpt):
|
|
153
|
-
val = bytes2float(wfk.read(8))
|
|
154
|
-
self.wtk.append(val)
|
|
155
|
-
#-----------------------#
|
|
156
|
-
# unpack pseudopotentials
|
|
157
|
-
wfk.read(4)
|
|
158
|
-
self.title:list[str] = []
|
|
159
|
-
self.znuclpsp:list[float] = []
|
|
160
|
-
self.zionpsp:list[float] = []
|
|
161
|
-
self.pspso:list[int] = []
|
|
162
|
-
self.pspdat:list[int] = []
|
|
163
|
-
self.pspcod:list[int] = []
|
|
164
|
-
self.pspxc:list[int] = []
|
|
165
|
-
self.lmn_size:list[int] = []
|
|
166
|
-
for i in range(self.npsp):
|
|
167
|
-
wfk.read(4)
|
|
168
|
-
self.title.append(wfk.read(132).decode())
|
|
169
|
-
self.znuclpsp.append(bytes2float(wfk.read(8)))
|
|
170
|
-
self.zionpsp.append(bytes2float(wfk.read(8)))
|
|
171
|
-
self.pspso.append(bytes2int(wfk.read(4)))
|
|
172
|
-
self.pspdat.append(bytes2int(wfk.read(4)))
|
|
173
|
-
self.pspcod.append(bytes2int(wfk.read(4)))
|
|
174
|
-
self.pspxc.append(bytes2int(wfk.read(4)))
|
|
175
|
-
self.lmn_size.append(bytes2int(wfk.read(4)))
|
|
176
|
-
wfk.read(4)
|
|
177
|
-
#------------------#
|
|
178
|
-
# unpack coordinates
|
|
179
|
-
wfk.read(4)
|
|
180
|
-
self.residm = bytes2float(wfk.read(8))
|
|
181
|
-
self.xred:list[np.ndarray] = []
|
|
182
|
-
for i in range(self.natom):
|
|
183
|
-
vec = np.zeros(3)
|
|
184
|
-
vec[0] = bytes2float(wfk.read(8))
|
|
185
|
-
vec[1] = bytes2float(wfk.read(8))
|
|
186
|
-
vec[2] = bytes2float(wfk.read(8))
|
|
187
|
-
self.xred.append(vec)
|
|
188
|
-
#------------------------------------#
|
|
189
|
-
# unpack total energy and fermi energy
|
|
190
|
-
self.etotal = bytes2float(wfk.read(8))
|
|
191
|
-
self.fermi = bytes2float(wfk.read(8))
|
|
192
|
-
wfk.read(4)
|
|
193
|
-
print('WFK header read')
|
|
194
|
-
wfk.close()
|
|
195
|
-
#-----------------------------------------------------------------------------------------------------------------#
|
|
196
|
-
# check for time reversal symmetry
|
|
197
|
-
def _CheckTimeRev(
|
|
198
|
-
self
|
|
199
|
-
):
|
|
200
|
-
# if system is centrosymmetric, do not double reciprocal symmetry operations
|
|
201
|
-
if True in [np.array_equal(-np.identity(3),mat) for mat in self.symrel]:
|
|
202
|
-
self.time_reversal = False
|
|
203
|
-
else:
|
|
204
|
-
self.time_reversal = True
|
|
205
|
-
print((
|
|
206
|
-
'Noncentrosymmetric system identified, assuming time reversal symmetry\n'
|
|
207
|
-
'To change this, set "check_time_rev" keyword to False when calling ReadWFK() or ReadEigenvalues()'
|
|
208
|
-
))
|
|
209
|
-
#-----------------------------------------------------------------------------------------------------------------#
|
|
210
|
-
# method to read entire body of abinit version 7 wavefunction file
|
|
211
|
-
def ReadWFK(
|
|
212
|
-
self, energy_level=np.nan, width=np.nan, check_time_rev:bool=True
|
|
213
|
-
)->Generator[wc.WFK, None, None]:
|
|
214
|
-
'''
|
|
215
|
-
Method that constructs WFK objects from ABINIT v7 WFK file
|
|
216
|
-
|
|
217
|
-
Parameters
|
|
218
|
-
----------
|
|
219
|
-
energy_level : float
|
|
220
|
-
The energy level to pull plane wave coefficients from
|
|
221
|
-
This is relative to the Fermi energy
|
|
222
|
-
If not defined but width is defined, default is 0 Hartree
|
|
223
|
-
|
|
224
|
-
width : float
|
|
225
|
-
Defines range about energy_level to search for plane wave coefficients
|
|
226
|
-
Total range is equal to width, so look width/2 above and below energy_level
|
|
227
|
-
If not defined but energy_level is defined, default is 0.005 Hartree
|
|
228
|
-
'''
|
|
229
|
-
# check for time reversal symmetry
|
|
230
|
-
if check_time_rev:
|
|
231
|
-
self._CheckTimeRev()
|
|
232
|
-
else:
|
|
233
|
-
self.time_reversal = False
|
|
234
|
-
# read wfk file
|
|
235
|
-
wfk = open(self.filename, 'rb')
|
|
236
|
-
skip = True
|
|
237
|
-
if energy_level is np.nan:
|
|
238
|
-
if width is np.nan:
|
|
239
|
-
skip = False
|
|
240
|
-
else:
|
|
241
|
-
energy_level = 0
|
|
242
|
-
else:
|
|
243
|
-
if width is np.nan:
|
|
244
|
-
width = 0.005
|
|
245
|
-
#-----------#
|
|
246
|
-
# skip header
|
|
247
|
-
wfk.read(298)
|
|
248
|
-
wfk.read(4*(2*self.nkpt + self.nkpt*self.nsppol + self.npsp + 10*self.nsym + self.natom))
|
|
249
|
-
wfk.read(8*(4*self.nkpt + self.bandtot + 3*self.nsym + self.ntypat + 3*self.natom))
|
|
250
|
-
wfk.read(self.npsp*(176))
|
|
251
|
-
#-------------------------------#
|
|
252
|
-
# begin reading wavefunction body
|
|
253
|
-
for i in range(self.nsppol):
|
|
254
|
-
for j in range(self.nkpt):
|
|
255
|
-
print(f'Reading kpoint {j+1} of {self.nkpt}', end='\r')
|
|
256
|
-
if j+1 == self.nkpt:
|
|
257
|
-
print('\n', end='')
|
|
258
|
-
pw_indices:list[tuple] = []
|
|
259
|
-
eigenvalues:list[float] = []
|
|
260
|
-
wfk.read(4)
|
|
261
|
-
npw = bytes2int(wfk.read(4))
|
|
262
|
-
self.nspinor = bytes2int(wfk.read(4))
|
|
263
|
-
nband_temp = bytes2int(wfk.read(4))
|
|
264
|
-
wfk.read(8)
|
|
265
|
-
for pw in range(npw):
|
|
266
|
-
kx = bytes2int(wfk.read(4))
|
|
267
|
-
ky = bytes2int(wfk.read(4))
|
|
268
|
-
kz = bytes2int(wfk.read(4))
|
|
269
|
-
pw_indices.append((kx, ky, kz))
|
|
270
|
-
wfk.read(8)
|
|
271
|
-
for nband in range(nband_temp):
|
|
272
|
-
eigenval = bytes2float(wfk.read(8))
|
|
273
|
-
eigenvalues.append(eigenval)
|
|
274
|
-
# skip reading coefficients if they energy level of interest is not in band
|
|
275
|
-
if skip:
|
|
276
|
-
min_val = self.fermi + energy_level - width/2
|
|
277
|
-
max_val = self.fermi + energy_level + width/2
|
|
278
|
-
eig_found = False
|
|
279
|
-
for eigval in eigenvalues:
|
|
280
|
-
if min_val <= eigval <= max_val:
|
|
281
|
-
eig_found = True
|
|
282
|
-
break
|
|
283
|
-
if eig_found:
|
|
284
|
-
yield self._ReadCoeffs(
|
|
285
|
-
wfk,
|
|
286
|
-
nband_temp,
|
|
287
|
-
npw,
|
|
288
|
-
eigenvalues,
|
|
289
|
-
pw_indices,
|
|
290
|
-
ind=j
|
|
291
|
-
)
|
|
292
|
-
else:
|
|
293
|
-
wfk.read(nband_temp*8)
|
|
294
|
-
wfk.read(4)
|
|
295
|
-
wfk.read(nband_temp*(8 + npw*16))
|
|
296
|
-
else:
|
|
297
|
-
yield self._ReadCoeffs(
|
|
298
|
-
wfk,
|
|
299
|
-
nband_temp,
|
|
300
|
-
npw,
|
|
301
|
-
eigenvalues,
|
|
302
|
-
pw_indices,
|
|
303
|
-
ind=j
|
|
304
|
-
)
|
|
305
|
-
print('WFK body read')
|
|
306
|
-
wfk.close()
|
|
307
|
-
#-----------------------------------------------------------------------------------------------------------------#
|
|
308
|
-
# method to read in plane wave coefficients
|
|
309
|
-
def _ReadCoeffs(
|
|
310
|
-
self, wfk_file, nbands, npws, eigs, pw_inds, ind
|
|
311
|
-
)->wc.WFK:
|
|
312
|
-
occupancies = np.zeros((1,nbands), dtype=float)
|
|
313
|
-
coeffs = np.zeros((nbands,npws), dtype=complex)
|
|
314
|
-
for nband in range(nbands):
|
|
315
|
-
occ = bytes2float(wfk_file.read(8))
|
|
316
|
-
occupancies[:,nband] = occ
|
|
317
|
-
wfk_file.read(4)
|
|
318
|
-
for nband in range(nbands):
|
|
319
|
-
wfk_file.read(4)
|
|
320
|
-
cg:np.ndarray = np.zeros((1,npws), dtype=complex)
|
|
321
|
-
for pw in range(npws):
|
|
322
|
-
cg1 = bytes2float(wfk_file.read(8))
|
|
323
|
-
cg2 = bytes2float(wfk_file.read(8))
|
|
324
|
-
cg[:,pw] = cg1 + 1j*cg2
|
|
325
|
-
coeffs[nband,:] = cg
|
|
326
|
-
wfk_file.read(4)
|
|
327
|
-
return wc.WFK(
|
|
328
|
-
eigenvalues=np.array(eigs),
|
|
329
|
-
wfk_coeffs=np.array(coeffs),
|
|
330
|
-
pw_indices=np.array(pw_inds),
|
|
331
|
-
kpoints=np.array(self.kpts[ind]),
|
|
332
|
-
nkpt=self.nkpt,
|
|
333
|
-
nbands=self.bands[0],
|
|
334
|
-
ngfftx=self.ngfftx,
|
|
335
|
-
ngffty=self.ngffty,
|
|
336
|
-
ngfftz=self.ngfftz,
|
|
337
|
-
symrel=np.array(self.symrel),
|
|
338
|
-
nsym=self.nsym,
|
|
339
|
-
lattice=self.real_lattice,
|
|
340
|
-
natom=self.natom,
|
|
341
|
-
xred=np.array(self.xred),
|
|
342
|
-
typat=self.typat,
|
|
343
|
-
znucltypat=self.znucltypat,
|
|
344
|
-
fermi_energy=self.fermi,
|
|
345
|
-
non_symm_vecs=np.array(self.tnons),
|
|
346
|
-
time_reversal=self.time_reversal
|
|
347
|
-
)
|
|
348
|
-
#-----------------------------------------------------------------------------------------------------------------#
|
|
349
|
-
# method to read only eigenvalues from body of abinit version 7 wavefunction file
|
|
350
|
-
def ReadEigenvalues(
|
|
351
|
-
self, check_time_rev:bool=True
|
|
352
|
-
)->Generator[wc.WFK, None, None]:
|
|
353
|
-
'''
|
|
354
|
-
Method that constructs WFK objects from ABINIT v7 WFK file.
|
|
355
|
-
'''
|
|
356
|
-
# check for time reversal symmetry
|
|
357
|
-
if check_time_rev:
|
|
358
|
-
self._CheckTimeRev()
|
|
359
|
-
else:
|
|
360
|
-
self.time_reversal = False
|
|
361
|
-
# read wfk
|
|
362
|
-
wfk = open(self.filename, 'rb')
|
|
363
|
-
#-----------#
|
|
364
|
-
# skip header
|
|
365
|
-
wfk.read(298)
|
|
366
|
-
wfk.read(4*(2*self.nkpt + self.nkpt*self.nsppol + self.npsp + 10*self.nsym + self.natom))
|
|
367
|
-
wfk.read(8*(4*self.nkpt + self.bandtot + 3*self.nsym + self.ntypat + 3*self.natom))
|
|
368
|
-
wfk.read(self.npsp*(176))
|
|
369
|
-
#-------------------------------#
|
|
370
|
-
# begin reading wavefunction body
|
|
371
|
-
for i in range(self.nsppol):
|
|
372
|
-
for j in range(self.nkpt):
|
|
373
|
-
print(f'Reading kpoint {j+1} of {self.nkpt}', end='\r')
|
|
374
|
-
if j+1 == self.nkpt:
|
|
375
|
-
print('\n', end='')
|
|
376
|
-
eigenvalues:list[float] = []
|
|
377
|
-
wfk.read(4)
|
|
378
|
-
npw = bytes2int(wfk.read(4))
|
|
379
|
-
self.nspinor = bytes2int(wfk.read(4))
|
|
380
|
-
nband_temp = bytes2int(wfk.read(4))
|
|
381
|
-
wfk.read(8)
|
|
382
|
-
wfk.read(12*npw)
|
|
383
|
-
wfk.read(8)
|
|
384
|
-
#------------------------------------------------------------------#
|
|
385
|
-
# only need eigenvalues for Fermi surface, skip over everything else
|
|
386
|
-
for nband in range(nband_temp):
|
|
387
|
-
eigenval = bytes2float(wfk.read(8))
|
|
388
|
-
eigenvalues.append(eigenval)
|
|
389
|
-
wfk.read(nband_temp*8)
|
|
390
|
-
wfk.read(4)
|
|
391
|
-
wfk.read(nband_temp*(8 + npw*16))
|
|
392
|
-
yield wc.WFK(
|
|
393
|
-
eigenvalues=np.array(eigenvalues),
|
|
394
|
-
kpoints=np.array(self.kpts[j]),
|
|
395
|
-
nkpt=self.nkpt,
|
|
396
|
-
nbands=self.bands[0],
|
|
397
|
-
ngfftx=self.ngfftx,
|
|
398
|
-
ngffty=self.ngffty,
|
|
399
|
-
ngfftz=self.ngfftz,
|
|
400
|
-
symrel=np.array(self.symrel),
|
|
401
|
-
nsym=self.nsym,
|
|
402
|
-
lattice=self.real_lattice,
|
|
403
|
-
natom=self.natom,
|
|
404
|
-
xred=np.array(self.xred),
|
|
405
|
-
typat=self.typat,
|
|
406
|
-
znucltypat=self.znucltypat,
|
|
407
|
-
fermi_energy=self.fermi,
|
|
408
|
-
non_symm_vecs=np.array(self.tnons),
|
|
409
|
-
time_reversal=self.time_reversal
|
|
410
|
-
)
|
|
411
|
-
print('WFK body read')
|
|
412
|
-
wfk.close()
|
|
413
|
-
|
|
414
|
-
#---------------------------------------------------------------------------------------------------------------------#
|
|
415
|
-
#----------------------------------------------------- END CLASS -----------------------------------------------------#
|
|
416
|
-
#---------------------------------------------------------------------------------------------------------------------#
|
|
417
|
-
|
|
418
|
-
class Abinit10WFK():
|
|
419
|
-
def __init__(
|
|
420
|
-
self, filename:str
|
|
421
|
-
):
|
|
422
|
-
self.filename = filename
|
|
423
|
-
self._ReadHeader()
|
|
424
|
-
#---------------------------------------------------------------------------------------------------------------------#
|
|
425
|
-
#------------------------------------------------------ METHODS ------------------------------------------------------#
|
|
426
|
-
#---------------------------------------------------------------------------------------------------------------------#
|
|
427
|
-
# method to read wavefunction header for abinit version 7
|
|
428
|
-
def _ReadHeader(
|
|
429
|
-
self
|
|
430
|
-
)->None:
|
|
431
|
-
wfk = open(self.filename, 'rb')
|
|
432
|
-
print('Reading WFK header')
|
|
433
|
-
#---------------#
|
|
434
|
-
# unpack integers
|
|
435
|
-
self.header = bytes2int(wfk.read(4))
|
|
436
|
-
self.version = wfk.read(6).decode()
|
|
437
|
-
wfk.read(2)
|
|
438
|
-
self.headform = bytes2int(wfk.read(4))
|
|
439
|
-
self.fform = bytes2int(wfk.read(4))
|
|
440
|
-
wfk.read(8)
|
|
441
|
-
self.bandtot = bytes2int(wfk.read(4))
|
|
442
|
-
self.date = bytes2int(wfk.read(4))
|
|
443
|
-
self.intxc = bytes2int(wfk.read(4))
|
|
444
|
-
self.ixc = bytes2int(wfk.read(4))
|
|
445
|
-
self.natom = bytes2int(wfk.read(4))
|
|
446
|
-
self.ngfftx = bytes2int(wfk.read(4))
|
|
447
|
-
self.ngffty = bytes2int(wfk.read(4))
|
|
448
|
-
self.ngfftz = bytes2int(wfk.read(4))
|
|
449
|
-
self.nkpt = bytes2int(wfk.read(4))
|
|
450
|
-
self.nspden = bytes2int(wfk.read(4))
|
|
451
|
-
self.nspinor = bytes2int(wfk.read(4))
|
|
452
|
-
self.nsppol = bytes2int(wfk.read(4))
|
|
453
|
-
self.nsym = bytes2int(wfk.read(4))
|
|
454
|
-
self.npsp = bytes2int(wfk.read(4))
|
|
455
|
-
self.ntypat = bytes2int(wfk.read(4))
|
|
456
|
-
self.occopt = bytes2int(wfk.read(4))
|
|
457
|
-
self.pertcase = bytes2int(wfk.read(4))
|
|
458
|
-
self.usepaw = bytes2int(wfk.read(4))
|
|
459
|
-
#--------------#
|
|
460
|
-
# unpack doubles
|
|
461
|
-
self.ecut = bytes2float(wfk.read(8))
|
|
462
|
-
self.ecutdg = bytes2float(wfk.read(8))
|
|
463
|
-
self.ecutsm = bytes2float(wfk.read(8))
|
|
464
|
-
self.ecut_eff = bytes2float(wfk.read(8))
|
|
465
|
-
self.qptnx = bytes2float(wfk.read(8))
|
|
466
|
-
self.qptny = bytes2float(wfk.read(8))
|
|
467
|
-
self.qptnz = bytes2float(wfk.read(8))
|
|
468
|
-
rprimd_ax = bytes2float(wfk.read(8))
|
|
469
|
-
rprimd_ay = bytes2float(wfk.read(8))
|
|
470
|
-
rprimd_az = bytes2float(wfk.read(8))
|
|
471
|
-
rprimd_bx = bytes2float(wfk.read(8))
|
|
472
|
-
rprimd_by = bytes2float(wfk.read(8))
|
|
473
|
-
rprimd_bz = bytes2float(wfk.read(8))
|
|
474
|
-
rprimd_cx = bytes2float(wfk.read(8))
|
|
475
|
-
rprimd_cy = bytes2float(wfk.read(8))
|
|
476
|
-
rprimd_cz = bytes2float(wfk.read(8))
|
|
477
|
-
#------------------------#
|
|
478
|
-
# convert Bohr to Angstrom
|
|
479
|
-
self.real_lattice = 0.529177*np.array([
|
|
480
|
-
[rprimd_ax, rprimd_ay, rprimd_az],
|
|
481
|
-
[rprimd_bx, rprimd_by, rprimd_bz],
|
|
482
|
-
[rprimd_cx, rprimd_cy, rprimd_cz]
|
|
483
|
-
]).reshape((3,3))
|
|
484
|
-
self.stmbias = bytes2float(wfk.read(8))
|
|
485
|
-
self.tphysel = bytes2float(wfk.read(8))
|
|
486
|
-
self.tsmear = bytes2float(wfk.read(8))
|
|
487
|
-
#---------------#
|
|
488
|
-
# unpack integers
|
|
489
|
-
self.usewvl = bytes2int(wfk.read(4))
|
|
490
|
-
self.nshiftk_orig = bytes2int(wfk.read(4))
|
|
491
|
-
self.nshiftk = bytes2int(wfk.read(4))
|
|
492
|
-
self.mband = bytes2int(wfk.read(4))
|
|
493
|
-
wfk.read(8)
|
|
494
|
-
self.istwfk:list[int] = []
|
|
495
|
-
for _ in range(self.nkpt):
|
|
496
|
-
val = bytes2int(wfk.read(4))
|
|
497
|
-
self.istwfk.append(val)
|
|
498
|
-
self.bands:list[int] = []
|
|
499
|
-
for _ in range(self.nkpt*self.nsppol):
|
|
500
|
-
val = bytes2int(wfk.read(4))
|
|
501
|
-
self.bands.append(val)
|
|
502
|
-
self.npwarr:list[int] = []
|
|
503
|
-
for _ in range(self.nkpt):
|
|
504
|
-
val = bytes2int(wfk.read(4))
|
|
505
|
-
self.npwarr.append(val)
|
|
506
|
-
self.so_psp:list[int] = []
|
|
507
|
-
for _ in range(self.npsp):
|
|
508
|
-
val = bytes2int(wfk.read(4))
|
|
509
|
-
self.so_psp.append(val)
|
|
510
|
-
self.symafm:list[int] = []
|
|
511
|
-
for _ in range(self.nsym):
|
|
512
|
-
val = bytes2int(wfk.read(4))
|
|
513
|
-
self.symafm.append(val)
|
|
514
|
-
self.symrel:list[np.ndarray] = []
|
|
515
|
-
for _ in range(self.nsym):
|
|
516
|
-
arr = np.zeros((3,3))
|
|
517
|
-
arr[0,0] = bytes2int(wfk.read(4))
|
|
518
|
-
arr[1,0] = bytes2int(wfk.read(4))
|
|
519
|
-
arr[2,0] = bytes2int(wfk.read(4))
|
|
520
|
-
arr[0,1] = bytes2int(wfk.read(4))
|
|
521
|
-
arr[1,1] = bytes2int(wfk.read(4))
|
|
522
|
-
arr[2,1] = bytes2int(wfk.read(4))
|
|
523
|
-
arr[0,2] = bytes2int(wfk.read(4))
|
|
524
|
-
arr[1,2] = bytes2int(wfk.read(4))
|
|
525
|
-
arr[2,2] = bytes2int(wfk.read(4))
|
|
526
|
-
self.symrel.append(arr)
|
|
527
|
-
self.typat:list[int] = []
|
|
528
|
-
for _ in range(self.natom):
|
|
529
|
-
val = bytes2int(wfk.read(4))
|
|
530
|
-
self.typat.append(val)
|
|
531
|
-
#--------------#
|
|
532
|
-
# unpack doubles
|
|
533
|
-
self.kpts:list[np.ndarray] = []
|
|
534
|
-
for _ in range(self.nkpt):
|
|
535
|
-
vec = np.zeros(3)
|
|
536
|
-
vec[0] = bytes2float(wfk.read(8))
|
|
537
|
-
vec[1] = bytes2float(wfk.read(8))
|
|
538
|
-
vec[2] = bytes2float(wfk.read(8))
|
|
539
|
-
self.kpts.append(vec)
|
|
540
|
-
self.occ:list[float] = []
|
|
541
|
-
for _ in range(self.bandtot):
|
|
542
|
-
val = bytes2float(wfk.read(8))
|
|
543
|
-
self.occ.append(val)
|
|
544
|
-
self.tnons:list[np.ndarray] = []
|
|
545
|
-
for _ in range(self.nsym):
|
|
546
|
-
vec = np.zeros(3)
|
|
547
|
-
vec[0] = bytes2float(wfk.read(8))
|
|
548
|
-
vec[1] = bytes2float(wfk.read(8))
|
|
549
|
-
vec[2] = bytes2float(wfk.read(8))
|
|
550
|
-
self.tnons.append(vec)
|
|
551
|
-
self.znucltypat:list[float] = []
|
|
552
|
-
for _ in range(self.ntypat):
|
|
553
|
-
val = bytes2float(wfk.read(8))
|
|
554
|
-
self.znucltypat.append(val)
|
|
555
|
-
self.wtk:list[float] = []
|
|
556
|
-
for _ in range(self.nkpt):
|
|
557
|
-
val = bytes2float(wfk.read(8))
|
|
558
|
-
self.wtk.append(val)
|
|
559
|
-
#------------------#
|
|
560
|
-
# unpack coordinates
|
|
561
|
-
wfk.read(8)
|
|
562
|
-
self.residm = bytes2float(wfk.read(8))
|
|
563
|
-
self.xred:list[np.ndarray] = []
|
|
564
|
-
for _ in range(self.natom):
|
|
565
|
-
vec = np.zeros(3)
|
|
566
|
-
vec[0] = bytes2float(wfk.read(8))
|
|
567
|
-
vec[1] = bytes2float(wfk.read(8))
|
|
568
|
-
vec[2] = bytes2float(wfk.read(8))
|
|
569
|
-
self.xred.append(vec)
|
|
570
|
-
#------------------------------------#
|
|
571
|
-
# unpack total energy and fermi energy
|
|
572
|
-
self.etotal = bytes2float(wfk.read(8))
|
|
573
|
-
self.fermi = bytes2float(wfk.read(8))
|
|
574
|
-
#-------------#
|
|
575
|
-
# unpack floats
|
|
576
|
-
self.amu:list[float] = []
|
|
577
|
-
for _ in range(self.ntypat):
|
|
578
|
-
val = bytes2float(wfk.read(8))
|
|
579
|
-
self.amu.append(val)
|
|
580
|
-
#----------------------------#
|
|
581
|
-
# unpack reciprocal space info
|
|
582
|
-
wfk.read(8)
|
|
583
|
-
self.kptopt = bytes2int(wfk.read(4))
|
|
584
|
-
self.pawcpxocc = bytes2int(wfk.read(4))
|
|
585
|
-
self.nelect = bytes2float(wfk.read(8))
|
|
586
|
-
self.cellcharge = bytes2float(wfk.read(8))
|
|
587
|
-
self.icoulomb = bytes2int(wfk.read(4))
|
|
588
|
-
rec_latt_ax = bytes2int(wfk.read(4))
|
|
589
|
-
rec_latt_ay = bytes2int(wfk.read(4))
|
|
590
|
-
rec_latt_az = bytes2int(wfk.read(4))
|
|
591
|
-
rec_latt_bx = bytes2int(wfk.read(4))
|
|
592
|
-
rec_latt_by = bytes2int(wfk.read(4))
|
|
593
|
-
rec_latt_bz = bytes2int(wfk.read(4))
|
|
594
|
-
rec_latt_cx = bytes2int(wfk.read(4))
|
|
595
|
-
rec_latt_cy = bytes2int(wfk.read(4))
|
|
596
|
-
rec_latt_cz = bytes2int(wfk.read(4))
|
|
597
|
-
self.kptr_latt = np.array([
|
|
598
|
-
[rec_latt_ax, rec_latt_ay, rec_latt_az],
|
|
599
|
-
[rec_latt_bx, rec_latt_by, rec_latt_bz],
|
|
600
|
-
[rec_latt_cx, rec_latt_cy, rec_latt_cz]
|
|
601
|
-
]).reshape((3,3))
|
|
602
|
-
rec_latt_ax = bytes2int(wfk.read(4))
|
|
603
|
-
rec_latt_ay = bytes2int(wfk.read(4))
|
|
604
|
-
rec_latt_az = bytes2int(wfk.read(4))
|
|
605
|
-
rec_latt_bx = bytes2int(wfk.read(4))
|
|
606
|
-
rec_latt_by = bytes2int(wfk.read(4))
|
|
607
|
-
rec_latt_bz = bytes2int(wfk.read(4))
|
|
608
|
-
rec_latt_cx = bytes2int(wfk.read(4))
|
|
609
|
-
rec_latt_cy = bytes2int(wfk.read(4))
|
|
610
|
-
rec_latt_cz = bytes2int(wfk.read(4))
|
|
611
|
-
self.kptr_latt_orig = np.array([
|
|
612
|
-
[rec_latt_ax, rec_latt_ay, rec_latt_az],
|
|
613
|
-
[rec_latt_bx, rec_latt_by, rec_latt_bz],
|
|
614
|
-
[rec_latt_cx, rec_latt_cy, rec_latt_cz]
|
|
615
|
-
]).reshape((3,3))
|
|
616
|
-
shift_orig_x = bytes2float(wfk.read(8))
|
|
617
|
-
shift_orig_y = bytes2float(wfk.read(8))
|
|
618
|
-
shift_orig_z = bytes2float(wfk.read(8))
|
|
619
|
-
self.origin_shift = [shift_orig_x, shift_orig_y, shift_orig_z]
|
|
620
|
-
shift_kx = bytes2float(wfk.read(8))
|
|
621
|
-
shift_ky = bytes2float(wfk.read(8))
|
|
622
|
-
shift_kz = bytes2float(wfk.read(8))
|
|
623
|
-
self.kpt_shift = [shift_kx, shift_ky, shift_kz]
|
|
624
|
-
#-----------------------#
|
|
625
|
-
# unpack pseudopotentials
|
|
626
|
-
wfk.read(4)
|
|
627
|
-
self.title:list[str] = []
|
|
628
|
-
self.znuclpsp:list[float] = []
|
|
629
|
-
self.zionpsp:list[float] = []
|
|
630
|
-
self.pspso:list[int] = []
|
|
631
|
-
self.pspdat:list[int] = []
|
|
632
|
-
self.pspcod:list[int] = []
|
|
633
|
-
self.pspxc:list[int] = []
|
|
634
|
-
self.lmn_size:list[int] = []
|
|
635
|
-
self.md5_pseudos:list[str] = []
|
|
636
|
-
for _ in range(self.npsp):
|
|
637
|
-
wfk.read(4)
|
|
638
|
-
self.title.append(wfk.read(132).decode())
|
|
639
|
-
self.znuclpsp.append(bytes2float(wfk.read(8)))
|
|
640
|
-
self.zionpsp.append(bytes2float(wfk.read(8)))
|
|
641
|
-
self.pspso.append(bytes2int(wfk.read(4)))
|
|
642
|
-
self.pspdat.append(bytes2int(wfk.read(4)))
|
|
643
|
-
self.pspcod.append(bytes2int(wfk.read(4)))
|
|
644
|
-
self.pspxc.append(bytes2int(wfk.read(4)))
|
|
645
|
-
self.lmn_size.append(bytes2int(wfk.read(4)))
|
|
646
|
-
self.md5_pseudos.append(wfk.read(32).decode())
|
|
647
|
-
wfk.read(4)
|
|
648
|
-
#---------------#
|
|
649
|
-
# unpack PAW data
|
|
650
|
-
if self.usepaw == 1:
|
|
651
|
-
wfk.read(4)
|
|
652
|
-
self.nrho:list[int] = []
|
|
653
|
-
for _ in range(self.natom):
|
|
654
|
-
for _ in range(self.nspden):
|
|
655
|
-
nrhoij = bytes2int(wfk.read(4))
|
|
656
|
-
self.nrho.append(nrhoij)
|
|
657
|
-
self.cplex = bytes2int(wfk.read(4))
|
|
658
|
-
self.nspden_paw = bytes2int(wfk.read(4))
|
|
659
|
-
wfk.read(8)
|
|
660
|
-
self.irho:list[int] = []
|
|
661
|
-
for i in range(self.natom):
|
|
662
|
-
for _ in range(self.nspden_paw):
|
|
663
|
-
nrhoij = self.nrho[i]
|
|
664
|
-
for _ in range(nrhoij):
|
|
665
|
-
rhoij = bytes2int(wfk.read(4))
|
|
666
|
-
self.irho.append(rhoij)
|
|
667
|
-
wfk.read(4)
|
|
668
|
-
self.rho:list[float] = []
|
|
669
|
-
for i in range(self.natom):
|
|
670
|
-
for _ in range(self.nspden_paw):
|
|
671
|
-
nrhoij = self.nrho[i]
|
|
672
|
-
for _ in range(nrhoij):
|
|
673
|
-
rhoij = bytes2float(wfk.read(8))
|
|
674
|
-
self.rho.append(rhoij)
|
|
675
|
-
wfk.read(4)
|
|
676
|
-
print('WFK header read')
|
|
677
|
-
wfk.close()
|
|
678
|
-
#-----------------------------------------------------------------------------------------------------------------#
|
|
679
|
-
# check for time reversal symmetry
|
|
680
|
-
def _CheckTimeRev(
|
|
681
|
-
self
|
|
682
|
-
):
|
|
683
|
-
# if system is centrosymmetric, do not double reciprocal symmetry operations
|
|
684
|
-
if True in [np.array_equal(-np.identity(3),mat) for mat in self.symrel]:
|
|
685
|
-
self.time_reversal = False
|
|
686
|
-
else:
|
|
687
|
-
self.time_reversal = True
|
|
688
|
-
print((
|
|
689
|
-
'Noncentrosymmetric system identified, assuming time reversal symmetry\n'
|
|
690
|
-
'To change this, set "check_time_rev" keyword to False when calling ReadWFK() or ReadEigenvalues()'
|
|
691
|
-
))
|
|
692
|
-
#-----------------------------------------------------------------------------------------------------------------#
|
|
693
|
-
# method to read entire body of abinit version 7 wavefunction file
|
|
694
|
-
def ReadWFK(
|
|
695
|
-
self, energy_level=np.nan, width=np.nan, check_time_rev:bool=True
|
|
696
|
-
)->Generator[wc.WFK, None, None]:
|
|
697
|
-
'''
|
|
698
|
-
Method that constructs WFK objects from ABINIT v10 WFK file.
|
|
699
|
-
|
|
700
|
-
Parameters
|
|
701
|
-
----------
|
|
702
|
-
energy_level : float
|
|
703
|
-
The energy level to pull plane wave coefficients from
|
|
704
|
-
This is relative to the Fermi energy
|
|
705
|
-
If not defined but width is defined, default is 0 Hartree
|
|
706
|
-
|
|
707
|
-
width : float
|
|
708
|
-
Defines range about energy_level to search for plane wave coefficients
|
|
709
|
-
Total range is equal to width, so look width/2 above and below energy_level
|
|
710
|
-
If not defined but energy_level is defined, default is 0.005 Hartree
|
|
711
|
-
'''
|
|
712
|
-
# check for time reversal symmetry
|
|
713
|
-
if check_time_rev:
|
|
714
|
-
self._CheckTimeRev()
|
|
715
|
-
else:
|
|
716
|
-
self.time_reversal = False
|
|
717
|
-
# read wfk
|
|
718
|
-
wfk = open(self.filename, 'rb')
|
|
719
|
-
skip = True
|
|
720
|
-
if energy_level is np.nan:
|
|
721
|
-
if width is np.nan:
|
|
722
|
-
skip = False
|
|
723
|
-
else:
|
|
724
|
-
energy_level = 0
|
|
725
|
-
else:
|
|
726
|
-
if width is np.nan:
|
|
727
|
-
width = 0.005
|
|
728
|
-
#-----------#
|
|
729
|
-
# skip header
|
|
730
|
-
wfk.read(468)
|
|
731
|
-
wfk.read(4*(2*self.nkpt + self.nkpt*self.nsppol + self.npsp + 10*self.nsym + self.natom))
|
|
732
|
-
wfk.read(8*(4*self.nkpt + self.bandtot + 3*self.nsym + 2*self.ntypat + 3*self.natom))
|
|
733
|
-
wfk.read(self.npsp*(208))
|
|
734
|
-
if self.usepaw == 1:
|
|
735
|
-
wfk.read(4)
|
|
736
|
-
for _ in range(self.natom):
|
|
737
|
-
for _ in range(self.nspden):
|
|
738
|
-
_ = wfk.read(4)
|
|
739
|
-
wfk.read(4)
|
|
740
|
-
wfk.read(4)
|
|
741
|
-
wfk.read(8)
|
|
742
|
-
for i in range(self.natom):
|
|
743
|
-
for _ in range(self.nspden_paw):
|
|
744
|
-
nrhoij = self.nrho[i]
|
|
745
|
-
for _ in range(nrhoij):
|
|
746
|
-
_ = wfk.read(4)
|
|
747
|
-
wfk.read(4)
|
|
748
|
-
for i in range(self.natom):
|
|
749
|
-
for _ in range(self.nspden_paw):
|
|
750
|
-
nrhoij = self.nrho[i]
|
|
751
|
-
for _ in range(nrhoij):
|
|
752
|
-
_ = wfk.read(8)
|
|
753
|
-
wfk.read(4)
|
|
754
|
-
#-------------------------------#
|
|
755
|
-
# begin reading wavefunction body
|
|
756
|
-
for i in range(self.nsppol):
|
|
757
|
-
for j in range(self.nkpt):
|
|
758
|
-
print(f'Reading kpoint {j+1} of {self.nkpt}', end='\r')
|
|
759
|
-
if j+1 == self.nkpt:
|
|
760
|
-
print('\n', end='')
|
|
761
|
-
wfk.read(4)
|
|
762
|
-
npw = bytes2int(wfk.read(4))
|
|
763
|
-
self.nspinor = bytes2int(wfk.read(4))
|
|
764
|
-
nband_temp = bytes2int(wfk.read(4))
|
|
765
|
-
pw_indices = np.zeros((npw,3), dtype=int)
|
|
766
|
-
eigenvalues = np.zeros(nband_temp, dtype=float)
|
|
767
|
-
wfk.read(8)
|
|
768
|
-
for pw in range(npw):
|
|
769
|
-
kx = bytes2int(wfk.read(4))
|
|
770
|
-
ky = bytes2int(wfk.read(4))
|
|
771
|
-
kz = bytes2int(wfk.read(4))
|
|
772
|
-
pw_indices[pw,:] = [kx, ky, kz]
|
|
773
|
-
wfk.read(8)
|
|
774
|
-
for nband in range(nband_temp):
|
|
775
|
-
eigenval = bytes2float(wfk.read(8))
|
|
776
|
-
eigenvalues[nband] = eigenval
|
|
777
|
-
# skip reading coefficients if they energy level of interest is not in band
|
|
778
|
-
if skip:
|
|
779
|
-
min_val = self.fermi + energy_level - width/2
|
|
780
|
-
max_val = self.fermi + energy_level + width/2
|
|
781
|
-
eig_found = False
|
|
782
|
-
for eigval in eigenvalues:
|
|
783
|
-
if min_val <= eigval <= max_val:
|
|
784
|
-
eig_found = True
|
|
785
|
-
break
|
|
786
|
-
if eig_found:
|
|
787
|
-
yield self._ReadCoeffs(
|
|
788
|
-
wfk,
|
|
789
|
-
nband_temp,
|
|
790
|
-
npw,
|
|
791
|
-
eigenvalues,
|
|
792
|
-
pw_indices,
|
|
793
|
-
ind=j
|
|
794
|
-
)
|
|
795
|
-
else:
|
|
796
|
-
wfk.read(nband_temp*8)
|
|
797
|
-
wfk.read(4)
|
|
798
|
-
wfk.read(nband_temp*(8 + npw*16))
|
|
799
|
-
# if not skip, read full wavefunction
|
|
800
|
-
else:
|
|
801
|
-
yield self._ReadCoeffs(
|
|
802
|
-
wfk,
|
|
803
|
-
nband_temp,
|
|
804
|
-
npw,
|
|
805
|
-
eigenvalues,
|
|
806
|
-
pw_indices,
|
|
807
|
-
ind=j
|
|
808
|
-
)
|
|
809
|
-
print('WFK body read')
|
|
810
|
-
wfk.close()
|
|
811
|
-
#-----------------------------------------------------------------------------------------------------------------#
|
|
812
|
-
# method to read in plane wave coefficients
|
|
813
|
-
def _ReadCoeffs(
|
|
814
|
-
self, wfk_file, nbands, npws, eigs, pw_inds, ind
|
|
815
|
-
)->wc.WFK:
|
|
816
|
-
occupancies = np.zeros((1,nbands), dtype=float)
|
|
817
|
-
coeffs = np.zeros((nbands,npws), dtype=complex)
|
|
818
|
-
for nband in range(nbands):
|
|
819
|
-
occ = bytes2float(wfk_file.read(8))
|
|
820
|
-
occupancies[:,nband] = occ
|
|
821
|
-
wfk_file.read(4)
|
|
822
|
-
for nband in range(nbands):
|
|
823
|
-
wfk_file.read(4)
|
|
824
|
-
cg:np.ndarray = np.zeros((1,npws), dtype=complex)
|
|
825
|
-
for pw in range(npws):
|
|
826
|
-
cg1 = bytes2float(wfk_file.read(8))
|
|
827
|
-
cg2 = bytes2float(wfk_file.read(8))
|
|
828
|
-
cg[:,pw] = cg1 + 1j*cg2
|
|
829
|
-
coeffs[nband,:] = cg
|
|
830
|
-
wfk_file.read(4)
|
|
831
|
-
return wc.WFK(
|
|
832
|
-
eigenvalues=np.array(eigs),
|
|
833
|
-
wfk_coeffs=np.array(coeffs),
|
|
834
|
-
pw_indices=np.array(pw_inds),
|
|
835
|
-
kpoints=np.array(self.kpts[ind]),
|
|
836
|
-
nkpt=self.nkpt,
|
|
837
|
-
nbands=self.bands[0],
|
|
838
|
-
ngfftx=self.ngfftx,
|
|
839
|
-
ngffty=self.ngffty,
|
|
840
|
-
ngfftz=self.ngfftz,
|
|
841
|
-
symrel=np.array(self.symrel),
|
|
842
|
-
nsym=self.nsym,
|
|
843
|
-
lattice=self.real_lattice,
|
|
844
|
-
natom=self.natom,
|
|
845
|
-
xred=np.array(self.xred),
|
|
846
|
-
typat=self.typat,
|
|
847
|
-
znucltypat=self.znucltypat,
|
|
848
|
-
fermi_energy=self.fermi,
|
|
849
|
-
non_symm_vecs=np.array(self.tnons),
|
|
850
|
-
time_reversal=self.time_reversal
|
|
851
|
-
)
|
|
852
|
-
#-----------------------------------------------------------------------------------------------------------------#
|
|
853
|
-
# method to read only eigenvalues from body of abinit version 10 wavefunction file
|
|
854
|
-
def ReadEigenvalues(
|
|
855
|
-
self, check_time_rev:bool=True
|
|
856
|
-
)->Generator[wc.WFK, None, None]:
|
|
857
|
-
'''
|
|
858
|
-
Method that constructs WFK objects from ABINIT v10 WFK file.
|
|
859
|
-
'''
|
|
860
|
-
# check for time reversal symmetry
|
|
861
|
-
if check_time_rev:
|
|
862
|
-
self._CheckTimeRev()
|
|
863
|
-
else:
|
|
864
|
-
self.time_reversal = False
|
|
865
|
-
# read wfk
|
|
866
|
-
wfk = open(self.filename, 'rb')
|
|
867
|
-
#-----------#
|
|
868
|
-
# skip header
|
|
869
|
-
wfk.read(468)
|
|
870
|
-
wfk.read(4*(2*self.nkpt + self.nkpt*self.nsppol + self.npsp + 10*self.nsym + self.natom))
|
|
871
|
-
wfk.read(8*(4*self.nkpt + self.bandtot + 3*self.nsym + 2*self.ntypat + 3*self.natom))
|
|
872
|
-
wfk.read(self.npsp*(208))
|
|
873
|
-
if self.usepaw == 1:
|
|
874
|
-
wfk.read(4)
|
|
875
|
-
for _ in range(self.natom):
|
|
876
|
-
for _ in range(self.nspden):
|
|
877
|
-
_ = wfk.read(4)
|
|
878
|
-
wfk.read(4)
|
|
879
|
-
wfk.read(4)
|
|
880
|
-
wfk.read(8)
|
|
881
|
-
for i in range(self.natom):
|
|
882
|
-
for _ in range(self.nspden_paw):
|
|
883
|
-
nrhoij = self.nrho[i]
|
|
884
|
-
for _ in range(nrhoij):
|
|
885
|
-
_ = wfk.read(4)
|
|
886
|
-
wfk.read(4)
|
|
887
|
-
for i in range(self.natom):
|
|
888
|
-
for _ in range(self.nspden_paw):
|
|
889
|
-
nrhoij = self.nrho[i]
|
|
890
|
-
for _ in range(nrhoij):
|
|
891
|
-
_ = wfk.read(8)
|
|
892
|
-
wfk.read(4)
|
|
893
|
-
#-------------------------------#
|
|
894
|
-
# begin reading wavefunction body
|
|
895
|
-
for i in range(self.nsppol):
|
|
896
|
-
for j in range(self.nkpt):
|
|
897
|
-
print(f'Reading kpoint {j+1} of {self.nkpt}', end='\r')
|
|
898
|
-
if j+1 == self.nkpt:
|
|
899
|
-
print('\n', end='')
|
|
900
|
-
wfk.read(4)
|
|
901
|
-
npw = bytes2int(wfk.read(4))
|
|
902
|
-
self.nspinor = bytes2int(wfk.read(4))
|
|
903
|
-
nband_temp = bytes2int(wfk.read(4))
|
|
904
|
-
eigenvalues = np.zeros((1,nband_temp), dtype=float)
|
|
905
|
-
wfk.read(8)
|
|
906
|
-
wfk.read(12*npw)
|
|
907
|
-
wfk.read(8)
|
|
908
|
-
#------------------------------------------------------------------#
|
|
909
|
-
# only need eigenvalues for Fermi surface, skip over everything else
|
|
910
|
-
for nband in range(nband_temp):
|
|
911
|
-
eigenval = bytes2float(wfk.read(8))
|
|
912
|
-
eigenvalues[:,nband] = eigenval
|
|
913
|
-
wfk.read(nband_temp*8)
|
|
914
|
-
wfk.read(4)
|
|
915
|
-
wfk.read(nband_temp*(8 + npw*16))
|
|
916
|
-
yield wc.WFK(
|
|
917
|
-
eigenvalues=np.array(eigenvalues),
|
|
918
|
-
kpoints=np.array(self.kpts[j]),
|
|
919
|
-
nkpt=self.nkpt,
|
|
920
|
-
nbands=self.bands[0],
|
|
921
|
-
ngfftx=self.ngfftx,
|
|
922
|
-
ngffty=self.ngffty,
|
|
923
|
-
ngfftz=self.ngfftz,
|
|
924
|
-
symrel=np.array(self.symrel),
|
|
925
|
-
nsym=self.nsym,
|
|
926
|
-
lattice=self.real_lattice,
|
|
927
|
-
natom=self.natom,
|
|
928
|
-
xred=np.array(self.xred),
|
|
929
|
-
typat=self.typat,
|
|
930
|
-
znucltypat=self.znucltypat,
|
|
931
|
-
fermi_energy=self.fermi,
|
|
932
|
-
non_symm_vecs=np.array(self.tnons),
|
|
933
|
-
time_reversal=self.time_reversal
|
|
934
|
-
)
|
|
935
|
-
print('WFK body read')
|
|
936
|
-
wfk.close()
|
|
937
|
-
|
|
938
|
-
#---------------------------------------------------------------------------------------------------------------------#
|
|
939
|
-
#----------------------------------------------------- END CLASS -----------------------------------------------------#
|
|
940
|
-
#---------------------------------------------------------------------------------------------------------------------#
|
|
941
|
-
|
|
942
|
-
class AbinitNetCDF():
|
|
943
|
-
def __init__(
|
|
944
|
-
self, filename:str
|
|
945
|
-
):
|
|
946
|
-
print('WFK found with netCDF4 format')
|
|
947
|
-
self.filename = filename
|
|
948
|
-
self.dataset = self._GetDataset()
|
|
949
|
-
self.kpts:np.ndarray = self.dataset.variables['reduced_coordinates_of_kpoints'][:]
|
|
950
|
-
self.nkpt:int = self.dataset.dimensions['number_of_kpoints'].size
|
|
951
|
-
self.xred:np.ndarray = self.dataset.variables['reduced_atom_positions'][:]
|
|
952
|
-
self.natom:int = self.dataset.dimensions['number_of_atoms'].size
|
|
953
|
-
self.tnons:np.ndarray = self.dataset.variables['reduced_symmetry_translations'][:]
|
|
954
|
-
self.symrel:np.ndarray = self.dataset.variables['reduced_symmetry_matrices'][:]
|
|
955
|
-
self.symrel = np.array([op.T for op in self.symrel])
|
|
956
|
-
self.nsym:int = self.dataset.dimensions['number_of_symmetry_operations'].size
|
|
957
|
-
self.fermi:float = self.dataset['fermi_energy'][:]
|
|
958
|
-
self.real_lattice:np.ndarray = self.dataset['primitive_vectors'][:]
|
|
959
|
-
self.real_lattice *= 0.529177
|
|
960
|
-
self.ngfftx:int = self.dataset.dimensions['number_of_grid_points_vector1'].size
|
|
961
|
-
self.ngffty:int = self.dataset.dimensions['number_of_grid_points_vector2'].size
|
|
962
|
-
self.ngfftz:int = self.dataset.dimensions['number_of_grid_points_vector3'].size
|
|
963
|
-
self.nband:int = int(self.dataset.dimensions['bantot'].size / self.nkpt)
|
|
964
|
-
self.typat:list = self.dataset.variables['atom_species'][:].tolist()
|
|
965
|
-
self.zion:np.ndarray = self.dataset.variables['atomic_numbers'][:]
|
|
966
|
-
self.znucltypat:list = [int(nuc) for nuc in self.zion]
|
|
967
|
-
self.bands = [self.nband]
|
|
968
|
-
#---------------------------------------------------------------------------------------------------------------------#
|
|
969
|
-
#------------------------------------------------------ METHODS ------------------------------------------------------#
|
|
970
|
-
#---------------------------------------------------------------------------------------------------------------------#
|
|
971
|
-
# create netCDF dataset
|
|
972
|
-
def _GetDataset(
|
|
973
|
-
self
|
|
974
|
-
):
|
|
975
|
-
from netCDF4 import Dataset
|
|
976
|
-
return Dataset(self.filename,format='NETCDF4')
|
|
977
|
-
#-----------------------------------------------------------------------------------------------------------------#
|
|
978
|
-
# check for time reversal symmetry
|
|
979
|
-
def _CheckTimeRev(
|
|
980
|
-
self
|
|
981
|
-
):
|
|
982
|
-
# if system is centrosymmetric, do not double reciprocal symmetry operations
|
|
983
|
-
if True in [np.array_equal(-np.identity(3),mat) for mat in self.symrel]:
|
|
984
|
-
self.time_reversal = False
|
|
985
|
-
else:
|
|
986
|
-
self.time_reversal = True
|
|
987
|
-
print((
|
|
988
|
-
'Noncentrosymmetric system identified, assuming time reversal symmetry\n'
|
|
989
|
-
'To change this, set "check_time_rev" keyword to False when calling ReadWFK() or ReadEigenvalues()'
|
|
990
|
-
))
|
|
991
|
-
#-----------------------------------------------------------------------------------------------------------------#
|
|
992
|
-
# fetch wavefunction coefficients from netcdf dataset
|
|
993
|
-
def ReadWFK(
|
|
994
|
-
self, energy_level:float=np.nan, width:float=np.nan, check_time_rev:bool=True
|
|
995
|
-
)->Generator[wc.WFK, None, None]:
|
|
996
|
-
# check for time reversal symmetry
|
|
997
|
-
if check_time_rev:
|
|
998
|
-
self._CheckTimeRev()
|
|
999
|
-
else:
|
|
1000
|
-
self.time_reversal = False
|
|
1001
|
-
eigs:np.ndarray = self.dataset.variables['eigenvalues'][:][0]
|
|
1002
|
-
pw_inds:np.ndarray = self.dataset.variables['reduced_coordinates_of_plane_waves'][:]
|
|
1003
|
-
# restructure wavefunction coefficients
|
|
1004
|
-
for kpt in range(self.nkpt):
|
|
1005
|
-
print(f'Reading kpoint {kpt+1} of {self.nkpt}', end='\r')
|
|
1006
|
-
kpt_coeffs = []
|
|
1007
|
-
# format plane wave indices
|
|
1008
|
-
kpt_pw_inds = pw_inds[kpt]
|
|
1009
|
-
kpt_pw_inds = kpt_pw_inds[~kpt_pw_inds.mask]
|
|
1010
|
-
kpt_pw_inds = kpt_pw_inds.reshape((-1,3))
|
|
1011
|
-
kpt_pw_inds = np.ma.getdata(kpt_pw_inds)
|
|
1012
|
-
coeffs = self.dataset.variables['coefficients_of_wavefunctions'][0,kpt]
|
|
1013
|
-
for band in coeffs:
|
|
1014
|
-
band = band[0]
|
|
1015
|
-
band = band[~band.mask]
|
|
1016
|
-
band = band.reshape((-1,2))
|
|
1017
|
-
band = np.ma.getdata(band)
|
|
1018
|
-
kpt_coeffs.append(band[:,0] + 1j*band[:,1])
|
|
1019
|
-
yield wc.WFK(
|
|
1020
|
-
eigenvalues=eigs[kpt,:],
|
|
1021
|
-
wfk_coeffs=np.array(kpt_coeffs),
|
|
1022
|
-
pw_indices=kpt_pw_inds,
|
|
1023
|
-
kpoints=self.kpts[kpt],
|
|
1024
|
-
nkpt=self.nkpt,
|
|
1025
|
-
nbands=self.nband,
|
|
1026
|
-
ngfftx=self.ngfftx,
|
|
1027
|
-
ngffty=self.ngffty,
|
|
1028
|
-
ngfftz=self.ngfftz,
|
|
1029
|
-
symrel=self.symrel,
|
|
1030
|
-
nsym=self.nsym,
|
|
1031
|
-
lattice=self.real_lattice,
|
|
1032
|
-
natom=self.natom,
|
|
1033
|
-
xred=self.xred,
|
|
1034
|
-
typat=self.typat,
|
|
1035
|
-
znucltypat=self.znucltypat,
|
|
1036
|
-
fermi_energy=self.fermi,
|
|
1037
|
-
non_symm_vecs=self.tnons,
|
|
1038
|
-
time_reversal=self.time_reversal
|
|
1039
|
-
)
|
|
1040
|
-
#-----------------------------------------------------------------------------------------------------------------#
|
|
1041
|
-
# fetch eigenvalues from netcdf dataset
|
|
1042
|
-
def ReadEigenvalues(
|
|
1043
|
-
self, energy_level:float=np.nan, width:float=np.nan, check_time_rev:bool=True
|
|
1044
|
-
)->Generator[wc.WFK, None, None]:
|
|
1045
|
-
# check for time reversal symmetry
|
|
1046
|
-
if check_time_rev:
|
|
1047
|
-
self._CheckTimeRev()
|
|
1048
|
-
else:
|
|
1049
|
-
self.time_reversal = False
|
|
1050
|
-
eigs:np.ndarray = self.dataset.variables['eigenvalues'][:][0]
|
|
1051
|
-
for i, kpt in enumerate(self.kpts):
|
|
1052
|
-
print(f'Reading kpoint {i+1} of {self.nkpt}', end='\r')
|
|
1053
|
-
yield wc.WFK(
|
|
1054
|
-
eigenvalues=eigs[i,:],
|
|
1055
|
-
kpoints=kpt,
|
|
1056
|
-
nkpt=self.nkpt,
|
|
1057
|
-
nbands=self.nband,
|
|
1058
|
-
ngfftx=self.ngfftx,
|
|
1059
|
-
ngffty=self.ngffty,
|
|
1060
|
-
ngfftz=self.ngfftz,
|
|
1061
|
-
symrel=self.symrel,
|
|
1062
|
-
nsym=self.nsym,
|
|
1063
|
-
lattice=self.real_lattice,
|
|
1064
|
-
natom=self.natom,
|
|
1065
|
-
xred=self.xred,
|
|
1066
|
-
typat=self.typat,
|
|
1067
|
-
znucltypat=self.znucltypat,
|
|
1068
|
-
fermi_energy=self.fermi,
|
|
1069
|
-
non_symm_vecs=self.tnons,
|
|
1070
|
-
time_reversal=self.time_reversal
|
|
1071
|
-
)
|
|
1072
|
-
|
|
1073
|
-
#---------------------------------------------------------------------------------------------------------------------#
|
|
1074
|
-
#----------------------------------------------------- END CLASS -----------------------------------------------------#
|
|
1075
|
-
#---------------------------------------------------------------------------------------------------------------------#
|
|
1076
|
-
|
|
1077
|
-
def AbinitWFK(
|
|
1078
|
-
filename:str
|
|
1079
|
-
)->Union[Abinit10WFK, Abinit7WFK, AbinitNetCDF]:
|
|
1080
|
-
'''
|
|
1081
|
-
A function for identifying ABINIT version and constructing respective AbinitWFK object.
|
|
1082
|
-
'''
|
|
1083
|
-
if filename.endswith('.nc'):
|
|
1084
|
-
return AbinitNetCDF(filename)
|
|
1085
|
-
wfk = open(filename, 'rb')
|
|
1086
|
-
wfk.read(4)
|
|
1087
|
-
version = wfk.read(6).decode()
|
|
1088
|
-
wfk.close()
|
|
1089
|
-
if version.strip().split('.')[0] == '7':
|
|
1090
|
-
return Abinit7WFK(filename)
|
|
1091
|
-
elif version.strip().split('.')[0] == '10':
|
|
1092
|
-
return Abinit10WFK(filename)
|
|
1093
|
-
else:
|
|
1094
|
-
print(f'Version {version} may not be supported, attempting to analyze with v10 procedure.')
|
|
1
|
+
import struct
|
|
2
|
+
import numpy as np
|
|
3
|
+
from typing import Generator, Union
|
|
4
|
+
import sys
|
|
5
|
+
from . import wfk_class as wc
|
|
6
|
+
np.set_printoptions(threshold=sys.maxsize)
|
|
7
|
+
|
|
8
|
+
def bytes2float(bin_data)->float:
|
|
9
|
+
return struct.unpack('<d', bin_data)[0]
|
|
10
|
+
|
|
11
|
+
def bytes2int(bin_data)->int:
|
|
12
|
+
return int.from_bytes(bin_data, 'little', signed=True)
|
|
13
|
+
|
|
14
|
+
class Abinit7WFK():
|
|
15
|
+
def __init__(
|
|
16
|
+
self, filename:str
|
|
17
|
+
):
|
|
18
|
+
self.filename = filename
|
|
19
|
+
self._ReadHeader()
|
|
20
|
+
#---------------------------------------------------------------------------------------------------------------------#
|
|
21
|
+
#------------------------------------------------------ METHODS ------------------------------------------------------#
|
|
22
|
+
#---------------------------------------------------------------------------------------------------------------------#
|
|
23
|
+
# method to read wavefunction header for abinit version 7
|
|
24
|
+
def _ReadHeader(
|
|
25
|
+
self
|
|
26
|
+
)->None:
|
|
27
|
+
wfk = open(self.filename, 'rb')
|
|
28
|
+
print('Reading WFK header')
|
|
29
|
+
#---------------#
|
|
30
|
+
# unpack integers
|
|
31
|
+
self.header = bytes2int(wfk.read(4))
|
|
32
|
+
self.version = wfk.read(6).decode()
|
|
33
|
+
self.headform = bytes2int(wfk.read(4))
|
|
34
|
+
self.fform = bytes2int(wfk.read(4))
|
|
35
|
+
wfk.read(8)
|
|
36
|
+
self.bandtot = bytes2int(wfk.read(4))
|
|
37
|
+
self.date = bytes2int(wfk.read(4))
|
|
38
|
+
self.intxc = bytes2int(wfk.read(4))
|
|
39
|
+
self.ixc = bytes2int(wfk.read(4))
|
|
40
|
+
self.natom = bytes2int(wfk.read(4))
|
|
41
|
+
self.ngfftx = bytes2int(wfk.read(4))
|
|
42
|
+
self.ngffty = bytes2int(wfk.read(4))
|
|
43
|
+
self.ngfftz = bytes2int(wfk.read(4))
|
|
44
|
+
self.nkpt = bytes2int(wfk.read(4))
|
|
45
|
+
self.nspden = bytes2int(wfk.read(4))
|
|
46
|
+
self.nspinor = bytes2int(wfk.read(4))
|
|
47
|
+
self.nsppol = bytes2int(wfk.read(4))
|
|
48
|
+
self.nsym = bytes2int(wfk.read(4))
|
|
49
|
+
self.npsp = bytes2int(wfk.read(4))
|
|
50
|
+
self.ntypat = bytes2int(wfk.read(4))
|
|
51
|
+
self.occopt = bytes2int(wfk.read(4))
|
|
52
|
+
self.pertcase = bytes2int(wfk.read(4))
|
|
53
|
+
self.usepaw = bytes2int(wfk.read(4))
|
|
54
|
+
if self.usepaw == 1:
|
|
55
|
+
raise NotImplementedError(
|
|
56
|
+
f'''PAW potentials are not supported by this program for Abinit v{self.version}.
|
|
57
|
+
This program supports PAW potentials in Abinit v10, consider upgrading to the latest version.
|
|
58
|
+
'''
|
|
59
|
+
)
|
|
60
|
+
#--------------#
|
|
61
|
+
# unpack doubles
|
|
62
|
+
self.ecut = bytes2float(wfk.read(8))
|
|
63
|
+
self.ecutdg = bytes2float(wfk.read(8))
|
|
64
|
+
self.ecutsm = bytes2float(wfk.read(8))
|
|
65
|
+
self.ecut_eff = bytes2float(wfk.read(8))
|
|
66
|
+
self.qptnx = bytes2float(wfk.read(8))
|
|
67
|
+
self.qptny = bytes2float(wfk.read(8))
|
|
68
|
+
self.qptnz = bytes2float(wfk.read(8))
|
|
69
|
+
rprimd_ax = bytes2float(wfk.read(8))
|
|
70
|
+
rprimd_ay = bytes2float(wfk.read(8))
|
|
71
|
+
rprimd_az = bytes2float(wfk.read(8))
|
|
72
|
+
rprimd_bx = bytes2float(wfk.read(8))
|
|
73
|
+
rprimd_by = bytes2float(wfk.read(8))
|
|
74
|
+
rprimd_bz = bytes2float(wfk.read(8))
|
|
75
|
+
rprimd_cx = bytes2float(wfk.read(8))
|
|
76
|
+
rprimd_cy = bytes2float(wfk.read(8))
|
|
77
|
+
rprimd_cz = bytes2float(wfk.read(8))
|
|
78
|
+
#------------------------#
|
|
79
|
+
# convert Bohr to Angstrom
|
|
80
|
+
self.real_lattice = 0.529177*np.array([[rprimd_ax, rprimd_ay, rprimd_az],
|
|
81
|
+
[rprimd_bx, rprimd_by, rprimd_bz],
|
|
82
|
+
[rprimd_cx, rprimd_cy, rprimd_cz]]).reshape((3,3))
|
|
83
|
+
self.stmbias = bytes2float(wfk.read(8))
|
|
84
|
+
self.tphysel = bytes2float(wfk.read(8))
|
|
85
|
+
self.tsmear = bytes2float(wfk.read(8))
|
|
86
|
+
#---------------#
|
|
87
|
+
# unpack integers
|
|
88
|
+
self.usewvl = bytes2int(wfk.read(4))
|
|
89
|
+
wfk.read(8)
|
|
90
|
+
self.istwfk:list[int] = []
|
|
91
|
+
for i in range(self.nkpt):
|
|
92
|
+
val = bytes2int(wfk.read(4))
|
|
93
|
+
self.istwfk.append(val)
|
|
94
|
+
self.bands:list[int] = []
|
|
95
|
+
for i in range(self.nkpt*self.nsppol):
|
|
96
|
+
val = bytes2int(wfk.read(4))
|
|
97
|
+
self.bands.append(val)
|
|
98
|
+
self.npwarr:list[int] = []
|
|
99
|
+
for i in range(self.nkpt):
|
|
100
|
+
val = bytes2int(wfk.read(4))
|
|
101
|
+
self.npwarr.append(val)
|
|
102
|
+
self.so_psp:list[int] = []
|
|
103
|
+
for i in range(self.npsp):
|
|
104
|
+
val = bytes2int(wfk.read(4))
|
|
105
|
+
self.so_psp.append(val)
|
|
106
|
+
self.symafm:list[int] = []
|
|
107
|
+
for i in range(self.nsym):
|
|
108
|
+
val = bytes2int(wfk.read(4))
|
|
109
|
+
self.symafm.append(val)
|
|
110
|
+
self.symrel:list[np.ndarray] = []
|
|
111
|
+
for i in range(self.nsym):
|
|
112
|
+
arr = np.zeros((3,3))
|
|
113
|
+
arr[0,0] = bytes2int(wfk.read(4))
|
|
114
|
+
arr[1,0] = bytes2int(wfk.read(4))
|
|
115
|
+
arr[2,0] = bytes2int(wfk.read(4))
|
|
116
|
+
arr[0,1] = bytes2int(wfk.read(4))
|
|
117
|
+
arr[1,1] = bytes2int(wfk.read(4))
|
|
118
|
+
arr[2,1] = bytes2int(wfk.read(4))
|
|
119
|
+
arr[0,2] = bytes2int(wfk.read(4))
|
|
120
|
+
arr[1,2] = bytes2int(wfk.read(4))
|
|
121
|
+
arr[2,2] = bytes2int(wfk.read(4))
|
|
122
|
+
self.symrel.append(arr)
|
|
123
|
+
self.typat:list[int] = []
|
|
124
|
+
for i in range(self.natom):
|
|
125
|
+
val = bytes2int(wfk.read(4))
|
|
126
|
+
self.typat.append(val)
|
|
127
|
+
#--------------#
|
|
128
|
+
# unpack doubles
|
|
129
|
+
self.kpts:list[np.ndarray] = []
|
|
130
|
+
for i in range(self.nkpt):
|
|
131
|
+
vec = np.zeros(3)
|
|
132
|
+
vec[0] = bytes2float(wfk.read(8))
|
|
133
|
+
vec[1] = bytes2float(wfk.read(8))
|
|
134
|
+
vec[2] = bytes2float(wfk.read(8))
|
|
135
|
+
self.kpts.append(vec)
|
|
136
|
+
self.occ:list[float] = []
|
|
137
|
+
for i in range(self.bandtot):
|
|
138
|
+
val = bytes2float(wfk.read(8))
|
|
139
|
+
self.occ.append(val)
|
|
140
|
+
self.tnons:list[np.ndarray] = []
|
|
141
|
+
for i in range(self.nsym):
|
|
142
|
+
vec = np.zeros(3)
|
|
143
|
+
vec[0] = bytes2float(wfk.read(8))
|
|
144
|
+
vec[1] = bytes2float(wfk.read(8))
|
|
145
|
+
vec[2] = bytes2float(wfk.read(8))
|
|
146
|
+
self.tnons.append(vec)
|
|
147
|
+
self.znucltypat:list[float] = []
|
|
148
|
+
for i in range(self.ntypat):
|
|
149
|
+
val = bytes2float(wfk.read(8))
|
|
150
|
+
self.znucltypat.append(val)
|
|
151
|
+
self.wtk:list[float] = []
|
|
152
|
+
for i in range(self.nkpt):
|
|
153
|
+
val = bytes2float(wfk.read(8))
|
|
154
|
+
self.wtk.append(val)
|
|
155
|
+
#-----------------------#
|
|
156
|
+
# unpack pseudopotentials
|
|
157
|
+
wfk.read(4)
|
|
158
|
+
self.title:list[str] = []
|
|
159
|
+
self.znuclpsp:list[float] = []
|
|
160
|
+
self.zionpsp:list[float] = []
|
|
161
|
+
self.pspso:list[int] = []
|
|
162
|
+
self.pspdat:list[int] = []
|
|
163
|
+
self.pspcod:list[int] = []
|
|
164
|
+
self.pspxc:list[int] = []
|
|
165
|
+
self.lmn_size:list[int] = []
|
|
166
|
+
for i in range(self.npsp):
|
|
167
|
+
wfk.read(4)
|
|
168
|
+
self.title.append(wfk.read(132).decode())
|
|
169
|
+
self.znuclpsp.append(bytes2float(wfk.read(8)))
|
|
170
|
+
self.zionpsp.append(bytes2float(wfk.read(8)))
|
|
171
|
+
self.pspso.append(bytes2int(wfk.read(4)))
|
|
172
|
+
self.pspdat.append(bytes2int(wfk.read(4)))
|
|
173
|
+
self.pspcod.append(bytes2int(wfk.read(4)))
|
|
174
|
+
self.pspxc.append(bytes2int(wfk.read(4)))
|
|
175
|
+
self.lmn_size.append(bytes2int(wfk.read(4)))
|
|
176
|
+
wfk.read(4)
|
|
177
|
+
#------------------#
|
|
178
|
+
# unpack coordinates
|
|
179
|
+
wfk.read(4)
|
|
180
|
+
self.residm = bytes2float(wfk.read(8))
|
|
181
|
+
self.xred:list[np.ndarray] = []
|
|
182
|
+
for i in range(self.natom):
|
|
183
|
+
vec = np.zeros(3)
|
|
184
|
+
vec[0] = bytes2float(wfk.read(8))
|
|
185
|
+
vec[1] = bytes2float(wfk.read(8))
|
|
186
|
+
vec[2] = bytes2float(wfk.read(8))
|
|
187
|
+
self.xred.append(vec)
|
|
188
|
+
#------------------------------------#
|
|
189
|
+
# unpack total energy and fermi energy
|
|
190
|
+
self.etotal = bytes2float(wfk.read(8))
|
|
191
|
+
self.fermi = bytes2float(wfk.read(8))
|
|
192
|
+
wfk.read(4)
|
|
193
|
+
print('WFK header read')
|
|
194
|
+
wfk.close()
|
|
195
|
+
#-----------------------------------------------------------------------------------------------------------------#
|
|
196
|
+
# check for time reversal symmetry
|
|
197
|
+
def _CheckTimeRev(
|
|
198
|
+
self
|
|
199
|
+
):
|
|
200
|
+
# if system is centrosymmetric, do not double reciprocal symmetry operations
|
|
201
|
+
if True in [np.array_equal(-np.identity(3),mat) for mat in self.symrel]:
|
|
202
|
+
self.time_reversal = False
|
|
203
|
+
else:
|
|
204
|
+
self.time_reversal = True
|
|
205
|
+
print((
|
|
206
|
+
'Noncentrosymmetric system identified, assuming time reversal symmetry\n'
|
|
207
|
+
'To change this, set "check_time_rev" keyword to False when calling ReadWFK() or ReadEigenvalues()'
|
|
208
|
+
))
|
|
209
|
+
#-----------------------------------------------------------------------------------------------------------------#
|
|
210
|
+
# method to read entire body of abinit version 7 wavefunction file
|
|
211
|
+
def ReadWFK(
|
|
212
|
+
self, energy_level=np.nan, width=np.nan, check_time_rev:bool=True
|
|
213
|
+
)->Generator[wc.WFK, None, None]:
|
|
214
|
+
'''
|
|
215
|
+
Method that constructs WFK objects from ABINIT v7 WFK file
|
|
216
|
+
|
|
217
|
+
Parameters
|
|
218
|
+
----------
|
|
219
|
+
energy_level : float
|
|
220
|
+
The energy level to pull plane wave coefficients from
|
|
221
|
+
This is relative to the Fermi energy
|
|
222
|
+
If not defined but width is defined, default is 0 Hartree
|
|
223
|
+
|
|
224
|
+
width : float
|
|
225
|
+
Defines range about energy_level to search for plane wave coefficients
|
|
226
|
+
Total range is equal to width, so look width/2 above and below energy_level
|
|
227
|
+
If not defined but energy_level is defined, default is 0.005 Hartree
|
|
228
|
+
'''
|
|
229
|
+
# check for time reversal symmetry
|
|
230
|
+
if check_time_rev:
|
|
231
|
+
self._CheckTimeRev()
|
|
232
|
+
else:
|
|
233
|
+
self.time_reversal = False
|
|
234
|
+
# read wfk file
|
|
235
|
+
wfk = open(self.filename, 'rb')
|
|
236
|
+
skip = True
|
|
237
|
+
if energy_level is np.nan:
|
|
238
|
+
if width is np.nan:
|
|
239
|
+
skip = False
|
|
240
|
+
else:
|
|
241
|
+
energy_level = 0
|
|
242
|
+
else:
|
|
243
|
+
if width is np.nan:
|
|
244
|
+
width = 0.005
|
|
245
|
+
#-----------#
|
|
246
|
+
# skip header
|
|
247
|
+
wfk.read(298)
|
|
248
|
+
wfk.read(4*(2*self.nkpt + self.nkpt*self.nsppol + self.npsp + 10*self.nsym + self.natom))
|
|
249
|
+
wfk.read(8*(4*self.nkpt + self.bandtot + 3*self.nsym + self.ntypat + 3*self.natom))
|
|
250
|
+
wfk.read(self.npsp*(176))
|
|
251
|
+
#-------------------------------#
|
|
252
|
+
# begin reading wavefunction body
|
|
253
|
+
for i in range(self.nsppol):
|
|
254
|
+
for j in range(self.nkpt):
|
|
255
|
+
print(f'Reading kpoint {j+1} of {self.nkpt}', end='\r')
|
|
256
|
+
if j+1 == self.nkpt:
|
|
257
|
+
print('\n', end='')
|
|
258
|
+
pw_indices:list[tuple] = []
|
|
259
|
+
eigenvalues:list[float] = []
|
|
260
|
+
wfk.read(4)
|
|
261
|
+
npw = bytes2int(wfk.read(4))
|
|
262
|
+
self.nspinor = bytes2int(wfk.read(4))
|
|
263
|
+
nband_temp = bytes2int(wfk.read(4))
|
|
264
|
+
wfk.read(8)
|
|
265
|
+
for pw in range(npw):
|
|
266
|
+
kx = bytes2int(wfk.read(4))
|
|
267
|
+
ky = bytes2int(wfk.read(4))
|
|
268
|
+
kz = bytes2int(wfk.read(4))
|
|
269
|
+
pw_indices.append((kx, ky, kz))
|
|
270
|
+
wfk.read(8)
|
|
271
|
+
for nband in range(nband_temp):
|
|
272
|
+
eigenval = bytes2float(wfk.read(8))
|
|
273
|
+
eigenvalues.append(eigenval)
|
|
274
|
+
# skip reading coefficients if they energy level of interest is not in band
|
|
275
|
+
if skip:
|
|
276
|
+
min_val = self.fermi + energy_level - width/2
|
|
277
|
+
max_val = self.fermi + energy_level + width/2
|
|
278
|
+
eig_found = False
|
|
279
|
+
for eigval in eigenvalues:
|
|
280
|
+
if min_val <= eigval <= max_val:
|
|
281
|
+
eig_found = True
|
|
282
|
+
break
|
|
283
|
+
if eig_found:
|
|
284
|
+
yield self._ReadCoeffs(
|
|
285
|
+
wfk,
|
|
286
|
+
nband_temp,
|
|
287
|
+
npw,
|
|
288
|
+
eigenvalues,
|
|
289
|
+
pw_indices,
|
|
290
|
+
ind=j
|
|
291
|
+
)
|
|
292
|
+
else:
|
|
293
|
+
wfk.read(nband_temp*8)
|
|
294
|
+
wfk.read(4)
|
|
295
|
+
wfk.read(nband_temp*(8 + npw*16))
|
|
296
|
+
else:
|
|
297
|
+
yield self._ReadCoeffs(
|
|
298
|
+
wfk,
|
|
299
|
+
nband_temp,
|
|
300
|
+
npw,
|
|
301
|
+
eigenvalues,
|
|
302
|
+
pw_indices,
|
|
303
|
+
ind=j
|
|
304
|
+
)
|
|
305
|
+
print('WFK body read')
|
|
306
|
+
wfk.close()
|
|
307
|
+
#-----------------------------------------------------------------------------------------------------------------#
|
|
308
|
+
# method to read in plane wave coefficients
|
|
309
|
+
def _ReadCoeffs(
|
|
310
|
+
self, wfk_file, nbands, npws, eigs, pw_inds, ind
|
|
311
|
+
)->wc.WFK:
|
|
312
|
+
occupancies = np.zeros((1,nbands), dtype=float)
|
|
313
|
+
coeffs = np.zeros((nbands,npws), dtype=complex)
|
|
314
|
+
for nband in range(nbands):
|
|
315
|
+
occ = bytes2float(wfk_file.read(8))
|
|
316
|
+
occupancies[:,nband] = occ
|
|
317
|
+
wfk_file.read(4)
|
|
318
|
+
for nband in range(nbands):
|
|
319
|
+
wfk_file.read(4)
|
|
320
|
+
cg:np.ndarray = np.zeros((1,npws), dtype=complex)
|
|
321
|
+
for pw in range(npws):
|
|
322
|
+
cg1 = bytes2float(wfk_file.read(8))
|
|
323
|
+
cg2 = bytes2float(wfk_file.read(8))
|
|
324
|
+
cg[:,pw] = cg1 + 1j*cg2
|
|
325
|
+
coeffs[nband,:] = cg
|
|
326
|
+
wfk_file.read(4)
|
|
327
|
+
return wc.WFK(
|
|
328
|
+
eigenvalues=np.array(eigs),
|
|
329
|
+
wfk_coeffs=np.array(coeffs),
|
|
330
|
+
pw_indices=np.array(pw_inds),
|
|
331
|
+
kpoints=np.array(self.kpts[ind]),
|
|
332
|
+
nkpt=self.nkpt,
|
|
333
|
+
nbands=self.bands[0],
|
|
334
|
+
ngfftx=self.ngfftx,
|
|
335
|
+
ngffty=self.ngffty,
|
|
336
|
+
ngfftz=self.ngfftz,
|
|
337
|
+
symrel=np.array(self.symrel),
|
|
338
|
+
nsym=self.nsym,
|
|
339
|
+
lattice=self.real_lattice,
|
|
340
|
+
natom=self.natom,
|
|
341
|
+
xred=np.array(self.xred),
|
|
342
|
+
typat=self.typat,
|
|
343
|
+
znucltypat=self.znucltypat,
|
|
344
|
+
fermi_energy=self.fermi,
|
|
345
|
+
non_symm_vecs=np.array(self.tnons),
|
|
346
|
+
time_reversal=self.time_reversal
|
|
347
|
+
)
|
|
348
|
+
#-----------------------------------------------------------------------------------------------------------------#
|
|
349
|
+
# method to read only eigenvalues from body of abinit version 7 wavefunction file
|
|
350
|
+
def ReadEigenvalues(
|
|
351
|
+
self, check_time_rev:bool=True
|
|
352
|
+
)->Generator[wc.WFK, None, None]:
|
|
353
|
+
'''
|
|
354
|
+
Method that constructs WFK objects from ABINIT v7 WFK file.
|
|
355
|
+
'''
|
|
356
|
+
# check for time reversal symmetry
|
|
357
|
+
if check_time_rev:
|
|
358
|
+
self._CheckTimeRev()
|
|
359
|
+
else:
|
|
360
|
+
self.time_reversal = False
|
|
361
|
+
# read wfk
|
|
362
|
+
wfk = open(self.filename, 'rb')
|
|
363
|
+
#-----------#
|
|
364
|
+
# skip header
|
|
365
|
+
wfk.read(298)
|
|
366
|
+
wfk.read(4*(2*self.nkpt + self.nkpt*self.nsppol + self.npsp + 10*self.nsym + self.natom))
|
|
367
|
+
wfk.read(8*(4*self.nkpt + self.bandtot + 3*self.nsym + self.ntypat + 3*self.natom))
|
|
368
|
+
wfk.read(self.npsp*(176))
|
|
369
|
+
#-------------------------------#
|
|
370
|
+
# begin reading wavefunction body
|
|
371
|
+
for i in range(self.nsppol):
|
|
372
|
+
for j in range(self.nkpt):
|
|
373
|
+
print(f'Reading kpoint {j+1} of {self.nkpt}', end='\r')
|
|
374
|
+
if j+1 == self.nkpt:
|
|
375
|
+
print('\n', end='')
|
|
376
|
+
eigenvalues:list[float] = []
|
|
377
|
+
wfk.read(4)
|
|
378
|
+
npw = bytes2int(wfk.read(4))
|
|
379
|
+
self.nspinor = bytes2int(wfk.read(4))
|
|
380
|
+
nband_temp = bytes2int(wfk.read(4))
|
|
381
|
+
wfk.read(8)
|
|
382
|
+
wfk.read(12*npw)
|
|
383
|
+
wfk.read(8)
|
|
384
|
+
#------------------------------------------------------------------#
|
|
385
|
+
# only need eigenvalues for Fermi surface, skip over everything else
|
|
386
|
+
for nband in range(nband_temp):
|
|
387
|
+
eigenval = bytes2float(wfk.read(8))
|
|
388
|
+
eigenvalues.append(eigenval)
|
|
389
|
+
wfk.read(nband_temp*8)
|
|
390
|
+
wfk.read(4)
|
|
391
|
+
wfk.read(nband_temp*(8 + npw*16))
|
|
392
|
+
yield wc.WFK(
|
|
393
|
+
eigenvalues=np.array(eigenvalues),
|
|
394
|
+
kpoints=np.array(self.kpts[j]),
|
|
395
|
+
nkpt=self.nkpt,
|
|
396
|
+
nbands=self.bands[0],
|
|
397
|
+
ngfftx=self.ngfftx,
|
|
398
|
+
ngffty=self.ngffty,
|
|
399
|
+
ngfftz=self.ngfftz,
|
|
400
|
+
symrel=np.array(self.symrel),
|
|
401
|
+
nsym=self.nsym,
|
|
402
|
+
lattice=self.real_lattice,
|
|
403
|
+
natom=self.natom,
|
|
404
|
+
xred=np.array(self.xred),
|
|
405
|
+
typat=self.typat,
|
|
406
|
+
znucltypat=self.znucltypat,
|
|
407
|
+
fermi_energy=self.fermi,
|
|
408
|
+
non_symm_vecs=np.array(self.tnons),
|
|
409
|
+
time_reversal=self.time_reversal
|
|
410
|
+
)
|
|
411
|
+
print('WFK body read')
|
|
412
|
+
wfk.close()
|
|
413
|
+
|
|
414
|
+
#---------------------------------------------------------------------------------------------------------------------#
|
|
415
|
+
#----------------------------------------------------- END CLASS -----------------------------------------------------#
|
|
416
|
+
#---------------------------------------------------------------------------------------------------------------------#
|
|
417
|
+
|
|
418
|
+
class Abinit10WFK():
|
|
419
|
+
def __init__(
|
|
420
|
+
self, filename:str
|
|
421
|
+
):
|
|
422
|
+
self.filename = filename
|
|
423
|
+
self._ReadHeader()
|
|
424
|
+
#---------------------------------------------------------------------------------------------------------------------#
|
|
425
|
+
#------------------------------------------------------ METHODS ------------------------------------------------------#
|
|
426
|
+
#---------------------------------------------------------------------------------------------------------------------#
|
|
427
|
+
# method to read wavefunction header for abinit version 7
|
|
428
|
+
def _ReadHeader(
|
|
429
|
+
self
|
|
430
|
+
)->None:
|
|
431
|
+
wfk = open(self.filename, 'rb')
|
|
432
|
+
print('Reading WFK header')
|
|
433
|
+
#---------------#
|
|
434
|
+
# unpack integers
|
|
435
|
+
self.header = bytes2int(wfk.read(4))
|
|
436
|
+
self.version = wfk.read(6).decode()
|
|
437
|
+
wfk.read(2)
|
|
438
|
+
self.headform = bytes2int(wfk.read(4))
|
|
439
|
+
self.fform = bytes2int(wfk.read(4))
|
|
440
|
+
wfk.read(8)
|
|
441
|
+
self.bandtot = bytes2int(wfk.read(4))
|
|
442
|
+
self.date = bytes2int(wfk.read(4))
|
|
443
|
+
self.intxc = bytes2int(wfk.read(4))
|
|
444
|
+
self.ixc = bytes2int(wfk.read(4))
|
|
445
|
+
self.natom = bytes2int(wfk.read(4))
|
|
446
|
+
self.ngfftx = bytes2int(wfk.read(4))
|
|
447
|
+
self.ngffty = bytes2int(wfk.read(4))
|
|
448
|
+
self.ngfftz = bytes2int(wfk.read(4))
|
|
449
|
+
self.nkpt = bytes2int(wfk.read(4))
|
|
450
|
+
self.nspden = bytes2int(wfk.read(4))
|
|
451
|
+
self.nspinor = bytes2int(wfk.read(4))
|
|
452
|
+
self.nsppol = bytes2int(wfk.read(4))
|
|
453
|
+
self.nsym = bytes2int(wfk.read(4))
|
|
454
|
+
self.npsp = bytes2int(wfk.read(4))
|
|
455
|
+
self.ntypat = bytes2int(wfk.read(4))
|
|
456
|
+
self.occopt = bytes2int(wfk.read(4))
|
|
457
|
+
self.pertcase = bytes2int(wfk.read(4))
|
|
458
|
+
self.usepaw = bytes2int(wfk.read(4))
|
|
459
|
+
#--------------#
|
|
460
|
+
# unpack doubles
|
|
461
|
+
self.ecut = bytes2float(wfk.read(8))
|
|
462
|
+
self.ecutdg = bytes2float(wfk.read(8))
|
|
463
|
+
self.ecutsm = bytes2float(wfk.read(8))
|
|
464
|
+
self.ecut_eff = bytes2float(wfk.read(8))
|
|
465
|
+
self.qptnx = bytes2float(wfk.read(8))
|
|
466
|
+
self.qptny = bytes2float(wfk.read(8))
|
|
467
|
+
self.qptnz = bytes2float(wfk.read(8))
|
|
468
|
+
rprimd_ax = bytes2float(wfk.read(8))
|
|
469
|
+
rprimd_ay = bytes2float(wfk.read(8))
|
|
470
|
+
rprimd_az = bytes2float(wfk.read(8))
|
|
471
|
+
rprimd_bx = bytes2float(wfk.read(8))
|
|
472
|
+
rprimd_by = bytes2float(wfk.read(8))
|
|
473
|
+
rprimd_bz = bytes2float(wfk.read(8))
|
|
474
|
+
rprimd_cx = bytes2float(wfk.read(8))
|
|
475
|
+
rprimd_cy = bytes2float(wfk.read(8))
|
|
476
|
+
rprimd_cz = bytes2float(wfk.read(8))
|
|
477
|
+
#------------------------#
|
|
478
|
+
# convert Bohr to Angstrom
|
|
479
|
+
self.real_lattice = 0.529177*np.array([
|
|
480
|
+
[rprimd_ax, rprimd_ay, rprimd_az],
|
|
481
|
+
[rprimd_bx, rprimd_by, rprimd_bz],
|
|
482
|
+
[rprimd_cx, rprimd_cy, rprimd_cz]
|
|
483
|
+
]).reshape((3,3))
|
|
484
|
+
self.stmbias = bytes2float(wfk.read(8))
|
|
485
|
+
self.tphysel = bytes2float(wfk.read(8))
|
|
486
|
+
self.tsmear = bytes2float(wfk.read(8))
|
|
487
|
+
#---------------#
|
|
488
|
+
# unpack integers
|
|
489
|
+
self.usewvl = bytes2int(wfk.read(4))
|
|
490
|
+
self.nshiftk_orig = bytes2int(wfk.read(4))
|
|
491
|
+
self.nshiftk = bytes2int(wfk.read(4))
|
|
492
|
+
self.mband = bytes2int(wfk.read(4))
|
|
493
|
+
wfk.read(8)
|
|
494
|
+
self.istwfk:list[int] = []
|
|
495
|
+
for _ in range(self.nkpt):
|
|
496
|
+
val = bytes2int(wfk.read(4))
|
|
497
|
+
self.istwfk.append(val)
|
|
498
|
+
self.bands:list[int] = []
|
|
499
|
+
for _ in range(self.nkpt*self.nsppol):
|
|
500
|
+
val = bytes2int(wfk.read(4))
|
|
501
|
+
self.bands.append(val)
|
|
502
|
+
self.npwarr:list[int] = []
|
|
503
|
+
for _ in range(self.nkpt):
|
|
504
|
+
val = bytes2int(wfk.read(4))
|
|
505
|
+
self.npwarr.append(val)
|
|
506
|
+
self.so_psp:list[int] = []
|
|
507
|
+
for _ in range(self.npsp):
|
|
508
|
+
val = bytes2int(wfk.read(4))
|
|
509
|
+
self.so_psp.append(val)
|
|
510
|
+
self.symafm:list[int] = []
|
|
511
|
+
for _ in range(self.nsym):
|
|
512
|
+
val = bytes2int(wfk.read(4))
|
|
513
|
+
self.symafm.append(val)
|
|
514
|
+
self.symrel:list[np.ndarray] = []
|
|
515
|
+
for _ in range(self.nsym):
|
|
516
|
+
arr = np.zeros((3,3))
|
|
517
|
+
arr[0,0] = bytes2int(wfk.read(4))
|
|
518
|
+
arr[1,0] = bytes2int(wfk.read(4))
|
|
519
|
+
arr[2,0] = bytes2int(wfk.read(4))
|
|
520
|
+
arr[0,1] = bytes2int(wfk.read(4))
|
|
521
|
+
arr[1,1] = bytes2int(wfk.read(4))
|
|
522
|
+
arr[2,1] = bytes2int(wfk.read(4))
|
|
523
|
+
arr[0,2] = bytes2int(wfk.read(4))
|
|
524
|
+
arr[1,2] = bytes2int(wfk.read(4))
|
|
525
|
+
arr[2,2] = bytes2int(wfk.read(4))
|
|
526
|
+
self.symrel.append(arr)
|
|
527
|
+
self.typat:list[int] = []
|
|
528
|
+
for _ in range(self.natom):
|
|
529
|
+
val = bytes2int(wfk.read(4))
|
|
530
|
+
self.typat.append(val)
|
|
531
|
+
#--------------#
|
|
532
|
+
# unpack doubles
|
|
533
|
+
self.kpts:list[np.ndarray] = []
|
|
534
|
+
for _ in range(self.nkpt):
|
|
535
|
+
vec = np.zeros(3)
|
|
536
|
+
vec[0] = bytes2float(wfk.read(8))
|
|
537
|
+
vec[1] = bytes2float(wfk.read(8))
|
|
538
|
+
vec[2] = bytes2float(wfk.read(8))
|
|
539
|
+
self.kpts.append(vec)
|
|
540
|
+
self.occ:list[float] = []
|
|
541
|
+
for _ in range(self.bandtot):
|
|
542
|
+
val = bytes2float(wfk.read(8))
|
|
543
|
+
self.occ.append(val)
|
|
544
|
+
self.tnons:list[np.ndarray] = []
|
|
545
|
+
for _ in range(self.nsym):
|
|
546
|
+
vec = np.zeros(3)
|
|
547
|
+
vec[0] = bytes2float(wfk.read(8))
|
|
548
|
+
vec[1] = bytes2float(wfk.read(8))
|
|
549
|
+
vec[2] = bytes2float(wfk.read(8))
|
|
550
|
+
self.tnons.append(vec)
|
|
551
|
+
self.znucltypat:list[float] = []
|
|
552
|
+
for _ in range(self.ntypat):
|
|
553
|
+
val = bytes2float(wfk.read(8))
|
|
554
|
+
self.znucltypat.append(val)
|
|
555
|
+
self.wtk:list[float] = []
|
|
556
|
+
for _ in range(self.nkpt):
|
|
557
|
+
val = bytes2float(wfk.read(8))
|
|
558
|
+
self.wtk.append(val)
|
|
559
|
+
#------------------#
|
|
560
|
+
# unpack coordinates
|
|
561
|
+
wfk.read(8)
|
|
562
|
+
self.residm = bytes2float(wfk.read(8))
|
|
563
|
+
self.xred:list[np.ndarray] = []
|
|
564
|
+
for _ in range(self.natom):
|
|
565
|
+
vec = np.zeros(3)
|
|
566
|
+
vec[0] = bytes2float(wfk.read(8))
|
|
567
|
+
vec[1] = bytes2float(wfk.read(8))
|
|
568
|
+
vec[2] = bytes2float(wfk.read(8))
|
|
569
|
+
self.xred.append(vec)
|
|
570
|
+
#------------------------------------#
|
|
571
|
+
# unpack total energy and fermi energy
|
|
572
|
+
self.etotal = bytes2float(wfk.read(8))
|
|
573
|
+
self.fermi = bytes2float(wfk.read(8))
|
|
574
|
+
#-------------#
|
|
575
|
+
# unpack floats
|
|
576
|
+
self.amu:list[float] = []
|
|
577
|
+
for _ in range(self.ntypat):
|
|
578
|
+
val = bytes2float(wfk.read(8))
|
|
579
|
+
self.amu.append(val)
|
|
580
|
+
#----------------------------#
|
|
581
|
+
# unpack reciprocal space info
|
|
582
|
+
wfk.read(8)
|
|
583
|
+
self.kptopt = bytes2int(wfk.read(4))
|
|
584
|
+
self.pawcpxocc = bytes2int(wfk.read(4))
|
|
585
|
+
self.nelect = bytes2float(wfk.read(8))
|
|
586
|
+
self.cellcharge = bytes2float(wfk.read(8))
|
|
587
|
+
self.icoulomb = bytes2int(wfk.read(4))
|
|
588
|
+
rec_latt_ax = bytes2int(wfk.read(4))
|
|
589
|
+
rec_latt_ay = bytes2int(wfk.read(4))
|
|
590
|
+
rec_latt_az = bytes2int(wfk.read(4))
|
|
591
|
+
rec_latt_bx = bytes2int(wfk.read(4))
|
|
592
|
+
rec_latt_by = bytes2int(wfk.read(4))
|
|
593
|
+
rec_latt_bz = bytes2int(wfk.read(4))
|
|
594
|
+
rec_latt_cx = bytes2int(wfk.read(4))
|
|
595
|
+
rec_latt_cy = bytes2int(wfk.read(4))
|
|
596
|
+
rec_latt_cz = bytes2int(wfk.read(4))
|
|
597
|
+
self.kptr_latt = np.array([
|
|
598
|
+
[rec_latt_ax, rec_latt_ay, rec_latt_az],
|
|
599
|
+
[rec_latt_bx, rec_latt_by, rec_latt_bz],
|
|
600
|
+
[rec_latt_cx, rec_latt_cy, rec_latt_cz]
|
|
601
|
+
]).reshape((3,3))
|
|
602
|
+
rec_latt_ax = bytes2int(wfk.read(4))
|
|
603
|
+
rec_latt_ay = bytes2int(wfk.read(4))
|
|
604
|
+
rec_latt_az = bytes2int(wfk.read(4))
|
|
605
|
+
rec_latt_bx = bytes2int(wfk.read(4))
|
|
606
|
+
rec_latt_by = bytes2int(wfk.read(4))
|
|
607
|
+
rec_latt_bz = bytes2int(wfk.read(4))
|
|
608
|
+
rec_latt_cx = bytes2int(wfk.read(4))
|
|
609
|
+
rec_latt_cy = bytes2int(wfk.read(4))
|
|
610
|
+
rec_latt_cz = bytes2int(wfk.read(4))
|
|
611
|
+
self.kptr_latt_orig = np.array([
|
|
612
|
+
[rec_latt_ax, rec_latt_ay, rec_latt_az],
|
|
613
|
+
[rec_latt_bx, rec_latt_by, rec_latt_bz],
|
|
614
|
+
[rec_latt_cx, rec_latt_cy, rec_latt_cz]
|
|
615
|
+
]).reshape((3,3))
|
|
616
|
+
shift_orig_x = bytes2float(wfk.read(8))
|
|
617
|
+
shift_orig_y = bytes2float(wfk.read(8))
|
|
618
|
+
shift_orig_z = bytes2float(wfk.read(8))
|
|
619
|
+
self.origin_shift = [shift_orig_x, shift_orig_y, shift_orig_z]
|
|
620
|
+
shift_kx = bytes2float(wfk.read(8))
|
|
621
|
+
shift_ky = bytes2float(wfk.read(8))
|
|
622
|
+
shift_kz = bytes2float(wfk.read(8))
|
|
623
|
+
self.kpt_shift = [shift_kx, shift_ky, shift_kz]
|
|
624
|
+
#-----------------------#
|
|
625
|
+
# unpack pseudopotentials
|
|
626
|
+
wfk.read(4)
|
|
627
|
+
self.title:list[str] = []
|
|
628
|
+
self.znuclpsp:list[float] = []
|
|
629
|
+
self.zionpsp:list[float] = []
|
|
630
|
+
self.pspso:list[int] = []
|
|
631
|
+
self.pspdat:list[int] = []
|
|
632
|
+
self.pspcod:list[int] = []
|
|
633
|
+
self.pspxc:list[int] = []
|
|
634
|
+
self.lmn_size:list[int] = []
|
|
635
|
+
self.md5_pseudos:list[str] = []
|
|
636
|
+
for _ in range(self.npsp):
|
|
637
|
+
wfk.read(4)
|
|
638
|
+
self.title.append(wfk.read(132).decode())
|
|
639
|
+
self.znuclpsp.append(bytes2float(wfk.read(8)))
|
|
640
|
+
self.zionpsp.append(bytes2float(wfk.read(8)))
|
|
641
|
+
self.pspso.append(bytes2int(wfk.read(4)))
|
|
642
|
+
self.pspdat.append(bytes2int(wfk.read(4)))
|
|
643
|
+
self.pspcod.append(bytes2int(wfk.read(4)))
|
|
644
|
+
self.pspxc.append(bytes2int(wfk.read(4)))
|
|
645
|
+
self.lmn_size.append(bytes2int(wfk.read(4)))
|
|
646
|
+
self.md5_pseudos.append(wfk.read(32).decode())
|
|
647
|
+
wfk.read(4)
|
|
648
|
+
#---------------#
|
|
649
|
+
# unpack PAW data
|
|
650
|
+
if self.usepaw == 1:
|
|
651
|
+
wfk.read(4)
|
|
652
|
+
self.nrho:list[int] = []
|
|
653
|
+
for _ in range(self.natom):
|
|
654
|
+
for _ in range(self.nspden):
|
|
655
|
+
nrhoij = bytes2int(wfk.read(4))
|
|
656
|
+
self.nrho.append(nrhoij)
|
|
657
|
+
self.cplex = bytes2int(wfk.read(4))
|
|
658
|
+
self.nspden_paw = bytes2int(wfk.read(4))
|
|
659
|
+
wfk.read(8)
|
|
660
|
+
self.irho:list[int] = []
|
|
661
|
+
for i in range(self.natom):
|
|
662
|
+
for _ in range(self.nspden_paw):
|
|
663
|
+
nrhoij = self.nrho[i]
|
|
664
|
+
for _ in range(nrhoij):
|
|
665
|
+
rhoij = bytes2int(wfk.read(4))
|
|
666
|
+
self.irho.append(rhoij)
|
|
667
|
+
wfk.read(4)
|
|
668
|
+
self.rho:list[float] = []
|
|
669
|
+
for i in range(self.natom):
|
|
670
|
+
for _ in range(self.nspden_paw):
|
|
671
|
+
nrhoij = self.nrho[i]
|
|
672
|
+
for _ in range(nrhoij):
|
|
673
|
+
rhoij = bytes2float(wfk.read(8))
|
|
674
|
+
self.rho.append(rhoij)
|
|
675
|
+
wfk.read(4)
|
|
676
|
+
print('WFK header read')
|
|
677
|
+
wfk.close()
|
|
678
|
+
#-----------------------------------------------------------------------------------------------------------------#
|
|
679
|
+
# check for time reversal symmetry
|
|
680
|
+
def _CheckTimeRev(
|
|
681
|
+
self
|
|
682
|
+
):
|
|
683
|
+
# if system is centrosymmetric, do not double reciprocal symmetry operations
|
|
684
|
+
if True in [np.array_equal(-np.identity(3),mat) for mat in self.symrel]:
|
|
685
|
+
self.time_reversal = False
|
|
686
|
+
else:
|
|
687
|
+
self.time_reversal = True
|
|
688
|
+
print((
|
|
689
|
+
'Noncentrosymmetric system identified, assuming time reversal symmetry\n'
|
|
690
|
+
'To change this, set "check_time_rev" keyword to False when calling ReadWFK() or ReadEigenvalues()'
|
|
691
|
+
))
|
|
692
|
+
#-----------------------------------------------------------------------------------------------------------------#
|
|
693
|
+
# method to read entire body of abinit version 7 wavefunction file
|
|
694
|
+
def ReadWFK(
|
|
695
|
+
self, energy_level=np.nan, width=np.nan, check_time_rev:bool=True
|
|
696
|
+
)->Generator[wc.WFK, None, None]:
|
|
697
|
+
'''
|
|
698
|
+
Method that constructs WFK objects from ABINIT v10 WFK file.
|
|
699
|
+
|
|
700
|
+
Parameters
|
|
701
|
+
----------
|
|
702
|
+
energy_level : float
|
|
703
|
+
The energy level to pull plane wave coefficients from
|
|
704
|
+
This is relative to the Fermi energy
|
|
705
|
+
If not defined but width is defined, default is 0 Hartree
|
|
706
|
+
|
|
707
|
+
width : float
|
|
708
|
+
Defines range about energy_level to search for plane wave coefficients
|
|
709
|
+
Total range is equal to width, so look width/2 above and below energy_level
|
|
710
|
+
If not defined but energy_level is defined, default is 0.005 Hartree
|
|
711
|
+
'''
|
|
712
|
+
# check for time reversal symmetry
|
|
713
|
+
if check_time_rev:
|
|
714
|
+
self._CheckTimeRev()
|
|
715
|
+
else:
|
|
716
|
+
self.time_reversal = False
|
|
717
|
+
# read wfk
|
|
718
|
+
wfk = open(self.filename, 'rb')
|
|
719
|
+
skip = True
|
|
720
|
+
if energy_level is np.nan:
|
|
721
|
+
if width is np.nan:
|
|
722
|
+
skip = False
|
|
723
|
+
else:
|
|
724
|
+
energy_level = 0
|
|
725
|
+
else:
|
|
726
|
+
if width is np.nan:
|
|
727
|
+
width = 0.005
|
|
728
|
+
#-----------#
|
|
729
|
+
# skip header
|
|
730
|
+
wfk.read(468)
|
|
731
|
+
wfk.read(4*(2*self.nkpt + self.nkpt*self.nsppol + self.npsp + 10*self.nsym + self.natom))
|
|
732
|
+
wfk.read(8*(4*self.nkpt + self.bandtot + 3*self.nsym + 2*self.ntypat + 3*self.natom))
|
|
733
|
+
wfk.read(self.npsp*(208))
|
|
734
|
+
if self.usepaw == 1:
|
|
735
|
+
wfk.read(4)
|
|
736
|
+
for _ in range(self.natom):
|
|
737
|
+
for _ in range(self.nspden):
|
|
738
|
+
_ = wfk.read(4)
|
|
739
|
+
wfk.read(4)
|
|
740
|
+
wfk.read(4)
|
|
741
|
+
wfk.read(8)
|
|
742
|
+
for i in range(self.natom):
|
|
743
|
+
for _ in range(self.nspden_paw):
|
|
744
|
+
nrhoij = self.nrho[i]
|
|
745
|
+
for _ in range(nrhoij):
|
|
746
|
+
_ = wfk.read(4)
|
|
747
|
+
wfk.read(4)
|
|
748
|
+
for i in range(self.natom):
|
|
749
|
+
for _ in range(self.nspden_paw):
|
|
750
|
+
nrhoij = self.nrho[i]
|
|
751
|
+
for _ in range(nrhoij):
|
|
752
|
+
_ = wfk.read(8)
|
|
753
|
+
wfk.read(4)
|
|
754
|
+
#-------------------------------#
|
|
755
|
+
# begin reading wavefunction body
|
|
756
|
+
for i in range(self.nsppol):
|
|
757
|
+
for j in range(self.nkpt):
|
|
758
|
+
print(f'Reading kpoint {j+1} of {self.nkpt}', end='\r')
|
|
759
|
+
if j+1 == self.nkpt:
|
|
760
|
+
print('\n', end='')
|
|
761
|
+
wfk.read(4)
|
|
762
|
+
npw = bytes2int(wfk.read(4))
|
|
763
|
+
self.nspinor = bytes2int(wfk.read(4))
|
|
764
|
+
nband_temp = bytes2int(wfk.read(4))
|
|
765
|
+
pw_indices = np.zeros((npw,3), dtype=int)
|
|
766
|
+
eigenvalues = np.zeros(nband_temp, dtype=float)
|
|
767
|
+
wfk.read(8)
|
|
768
|
+
for pw in range(npw):
|
|
769
|
+
kx = bytes2int(wfk.read(4))
|
|
770
|
+
ky = bytes2int(wfk.read(4))
|
|
771
|
+
kz = bytes2int(wfk.read(4))
|
|
772
|
+
pw_indices[pw,:] = [kx, ky, kz]
|
|
773
|
+
wfk.read(8)
|
|
774
|
+
for nband in range(nband_temp):
|
|
775
|
+
eigenval = bytes2float(wfk.read(8))
|
|
776
|
+
eigenvalues[nband] = eigenval
|
|
777
|
+
# skip reading coefficients if they energy level of interest is not in band
|
|
778
|
+
if skip:
|
|
779
|
+
min_val = self.fermi + energy_level - width/2
|
|
780
|
+
max_val = self.fermi + energy_level + width/2
|
|
781
|
+
eig_found = False
|
|
782
|
+
for eigval in eigenvalues:
|
|
783
|
+
if min_val <= eigval <= max_val:
|
|
784
|
+
eig_found = True
|
|
785
|
+
break
|
|
786
|
+
if eig_found:
|
|
787
|
+
yield self._ReadCoeffs(
|
|
788
|
+
wfk,
|
|
789
|
+
nband_temp,
|
|
790
|
+
npw,
|
|
791
|
+
eigenvalues,
|
|
792
|
+
pw_indices,
|
|
793
|
+
ind=j
|
|
794
|
+
)
|
|
795
|
+
else:
|
|
796
|
+
wfk.read(nband_temp*8)
|
|
797
|
+
wfk.read(4)
|
|
798
|
+
wfk.read(nband_temp*(8 + npw*16))
|
|
799
|
+
# if not skip, read full wavefunction
|
|
800
|
+
else:
|
|
801
|
+
yield self._ReadCoeffs(
|
|
802
|
+
wfk,
|
|
803
|
+
nband_temp,
|
|
804
|
+
npw,
|
|
805
|
+
eigenvalues,
|
|
806
|
+
pw_indices,
|
|
807
|
+
ind=j
|
|
808
|
+
)
|
|
809
|
+
print('WFK body read')
|
|
810
|
+
wfk.close()
|
|
811
|
+
#-----------------------------------------------------------------------------------------------------------------#
|
|
812
|
+
# method to read in plane wave coefficients
|
|
813
|
+
def _ReadCoeffs(
|
|
814
|
+
self, wfk_file, nbands, npws, eigs, pw_inds, ind
|
|
815
|
+
)->wc.WFK:
|
|
816
|
+
occupancies = np.zeros((1,nbands), dtype=float)
|
|
817
|
+
coeffs = np.zeros((nbands,npws), dtype=complex)
|
|
818
|
+
for nband in range(nbands):
|
|
819
|
+
occ = bytes2float(wfk_file.read(8))
|
|
820
|
+
occupancies[:,nband] = occ
|
|
821
|
+
wfk_file.read(4)
|
|
822
|
+
for nband in range(nbands):
|
|
823
|
+
wfk_file.read(4)
|
|
824
|
+
cg:np.ndarray = np.zeros((1,npws), dtype=complex)
|
|
825
|
+
for pw in range(npws):
|
|
826
|
+
cg1 = bytes2float(wfk_file.read(8))
|
|
827
|
+
cg2 = bytes2float(wfk_file.read(8))
|
|
828
|
+
cg[:,pw] = cg1 + 1j*cg2
|
|
829
|
+
coeffs[nband,:] = cg
|
|
830
|
+
wfk_file.read(4)
|
|
831
|
+
return wc.WFK(
|
|
832
|
+
eigenvalues=np.array(eigs),
|
|
833
|
+
wfk_coeffs=np.array(coeffs),
|
|
834
|
+
pw_indices=np.array(pw_inds),
|
|
835
|
+
kpoints=np.array(self.kpts[ind]),
|
|
836
|
+
nkpt=self.nkpt,
|
|
837
|
+
nbands=self.bands[0],
|
|
838
|
+
ngfftx=self.ngfftx,
|
|
839
|
+
ngffty=self.ngffty,
|
|
840
|
+
ngfftz=self.ngfftz,
|
|
841
|
+
symrel=np.array(self.symrel),
|
|
842
|
+
nsym=self.nsym,
|
|
843
|
+
lattice=self.real_lattice,
|
|
844
|
+
natom=self.natom,
|
|
845
|
+
xred=np.array(self.xred),
|
|
846
|
+
typat=self.typat,
|
|
847
|
+
znucltypat=self.znucltypat,
|
|
848
|
+
fermi_energy=self.fermi,
|
|
849
|
+
non_symm_vecs=np.array(self.tnons),
|
|
850
|
+
time_reversal=self.time_reversal
|
|
851
|
+
)
|
|
852
|
+
#-----------------------------------------------------------------------------------------------------------------#
|
|
853
|
+
# method to read only eigenvalues from body of abinit version 10 wavefunction file
|
|
854
|
+
def ReadEigenvalues(
|
|
855
|
+
self, check_time_rev:bool=True
|
|
856
|
+
)->Generator[wc.WFK, None, None]:
|
|
857
|
+
'''
|
|
858
|
+
Method that constructs WFK objects from ABINIT v10 WFK file.
|
|
859
|
+
'''
|
|
860
|
+
# check for time reversal symmetry
|
|
861
|
+
if check_time_rev:
|
|
862
|
+
self._CheckTimeRev()
|
|
863
|
+
else:
|
|
864
|
+
self.time_reversal = False
|
|
865
|
+
# read wfk
|
|
866
|
+
wfk = open(self.filename, 'rb')
|
|
867
|
+
#-----------#
|
|
868
|
+
# skip header
|
|
869
|
+
wfk.read(468)
|
|
870
|
+
wfk.read(4*(2*self.nkpt + self.nkpt*self.nsppol + self.npsp + 10*self.nsym + self.natom))
|
|
871
|
+
wfk.read(8*(4*self.nkpt + self.bandtot + 3*self.nsym + 2*self.ntypat + 3*self.natom))
|
|
872
|
+
wfk.read(self.npsp*(208))
|
|
873
|
+
if self.usepaw == 1:
|
|
874
|
+
wfk.read(4)
|
|
875
|
+
for _ in range(self.natom):
|
|
876
|
+
for _ in range(self.nspden):
|
|
877
|
+
_ = wfk.read(4)
|
|
878
|
+
wfk.read(4)
|
|
879
|
+
wfk.read(4)
|
|
880
|
+
wfk.read(8)
|
|
881
|
+
for i in range(self.natom):
|
|
882
|
+
for _ in range(self.nspden_paw):
|
|
883
|
+
nrhoij = self.nrho[i]
|
|
884
|
+
for _ in range(nrhoij):
|
|
885
|
+
_ = wfk.read(4)
|
|
886
|
+
wfk.read(4)
|
|
887
|
+
for i in range(self.natom):
|
|
888
|
+
for _ in range(self.nspden_paw):
|
|
889
|
+
nrhoij = self.nrho[i]
|
|
890
|
+
for _ in range(nrhoij):
|
|
891
|
+
_ = wfk.read(8)
|
|
892
|
+
wfk.read(4)
|
|
893
|
+
#-------------------------------#
|
|
894
|
+
# begin reading wavefunction body
|
|
895
|
+
for i in range(self.nsppol):
|
|
896
|
+
for j in range(self.nkpt):
|
|
897
|
+
print(f'Reading kpoint {j+1} of {self.nkpt}', end='\r')
|
|
898
|
+
if j+1 == self.nkpt:
|
|
899
|
+
print('\n', end='')
|
|
900
|
+
wfk.read(4)
|
|
901
|
+
npw = bytes2int(wfk.read(4))
|
|
902
|
+
self.nspinor = bytes2int(wfk.read(4))
|
|
903
|
+
nband_temp = bytes2int(wfk.read(4))
|
|
904
|
+
eigenvalues = np.zeros((1,nband_temp), dtype=float)
|
|
905
|
+
wfk.read(8)
|
|
906
|
+
wfk.read(12*npw)
|
|
907
|
+
wfk.read(8)
|
|
908
|
+
#------------------------------------------------------------------#
|
|
909
|
+
# only need eigenvalues for Fermi surface, skip over everything else
|
|
910
|
+
for nband in range(nband_temp):
|
|
911
|
+
eigenval = bytes2float(wfk.read(8))
|
|
912
|
+
eigenvalues[:,nband] = eigenval
|
|
913
|
+
wfk.read(nband_temp*8)
|
|
914
|
+
wfk.read(4)
|
|
915
|
+
wfk.read(nband_temp*(8 + npw*16))
|
|
916
|
+
yield wc.WFK(
|
|
917
|
+
eigenvalues=np.array(eigenvalues),
|
|
918
|
+
kpoints=np.array(self.kpts[j]),
|
|
919
|
+
nkpt=self.nkpt,
|
|
920
|
+
nbands=self.bands[0],
|
|
921
|
+
ngfftx=self.ngfftx,
|
|
922
|
+
ngffty=self.ngffty,
|
|
923
|
+
ngfftz=self.ngfftz,
|
|
924
|
+
symrel=np.array(self.symrel),
|
|
925
|
+
nsym=self.nsym,
|
|
926
|
+
lattice=self.real_lattice,
|
|
927
|
+
natom=self.natom,
|
|
928
|
+
xred=np.array(self.xred),
|
|
929
|
+
typat=self.typat,
|
|
930
|
+
znucltypat=self.znucltypat,
|
|
931
|
+
fermi_energy=self.fermi,
|
|
932
|
+
non_symm_vecs=np.array(self.tnons),
|
|
933
|
+
time_reversal=self.time_reversal
|
|
934
|
+
)
|
|
935
|
+
print('WFK body read')
|
|
936
|
+
wfk.close()
|
|
937
|
+
|
|
938
|
+
#---------------------------------------------------------------------------------------------------------------------#
|
|
939
|
+
#----------------------------------------------------- END CLASS -----------------------------------------------------#
|
|
940
|
+
#---------------------------------------------------------------------------------------------------------------------#
|
|
941
|
+
|
|
942
|
+
class AbinitNetCDF():
|
|
943
|
+
def __init__(
|
|
944
|
+
self, filename:str
|
|
945
|
+
):
|
|
946
|
+
print('WFK found with netCDF4 format')
|
|
947
|
+
self.filename = filename
|
|
948
|
+
self.dataset = self._GetDataset()
|
|
949
|
+
self.kpts:np.ndarray = self.dataset.variables['reduced_coordinates_of_kpoints'][:]
|
|
950
|
+
self.nkpt:int = self.dataset.dimensions['number_of_kpoints'].size
|
|
951
|
+
self.xred:np.ndarray = self.dataset.variables['reduced_atom_positions'][:]
|
|
952
|
+
self.natom:int = self.dataset.dimensions['number_of_atoms'].size
|
|
953
|
+
self.tnons:np.ndarray = self.dataset.variables['reduced_symmetry_translations'][:]
|
|
954
|
+
self.symrel:np.ndarray = self.dataset.variables['reduced_symmetry_matrices'][:]
|
|
955
|
+
self.symrel = np.array([op.T for op in self.symrel])
|
|
956
|
+
self.nsym:int = self.dataset.dimensions['number_of_symmetry_operations'].size
|
|
957
|
+
self.fermi:float = self.dataset['fermi_energy'][:]
|
|
958
|
+
self.real_lattice:np.ndarray = self.dataset['primitive_vectors'][:]
|
|
959
|
+
self.real_lattice *= 0.529177
|
|
960
|
+
self.ngfftx:int = self.dataset.dimensions['number_of_grid_points_vector1'].size
|
|
961
|
+
self.ngffty:int = self.dataset.dimensions['number_of_grid_points_vector2'].size
|
|
962
|
+
self.ngfftz:int = self.dataset.dimensions['number_of_grid_points_vector3'].size
|
|
963
|
+
self.nband:int = int(self.dataset.dimensions['bantot'].size / self.nkpt)
|
|
964
|
+
self.typat:list = self.dataset.variables['atom_species'][:].tolist()
|
|
965
|
+
self.zion:np.ndarray = self.dataset.variables['atomic_numbers'][:]
|
|
966
|
+
self.znucltypat:list = [int(nuc) for nuc in self.zion]
|
|
967
|
+
self.bands = [self.nband]
|
|
968
|
+
#---------------------------------------------------------------------------------------------------------------------#
|
|
969
|
+
#------------------------------------------------------ METHODS ------------------------------------------------------#
|
|
970
|
+
#---------------------------------------------------------------------------------------------------------------------#
|
|
971
|
+
# create netCDF dataset
|
|
972
|
+
def _GetDataset(
|
|
973
|
+
self
|
|
974
|
+
):
|
|
975
|
+
from netCDF4 import Dataset
|
|
976
|
+
return Dataset(self.filename,format='NETCDF4')
|
|
977
|
+
#-----------------------------------------------------------------------------------------------------------------#
|
|
978
|
+
# check for time reversal symmetry
|
|
979
|
+
def _CheckTimeRev(
|
|
980
|
+
self
|
|
981
|
+
):
|
|
982
|
+
# if system is centrosymmetric, do not double reciprocal symmetry operations
|
|
983
|
+
if True in [np.array_equal(-np.identity(3),mat) for mat in self.symrel]:
|
|
984
|
+
self.time_reversal = False
|
|
985
|
+
else:
|
|
986
|
+
self.time_reversal = True
|
|
987
|
+
print((
|
|
988
|
+
'Noncentrosymmetric system identified, assuming time reversal symmetry\n'
|
|
989
|
+
'To change this, set "check_time_rev" keyword to False when calling ReadWFK() or ReadEigenvalues()'
|
|
990
|
+
))
|
|
991
|
+
#-----------------------------------------------------------------------------------------------------------------#
|
|
992
|
+
# fetch wavefunction coefficients from netcdf dataset
|
|
993
|
+
def ReadWFK(
|
|
994
|
+
self, energy_level:float=np.nan, width:float=np.nan, check_time_rev:bool=True
|
|
995
|
+
)->Generator[wc.WFK, None, None]:
|
|
996
|
+
# check for time reversal symmetry
|
|
997
|
+
if check_time_rev:
|
|
998
|
+
self._CheckTimeRev()
|
|
999
|
+
else:
|
|
1000
|
+
self.time_reversal = False
|
|
1001
|
+
eigs:np.ndarray = self.dataset.variables['eigenvalues'][:][0]
|
|
1002
|
+
pw_inds:np.ndarray = self.dataset.variables['reduced_coordinates_of_plane_waves'][:]
|
|
1003
|
+
# restructure wavefunction coefficients
|
|
1004
|
+
for kpt in range(self.nkpt):
|
|
1005
|
+
print(f'Reading kpoint {kpt+1} of {self.nkpt}', end='\r')
|
|
1006
|
+
kpt_coeffs = []
|
|
1007
|
+
# format plane wave indices
|
|
1008
|
+
kpt_pw_inds = pw_inds[kpt]
|
|
1009
|
+
kpt_pw_inds = kpt_pw_inds[~kpt_pw_inds.mask]
|
|
1010
|
+
kpt_pw_inds = kpt_pw_inds.reshape((-1,3))
|
|
1011
|
+
kpt_pw_inds = np.ma.getdata(kpt_pw_inds)
|
|
1012
|
+
coeffs = self.dataset.variables['coefficients_of_wavefunctions'][0,kpt]
|
|
1013
|
+
for band in coeffs:
|
|
1014
|
+
band = band[0]
|
|
1015
|
+
band = band[~band.mask]
|
|
1016
|
+
band = band.reshape((-1,2))
|
|
1017
|
+
band = np.ma.getdata(band)
|
|
1018
|
+
kpt_coeffs.append(band[:,0] + 1j*band[:,1])
|
|
1019
|
+
yield wc.WFK(
|
|
1020
|
+
eigenvalues=eigs[kpt,:],
|
|
1021
|
+
wfk_coeffs=np.array(kpt_coeffs),
|
|
1022
|
+
pw_indices=kpt_pw_inds,
|
|
1023
|
+
kpoints=self.kpts[kpt],
|
|
1024
|
+
nkpt=self.nkpt,
|
|
1025
|
+
nbands=self.nband,
|
|
1026
|
+
ngfftx=self.ngfftx,
|
|
1027
|
+
ngffty=self.ngffty,
|
|
1028
|
+
ngfftz=self.ngfftz,
|
|
1029
|
+
symrel=self.symrel,
|
|
1030
|
+
nsym=self.nsym,
|
|
1031
|
+
lattice=self.real_lattice,
|
|
1032
|
+
natom=self.natom,
|
|
1033
|
+
xred=self.xred,
|
|
1034
|
+
typat=self.typat,
|
|
1035
|
+
znucltypat=self.znucltypat,
|
|
1036
|
+
fermi_energy=self.fermi,
|
|
1037
|
+
non_symm_vecs=self.tnons,
|
|
1038
|
+
time_reversal=self.time_reversal
|
|
1039
|
+
)
|
|
1040
|
+
#-----------------------------------------------------------------------------------------------------------------#
|
|
1041
|
+
# fetch eigenvalues from netcdf dataset
|
|
1042
|
+
def ReadEigenvalues(
|
|
1043
|
+
self, energy_level:float=np.nan, width:float=np.nan, check_time_rev:bool=True
|
|
1044
|
+
)->Generator[wc.WFK, None, None]:
|
|
1045
|
+
# check for time reversal symmetry
|
|
1046
|
+
if check_time_rev:
|
|
1047
|
+
self._CheckTimeRev()
|
|
1048
|
+
else:
|
|
1049
|
+
self.time_reversal = False
|
|
1050
|
+
eigs:np.ndarray = self.dataset.variables['eigenvalues'][:][0]
|
|
1051
|
+
for i, kpt in enumerate(self.kpts):
|
|
1052
|
+
print(f'Reading kpoint {i+1} of {self.nkpt}', end='\r')
|
|
1053
|
+
yield wc.WFK(
|
|
1054
|
+
eigenvalues=eigs[i,:],
|
|
1055
|
+
kpoints=kpt,
|
|
1056
|
+
nkpt=self.nkpt,
|
|
1057
|
+
nbands=self.nband,
|
|
1058
|
+
ngfftx=self.ngfftx,
|
|
1059
|
+
ngffty=self.ngffty,
|
|
1060
|
+
ngfftz=self.ngfftz,
|
|
1061
|
+
symrel=self.symrel,
|
|
1062
|
+
nsym=self.nsym,
|
|
1063
|
+
lattice=self.real_lattice,
|
|
1064
|
+
natom=self.natom,
|
|
1065
|
+
xred=self.xred,
|
|
1066
|
+
typat=self.typat,
|
|
1067
|
+
znucltypat=self.znucltypat,
|
|
1068
|
+
fermi_energy=self.fermi,
|
|
1069
|
+
non_symm_vecs=self.tnons,
|
|
1070
|
+
time_reversal=self.time_reversal
|
|
1071
|
+
)
|
|
1072
|
+
|
|
1073
|
+
#---------------------------------------------------------------------------------------------------------------------#
|
|
1074
|
+
#----------------------------------------------------- END CLASS -----------------------------------------------------#
|
|
1075
|
+
#---------------------------------------------------------------------------------------------------------------------#
|
|
1076
|
+
|
|
1077
|
+
def AbinitWFK(
|
|
1078
|
+
filename:str
|
|
1079
|
+
)->Union[Abinit10WFK, Abinit7WFK, AbinitNetCDF]:
|
|
1080
|
+
'''
|
|
1081
|
+
A function for identifying ABINIT version and constructing respective AbinitWFK object.
|
|
1082
|
+
'''
|
|
1083
|
+
if filename.endswith('.nc'):
|
|
1084
|
+
return AbinitNetCDF(filename)
|
|
1085
|
+
wfk = open(filename, 'rb')
|
|
1086
|
+
wfk.read(4)
|
|
1087
|
+
version = wfk.read(6).decode()
|
|
1088
|
+
wfk.close()
|
|
1089
|
+
if version.strip().split('.')[0] == '7':
|
|
1090
|
+
return Abinit7WFK(filename)
|
|
1091
|
+
elif version.strip().split('.')[0] == '10':
|
|
1092
|
+
return Abinit10WFK(filename)
|
|
1093
|
+
else:
|
|
1094
|
+
print(f'Version {version} may not be supported, attempting to analyze with v10 procedure.')
|
|
1095
1095
|
return Abinit10WFK(filename)
|