bandu 1.3.5__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 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)