root / ase / calculators / abinit.py @ 20
Historique | Voir | Annoter | Télécharger (21,37 ko)
1 | 1 | tkerber | """This module defines an ASE interface to ABINIT.
|
---|---|---|---|
2 | 1 | tkerber |
|
3 | 1 | tkerber | http://www.abinit.org/
|
4 | 1 | tkerber | """
|
5 | 1 | tkerber | |
6 | 1 | tkerber | import os |
7 | 1 | tkerber | from glob import glob |
8 | 1 | tkerber | from os.path import join, isfile, islink |
9 | 1 | tkerber | |
10 | 1 | tkerber | import numpy as np |
11 | 1 | tkerber | |
12 | 1 | tkerber | from ase.data import chemical_symbols |
13 | 1 | tkerber | from ase.data import atomic_numbers |
14 | 1 | tkerber | from ase.units import Bohr, Hartree |
15 | 1 | tkerber | |
16 | 1 | tkerber | |
17 | 1 | tkerber | class Abinit: |
18 | 1 | tkerber | """Class for doing ABINIT calculations.
|
19 | 1 | tkerber |
|
20 | 1 | tkerber | The default parameters are very close to those that the ABINIT
|
21 | 1 | tkerber | Fortran code would use. These are the exceptions::
|
22 | 1 | tkerber |
|
23 | 1 | tkerber | calc = Abinit(label='abinit', xc='LDA', pulay=5, mix=0.1)
|
24 | 1 | tkerber |
|
25 | 1 | tkerber | Use the set_inp method to set extra INPUT parameters::
|
26 | 1 | tkerber |
|
27 | 1 | tkerber | calc.set_inp('nstep', 30)
|
28 | 1 | tkerber |
|
29 | 1 | tkerber | """
|
30 | 1 | tkerber | def __init__(self, label='abinit', xc='LDA', kpts=None, nbands=0, |
31 | 1 | tkerber | width=0.04*Hartree, ecut=None, charge=0, |
32 | 1 | tkerber | pulay=5, mix=0.1, pps='fhi', toldfe=1.0e-6 |
33 | 1 | tkerber | ): |
34 | 1 | tkerber | """Construct ABINIT-calculator object.
|
35 | 1 | tkerber |
|
36 | 1 | tkerber | Parameters
|
37 | 1 | tkerber | ==========
|
38 | 1 | tkerber | label: str
|
39 | 1 | tkerber | Prefix to use for filenames (label.in, label.txt, ...).
|
40 | 1 | tkerber | Default is 'abinit'.
|
41 | 1 | tkerber | xc: str
|
42 | 1 | tkerber | Exchange-correlation functional. Must be one of LDA, PBE,
|
43 | 1 | tkerber | revPBE, RPBE.
|
44 | 1 | tkerber | kpts: list of three int
|
45 | 1 | tkerber | Monkhost-Pack sampling.
|
46 | 1 | tkerber | nbands: int
|
47 | 1 | tkerber | Number of bands.
|
48 | 1 | tkerber | Default is 0.
|
49 | 1 | tkerber | width: float
|
50 | 1 | tkerber | Fermi-distribution width in eV.
|
51 | 1 | tkerber | Default is 0.04 Hartree.
|
52 | 1 | tkerber | ecut: float
|
53 | 1 | tkerber | Planewave cutoff energy in eV.
|
54 | 1 | tkerber | No default.
|
55 | 1 | tkerber | charge: float
|
56 | 1 | tkerber | Total charge of the system.
|
57 | 1 | tkerber | Default is 0.
|
58 | 1 | tkerber | pulay: int
|
59 | 1 | tkerber | Number of old densities to use for Pulay mixing.
|
60 | 1 | tkerber | mix: float
|
61 | 1 | tkerber | Mixing parameter between zero and one for density mixing.
|
62 | 1 | tkerber |
|
63 | 1 | tkerber | Examples
|
64 | 1 | tkerber | ========
|
65 | 1 | tkerber | Use default values:
|
66 | 1 | tkerber |
|
67 | 1 | tkerber | >>> h = Atoms('H', calculator=Abinit())
|
68 | 1 | tkerber | >>> h.center(vacuum=3.0)
|
69 | 1 | tkerber | >>> e = h.get_potential_energy()
|
70 | 1 | tkerber |
|
71 | 1 | tkerber | """
|
72 | 1 | tkerber | |
73 | 1 | tkerber | if not nbands > 0: |
74 | 1 | tkerber | raise ValueError('Number of bands (nbands) not set') |
75 | 1 | tkerber | |
76 | 1 | tkerber | if ecut is None: |
77 | 1 | tkerber | raise ValueError('Planewave cutoff energy in eV (ecut) not set') |
78 | 1 | tkerber | |
79 | 1 | tkerber | self.label = label#################### != out |
80 | 1 | tkerber | self.xc = xc
|
81 | 1 | tkerber | self.kpts = kpts
|
82 | 1 | tkerber | self.nbands = nbands
|
83 | 1 | tkerber | self.width = width
|
84 | 1 | tkerber | self.ecut = ecut
|
85 | 1 | tkerber | self.charge = charge
|
86 | 1 | tkerber | self.pulay = pulay
|
87 | 1 | tkerber | self.mix = mix
|
88 | 1 | tkerber | self.pps = pps
|
89 | 1 | tkerber | self.toldfe = toldfe
|
90 | 1 | tkerber | if not pps in ['fhi', 'hgh', 'hgh.sc']: |
91 | 1 | tkerber | raise ValueError('Unexpected PP identifier %s' % pps) |
92 | 1 | tkerber | |
93 | 1 | tkerber | self.converged = False |
94 | 1 | tkerber | self.inp = {}
|
95 | 1 | tkerber | self.n_entries_int = 20 # integer entries per line |
96 | 1 | tkerber | self.n_entries_float = 8 # float entries per line |
97 | 1 | tkerber | |
98 | 1 | tkerber | def update(self, atoms): |
99 | 1 | tkerber | if (not self.converged or |
100 | 1 | tkerber | len(self.numbers) != len(atoms) or |
101 | 1 | tkerber | (self.numbers != atoms.get_atomic_numbers()).any()):
|
102 | 1 | tkerber | self.initialize(atoms)
|
103 | 1 | tkerber | self.calculate(atoms)
|
104 | 1 | tkerber | elif ((self.positions != atoms.get_positions()).any() or |
105 | 1 | tkerber | (self.pbc != atoms.get_pbc()).any() or |
106 | 1 | tkerber | (self.cell != atoms.get_cell()).any()):
|
107 | 1 | tkerber | self.calculate(atoms)
|
108 | 1 | tkerber | |
109 | 1 | tkerber | def initialize(self, atoms): |
110 | 1 | tkerber | self.numbers = atoms.get_atomic_numbers().copy()
|
111 | 1 | tkerber | self.species = []
|
112 | 1 | tkerber | for a, Z in enumerate(self.numbers): |
113 | 1 | tkerber | if Z not in self.species: |
114 | 1 | tkerber | self.species.append(Z)
|
115 | 1 | tkerber | |
116 | 1 | tkerber | if 'ABINIT_PP_PATH' in os.environ: |
117 | 1 | tkerber | pppaths = os.environ['ABINIT_PP_PATH'].split(':') |
118 | 1 | tkerber | else:
|
119 | 1 | tkerber | pppaths = [] |
120 | 1 | tkerber | |
121 | 1 | tkerber | self.ppp_list = []
|
122 | 1 | tkerber | if self.xc != 'LDA': |
123 | 1 | tkerber | xcname = 'GGA'
|
124 | 1 | tkerber | else:
|
125 | 1 | tkerber | xcname = 'LDA'
|
126 | 1 | tkerber | |
127 | 1 | tkerber | for Z in self.species: |
128 | 1 | tkerber | symbol = chemical_symbols[abs(Z)]
|
129 | 1 | tkerber | number = atomic_numbers[symbol] |
130 | 1 | tkerber | |
131 | 1 | tkerber | pps = self.pps
|
132 | 1 | tkerber | if pps == 'fhi': |
133 | 1 | tkerber | name = '%02d-%s.%s.fhi' % (number, symbol, xcname)
|
134 | 1 | tkerber | elif pps in ('hgh', 'hgh.sc'): |
135 | 1 | tkerber | hghtemplate = '%d%s.%s.hgh' # E.g. "42mo.6.hgh" |
136 | 1 | tkerber | # There might be multiple files with different valence
|
137 | 1 | tkerber | # electron counts, so we must choose between
|
138 | 1 | tkerber | # the ordinary and the semicore versions for some elements.
|
139 | 1 | tkerber | #
|
140 | 1 | tkerber | # Therefore we first use glob to get all relevant files,
|
141 | 1 | tkerber | # then pick the correct one afterwards.
|
142 | 1 | tkerber | name = hghtemplate % (number, symbol.lower(), '*')
|
143 | 1 | tkerber | |
144 | 1 | tkerber | found = False
|
145 | 1 | tkerber | for path in pppaths: |
146 | 1 | tkerber | if pps.startswith('hgh'): |
147 | 1 | tkerber | filenames = glob(join(path, name)) |
148 | 1 | tkerber | if not filenames: |
149 | 1 | tkerber | continue
|
150 | 1 | tkerber | assert len(filenames) in [0, 1, 2] |
151 | 1 | tkerber | if pps == 'hgh': |
152 | 1 | tkerber | selector = min # Lowest possible valence electron count |
153 | 1 | tkerber | else:
|
154 | 1 | tkerber | assert pps == 'hgh.sc' |
155 | 1 | tkerber | selector = max # Semicore - highest electron count |
156 | 1 | tkerber | Z = selector([int(os.path.split(name)[1].split('.')[1]) |
157 | 1 | tkerber | for name in filenames]) |
158 | 1 | tkerber | name = hghtemplate % (number, symbol.lower(), str(Z))
|
159 | 1 | tkerber | filename = join(path, name) |
160 | 1 | tkerber | if isfile(filename) or islink(filename): |
161 | 1 | tkerber | found = True
|
162 | 1 | tkerber | self.ppp_list.append(filename)
|
163 | 1 | tkerber | break
|
164 | 1 | tkerber | if not found: |
165 | 1 | tkerber | raise RuntimeError('No pseudopotential for %s!' % symbol) |
166 | 1 | tkerber | |
167 | 1 | tkerber | self.converged = False |
168 | 1 | tkerber | |
169 | 1 | tkerber | def get_potential_energy(self, atoms, force_consistent=False): |
170 | 1 | tkerber | self.update(atoms)
|
171 | 1 | tkerber | |
172 | 1 | tkerber | if force_consistent:
|
173 | 1 | tkerber | return self.efree |
174 | 1 | tkerber | else:
|
175 | 1 | tkerber | # Energy extrapolated to zero Kelvin:
|
176 | 1 | tkerber | return (self.etotal + self.efree) / 2 |
177 | 1 | tkerber | |
178 | 1 | tkerber | def get_number_of_bands(self): |
179 | 1 | tkerber | return self.nbands |
180 | 1 | tkerber | |
181 | 1 | tkerber | def get_kpts_info(self, kpt=0, spin=0, mode='eigenvalues'): |
182 | 1 | tkerber | return self.read_kpts_info(kpt, spin, mode) |
183 | 1 | tkerber | |
184 | 1 | tkerber | def get_k_point_weights(self): |
185 | 1 | tkerber | return self.get_kpts_info(kpt=0, spin=0, mode='k_point_weights') |
186 | 1 | tkerber | |
187 | 1 | tkerber | def get_bz_k_points(self): |
188 | 1 | tkerber | raise NotImplementedError |
189 | 1 | tkerber | |
190 | 1 | tkerber | def get_ibz_k_points(self): |
191 | 1 | tkerber | return self.get_kpts_info(kpt=0, spin=0, mode='ibz_k_points') |
192 | 1 | tkerber | |
193 | 1 | tkerber | def get_fermi_level(self): |
194 | 1 | tkerber | return self.read_fermi() |
195 | 1 | tkerber | |
196 | 1 | tkerber | def get_eigenvalues(self, kpt=0, spin=0): |
197 | 1 | tkerber | return self.get_kpts_info(kpt, spin, 'eigenvalues') |
198 | 1 | tkerber | |
199 | 1 | tkerber | def get_occupations(self, kpt=0, spin=0): |
200 | 1 | tkerber | return self.get_kpts_info(kpt, spin, 'occupations') |
201 | 1 | tkerber | |
202 | 1 | tkerber | def get_forces(self, atoms): |
203 | 1 | tkerber | self.update(atoms)
|
204 | 1 | tkerber | return self.forces.copy() |
205 | 1 | tkerber | |
206 | 1 | tkerber | def get_stress(self, atoms): |
207 | 1 | tkerber | self.update(atoms)
|
208 | 1 | tkerber | return self.stress.copy() |
209 | 1 | tkerber | |
210 | 1 | tkerber | def calculate(self, atoms): |
211 | 1 | tkerber | self.positions = atoms.get_positions().copy()
|
212 | 1 | tkerber | self.cell = atoms.get_cell().copy()
|
213 | 1 | tkerber | self.pbc = atoms.get_pbc().copy()
|
214 | 1 | tkerber | |
215 | 1 | tkerber | self.write_files()
|
216 | 1 | tkerber | |
217 | 1 | tkerber | self.write_inp(atoms)
|
218 | 1 | tkerber | |
219 | 1 | tkerber | abinit = os.environ['ABINIT_SCRIPT']
|
220 | 1 | tkerber | locals = {'label': self.label} |
221 | 1 | tkerber | |
222 | 1 | tkerber | # Now, because (stupidly) abinit when it finds a name it uses nameA
|
223 | 1 | tkerber | # and when nameA exists it uses nameB, etc.
|
224 | 1 | tkerber | # we need to rename our *.txt file to *.txt.bak
|
225 | 1 | tkerber | filename = self.label + '.txt' |
226 | 1 | tkerber | if islink(filename) or isfile(filename): |
227 | 1 | tkerber | os.rename(filename, filename+'.bak')
|
228 | 1 | tkerber | |
229 | 1 | tkerber | execfile(abinit, {}, locals) |
230 | 1 | tkerber | exitcode = locals['exitcode'] |
231 | 1 | tkerber | if exitcode != 0: |
232 | 1 | tkerber | raise RuntimeError(('Abinit exited with exit code: %d. ' + |
233 | 1 | tkerber | 'Check %s.log for more information.') %
|
234 | 1 | tkerber | (exitcode, self.label))
|
235 | 1 | tkerber | |
236 | 1 | tkerber | self.read()
|
237 | 1 | tkerber | |
238 | 1 | tkerber | self.converged = True |
239 | 1 | tkerber | |
240 | 1 | tkerber | def write_files(self): |
241 | 1 | tkerber | """Write input parameters to files-file."""
|
242 | 1 | tkerber | fh = open(self.label + '.files', 'w') |
243 | 1 | tkerber | |
244 | 1 | tkerber | import getpass |
245 | 1 | tkerber | #find a suitable default scratchdir (should be writeable!)
|
246 | 1 | tkerber | username=getpass.getuser() |
247 | 1 | tkerber | |
248 | 1 | tkerber | if os.access("/scratch/"+username,os.W_OK): |
249 | 1 | tkerber | scratch = "/scratch/"+username
|
250 | 1 | tkerber | elif os.access("/scratch/",os.W_OK): |
251 | 1 | tkerber | scratch = "/scratch/"
|
252 | 1 | tkerber | else:
|
253 | 1 | tkerber | if os.access(os.curdir,os.W_OK):
|
254 | 1 | tkerber | scratch = os.curdir #if no /scratch use curdir
|
255 | 1 | tkerber | else:
|
256 | 1 | tkerber | raise IOError,"No suitable scratch directory and no write access to current dir" |
257 | 1 | tkerber | |
258 | 1 | tkerber | fh.write('%s\n' % (self.label+'.in')) # input |
259 | 1 | tkerber | fh.write('%s\n' % (self.label+'.txt')) # output |
260 | 1 | tkerber | fh.write('%s\n' % (self.label+'i')) # input |
261 | 1 | tkerber | fh.write('%s\n' % (self.label+'o')) # output |
262 | 1 | tkerber | # scratch files
|
263 | 1 | tkerber | fh.write('%s\n' % (os.path.join(scratch, self.label+'.abinit'))) |
264 | 1 | tkerber | # Provide the psp files
|
265 | 1 | tkerber | for ppp in self.ppp_list: |
266 | 1 | tkerber | fh.write('%s\n' % (ppp)) # psp file path |
267 | 1 | tkerber | |
268 | 1 | tkerber | fh.close() |
269 | 1 | tkerber | |
270 | 1 | tkerber | def set_inp(self, key, value): |
271 | 1 | tkerber | """Set INPUT parameter."""
|
272 | 1 | tkerber | self.inp[key] = value
|
273 | 1 | tkerber | |
274 | 1 | tkerber | def write_inp(self, atoms): |
275 | 1 | tkerber | """Write input parameters to in-file."""
|
276 | 1 | tkerber | fh = open(self.label + '.in', 'w') |
277 | 1 | tkerber | |
278 | 1 | tkerber | inp = { |
279 | 1 | tkerber | #'SystemLabel': self.label,
|
280 | 1 | tkerber | #'LatticeConstant': 1.0,
|
281 | 1 | tkerber | 'natom': len(atoms), |
282 | 1 | tkerber | 'charge': self.charge, |
283 | 1 | tkerber | 'nband': self.nbands, |
284 | 1 | tkerber | #'DM.UseSaveDM': self.converged,
|
285 | 1 | tkerber | #'SolutionMethod': 'diagon',
|
286 | 1 | tkerber | 'npulayit': self.pulay, # default 7 |
287 | 1 | tkerber | 'diemix': self.mix |
288 | 1 | tkerber | } |
289 | 1 | tkerber | |
290 | 1 | tkerber | if self.ecut is not None: |
291 | 1 | tkerber | inp['ecut'] = str(self.ecut)+' eV' # default Ha |
292 | 1 | tkerber | |
293 | 1 | tkerber | if self.width is not None: |
294 | 1 | tkerber | inp['tsmear'] = str(self.width)+' eV' # default Ha |
295 | 1 | tkerber | fh.write('occopt 3 # Fermi-Dirac smearing\n')
|
296 | 1 | tkerber | |
297 | 1 | tkerber | inp['ixc'] = { # default 1 |
298 | 1 | tkerber | 'LDA': 7, |
299 | 1 | tkerber | 'PBE': 11, |
300 | 1 | tkerber | 'revPBE': 14, |
301 | 1 | tkerber | 'RPBE': 15, |
302 | 1 | tkerber | 'WC': 23 |
303 | 1 | tkerber | }[self.xc]
|
304 | 1 | tkerber | |
305 | 1 | tkerber | magmoms = atoms.get_initial_magnetic_moments() |
306 | 1 | tkerber | if magmoms.any():
|
307 | 1 | tkerber | inp['nsppol'] = 2 |
308 | 1 | tkerber | fh.write('spinat\n')
|
309 | 1 | tkerber | for n, M in enumerate(magmoms): |
310 | 1 | tkerber | if M != 0: |
311 | 1 | tkerber | fh.write('%.14f %.14f %.14f\n' % (0, 0, M)) |
312 | 1 | tkerber | else:
|
313 | 1 | tkerber | inp['nsppol'] = 1 |
314 | 1 | tkerber | #fh.write('spinat\n')
|
315 | 1 | tkerber | #for n, M in enumerate(magmoms):
|
316 | 1 | tkerber | # if M != 0:
|
317 | 1 | tkerber | # fh.write('%.14f\n' % (M))
|
318 | 1 | tkerber | |
319 | 1 | tkerber | inp.update(self.inp)
|
320 | 1 | tkerber | |
321 | 1 | tkerber | for key, value in inp.items(): |
322 | 1 | tkerber | if value is None: |
323 | 1 | tkerber | continue
|
324 | 1 | tkerber | |
325 | 1 | tkerber | if isinstance(value, list): |
326 | 1 | tkerber | fh.write('%block %s\n' % key)
|
327 | 1 | tkerber | for line in value: |
328 | 1 | tkerber | fh.write(' '.join(['%s' % x for x in line]) + '\n') |
329 | 1 | tkerber | fh.write('%endblock %s\n' % key)
|
330 | 1 | tkerber | |
331 | 1 | tkerber | unit = keys_with_units.get(inpify(key)) |
332 | 1 | tkerber | if unit is None: |
333 | 1 | tkerber | fh.write('%s %s\n' % (key, value))
|
334 | 1 | tkerber | else:
|
335 | 1 | tkerber | if 'fs**2' in unit: |
336 | 1 | tkerber | value /= fs**2
|
337 | 1 | tkerber | elif 'fs' in unit: |
338 | 1 | tkerber | value /= fs |
339 | 1 | tkerber | fh.write('%s %f %s\n' % (key, value, unit))
|
340 | 1 | tkerber | |
341 | 1 | tkerber | fh.write('#Definition of the unit cell\n')
|
342 | 1 | tkerber | fh.write('acell\n')
|
343 | 1 | tkerber | fh.write('%.14f %.14f %.14f Angstrom\n' % (1.0, 1.0, 1.0)) |
344 | 1 | tkerber | fh.write('rprim\n')
|
345 | 1 | tkerber | for v in self.cell: |
346 | 1 | tkerber | fh.write('%.14f %.14f %.14f\n' % tuple(v)) |
347 | 1 | tkerber | |
348 | 1 | tkerber | fh.write('chkprim 0 # Allow non-primitive cells\n')
|
349 | 1 | tkerber | |
350 | 1 | tkerber | fh.write('#Definition of the atom types\n')
|
351 | 1 | tkerber | fh.write('ntypat %d\n' % (len(self.species))) |
352 | 1 | tkerber | fh.write('znucl')
|
353 | 1 | tkerber | for n, Z in enumerate(self.species): |
354 | 1 | tkerber | fh.write(' %d' % (Z))
|
355 | 1 | tkerber | fh.write('\n')
|
356 | 1 | tkerber | fh.write('#Enumerate different atomic species\n')
|
357 | 1 | tkerber | fh.write('typat')
|
358 | 1 | tkerber | fh.write('\n')
|
359 | 1 | tkerber | self.types = []
|
360 | 1 | tkerber | for Z in self.numbers: |
361 | 1 | tkerber | for n, Zs in enumerate(self.species): |
362 | 1 | tkerber | if Z == Zs:
|
363 | 1 | tkerber | self.types.append(n+1) |
364 | 1 | tkerber | for n, type in enumerate(self.types): |
365 | 1 | tkerber | fh.write(' %d' % (type)) |
366 | 1 | tkerber | if n > 1 and ((n % self.n_entries_int) == 1): |
367 | 1 | tkerber | fh.write('\n')
|
368 | 1 | tkerber | fh.write('\n')
|
369 | 1 | tkerber | |
370 | 1 | tkerber | fh.write('#Definition of the atoms\n')
|
371 | 1 | tkerber | fh.write('xangst\n')
|
372 | 1 | tkerber | a = 0
|
373 | 1 | tkerber | for pos, Z in zip(self.positions, self.numbers): |
374 | 1 | tkerber | a += 1
|
375 | 1 | tkerber | fh.write('%.14f %.14f %.14f\n' % tuple(pos)) |
376 | 1 | tkerber | |
377 | 1 | tkerber | if self.kpts is not None: |
378 | 1 | tkerber | fh.write('kptopt %d\n' % (1)) |
379 | 1 | tkerber | fh.write('ngkpt ')
|
380 | 1 | tkerber | fh.write('%d %d %d\n' % tuple(self.kpts)) |
381 | 1 | tkerber | |
382 | 1 | tkerber | fh.write('#Definition of the SCF procedure\n')
|
383 | 1 | tkerber | fh.write('toldfe %.1g\n' % self.toldfe) |
384 | 1 | tkerber | fh.write('chkexit 1 # abinit.exit file in the running directory terminates after the current SCF\n')
|
385 | 1 | tkerber | |
386 | 1 | tkerber | fh.close() |
387 | 1 | tkerber | |
388 | 1 | tkerber | def read_fermi(self): |
389 | 1 | tkerber | """Method that reads Fermi energy in Hartree from the output file
|
390 | 1 | tkerber | and returns it in eV"""
|
391 | 1 | tkerber | E_f=None
|
392 | 1 | tkerber | filename = self.label + '.txt' |
393 | 1 | tkerber | text = open(filename).read().lower()
|
394 | 1 | tkerber | assert 'error' not in text |
395 | 1 | tkerber | for line in iter(text.split('\n')): |
396 | 1 | tkerber | if line.rfind('fermi (or homo) energy (hartree) =') > -1: |
397 | 1 | tkerber | E_f = float(line.split('=')[1].strip().split()[0]) |
398 | 1 | tkerber | return E_f*Hartree
|
399 | 1 | tkerber | |
400 | 1 | tkerber | def read_kpts_info(self, kpt=0, spin=0, mode='eigenvalues'): |
401 | 1 | tkerber | """ Returns list of eigenvalues, occupations, kpts weights, or
|
402 | 1 | tkerber | kpts coordinates for given kpt and spin"""
|
403 | 1 | tkerber | # output may look like this (or without occupation entries); 8 entries per line:
|
404 | 1 | tkerber | #
|
405 | 1 | tkerber | # Eigenvalues (hartree) for nkpt= 20 k points:
|
406 | 1 | tkerber | # kpt# 1, nband= 3, wtk= 0.01563, kpt= 0.0625 0.0625 0.0625 (reduced coord)
|
407 | 1 | tkerber | # -0.09911 0.15393 0.15393
|
408 | 1 | tkerber | # occupation numbers for kpt# 1
|
409 | 1 | tkerber | # 2.00000 0.00000 0.00000
|
410 | 1 | tkerber | # kpt# 2, nband= 3, wtk= 0.04688, kpt= 0.1875 0.0625 0.0625 (reduced coord)
|
411 | 1 | tkerber | # ...
|
412 | 1 | tkerber | #
|
413 | 1 | tkerber | assert mode in ['eigenvalues' , 'occupations', 'ibz_k_points', 'k_point_weights'], 'mode not in [\'eigenvalues\' , \'occupations\', \'ibz_k_points\', \'k_point_weights\']' |
414 | 1 | tkerber | # number of lines of eigenvalues/occupations for a kpt
|
415 | 1 | tkerber | n_entry_lines = max(1, int(self.nbands/self.n_entries_float)) |
416 | 1 | tkerber | #
|
417 | 1 | tkerber | filename = self.label + '.txt' |
418 | 1 | tkerber | text = open(filename).read().lower()
|
419 | 1 | tkerber | assert 'error' not in text |
420 | 1 | tkerber | lines = iter(text.split('\n')) |
421 | 1 | tkerber | text_list = [] |
422 | 1 | tkerber | # find the begining line of eigenvalues
|
423 | 1 | tkerber | contains_eigenvalues = False
|
424 | 1 | tkerber | for line in lines: |
425 | 1 | tkerber | #if line.rfind('eigenvalues (hartree) for nkpt') > -1: #MDTMP
|
426 | 1 | tkerber | if line.rfind('eigenvalues ( ev ) for nkpt') > -1: |
427 | 1 | tkerber | n_kpts = int(line.split('nkpt=')[1].strip().split()[0]) |
428 | 1 | tkerber | contains_eigenvalues = True
|
429 | 1 | tkerber | break
|
430 | 1 | tkerber | # find the end line of eigenvalues starting from linenum
|
431 | 1 | tkerber | for line in lines: |
432 | 1 | tkerber | text_list.append(line) |
433 | 1 | tkerber | if not line.strip(): # find a blank line |
434 | 1 | tkerber | break
|
435 | 1 | tkerber | # remove last (blank) line
|
436 | 1 | tkerber | text_list = text_list[:-1]
|
437 | 1 | tkerber | |
438 | 1 | tkerber | assert contains_eigenvalues, 'No eigenvalues found in the output' |
439 | 1 | tkerber | |
440 | 1 | tkerber | # join text eigenvalues description with eigenvalues
|
441 | 1 | tkerber | # or occupation numbers for kpt# with occupations
|
442 | 1 | tkerber | contains_occupations = False
|
443 | 1 | tkerber | for line in text_list: |
444 | 1 | tkerber | if line.rfind('occupation numbers') > -1: |
445 | 1 | tkerber | contains_occupations = True
|
446 | 1 | tkerber | break
|
447 | 1 | tkerber | if mode == 'occupations': |
448 | 1 | tkerber | assert contains_occupations, 'No occupations found in the output' |
449 | 1 | tkerber | |
450 | 1 | tkerber | if contains_occupations:
|
451 | 1 | tkerber | range_kpts = 2*n_kpts
|
452 | 1 | tkerber | else:
|
453 | 1 | tkerber | range_kpts = n_kpts |
454 | 1 | tkerber | #
|
455 | 1 | tkerber | values_list = [] |
456 | 1 | tkerber | offset = 0
|
457 | 1 | tkerber | for kpt_entry in range(range_kpts): |
458 | 1 | tkerber | full_line = ''
|
459 | 1 | tkerber | for entry_line in range(n_entry_lines+1): |
460 | 1 | tkerber | full_line = full_line+str(text_list[offset+entry_line])
|
461 | 1 | tkerber | first_line = text_list[offset] |
462 | 1 | tkerber | if mode == 'occupations': |
463 | 1 | tkerber | if first_line.rfind('occupation numbers') > -1: |
464 | 1 | tkerber | # extract numbers
|
465 | 1 | tkerber | full_line = [float(v) for v in full_line.split('#')[1].strip().split()[1:]] |
466 | 1 | tkerber | values_list.append(full_line) |
467 | 1 | tkerber | elif mode in ['eigenvalues', 'ibz_k_points', 'k_point_weights']: |
468 | 1 | tkerber | if first_line.rfind('reduced coord') > -1: |
469 | 1 | tkerber | # extract numbers
|
470 | 1 | tkerber | if mode == 'eigenvalues': |
471 | 1 | tkerber | #full_line = [Hartree*float(v) for v in full_line.split(')')[1].strip().split()[:]] # MDTMP
|
472 | 1 | tkerber | full_line = [float(v) for v in full_line.split(')')[1].strip().split()[:]] |
473 | 1 | tkerber | elif mode == 'ibz_k_points': |
474 | 1 | tkerber | full_line = [float(v) for v in full_line.split('kpt=')[1].strip().split('(')[0].split()] |
475 | 1 | tkerber | else:
|
476 | 1 | tkerber | full_line = float(full_line.split('wtk=')[1].strip().split(',')[0].split()[0]) |
477 | 1 | tkerber | values_list.append(full_line) |
478 | 1 | tkerber | offset = offset+n_entry_lines+1
|
479 | 1 | tkerber | #
|
480 | 1 | tkerber | if mode in ['occupations', 'eigenvalues']: |
481 | 1 | tkerber | return np.array(values_list[kpt])
|
482 | 1 | tkerber | else:
|
483 | 1 | tkerber | return np.array(values_list)
|
484 | 1 | tkerber | |
485 | 1 | tkerber | def read(self): |
486 | 1 | tkerber | """Read results from ABINIT's text-output file."""
|
487 | 1 | tkerber | filename = self.label + '.txt' |
488 | 1 | tkerber | text = open(filename).read().lower()
|
489 | 1 | tkerber | assert 'error' not in text |
490 | 1 | tkerber | assert 'was not enough scf cycles to converge' not in text |
491 | 1 | tkerber | # some consistency ckecks
|
492 | 1 | tkerber | for line in iter(text.split('\n')): |
493 | 1 | tkerber | if line.rfind('natom ') > -1: |
494 | 1 | tkerber | natom = int(line.split()[-1]) |
495 | 1 | tkerber | assert natom == len(self.numbers) |
496 | 1 | tkerber | for line in iter(text.split('\n')): |
497 | 1 | tkerber | if line.rfind('znucl ') > -1: |
498 | 1 | tkerber | znucl = [float(Z) for Z in line.split()[1:]] |
499 | 1 | tkerber | for n, Z in enumerate(self.species): |
500 | 1 | tkerber | assert Z == znucl[n]
|
501 | 1 | tkerber | lines = text.split('\n')
|
502 | 1 | tkerber | for n, line in enumerate(lines): |
503 | 1 | tkerber | if line.rfind(' typat ') > -1: |
504 | 1 | tkerber | nlines = len(self.numbers) / self.n_entries_int |
505 | 1 | tkerber | typat = [int(t) for t in line.split()[1:]] # first line |
506 | 1 | tkerber | for nline in range(nlines): # remaining lines |
507 | 1 | tkerber | for t in lines[1 + n + nline].split()[:]: |
508 | 1 | tkerber | typat.append(int(t))
|
509 | 1 | tkerber | for n, t in enumerate(self.types): |
510 | 1 | tkerber | assert t == typat[n]
|
511 | 1 | tkerber | |
512 | 1 | tkerber | lines = iter(text.split('\n')) |
513 | 1 | tkerber | # Stress:
|
514 | 1 | tkerber | # Printed in the output in the following format [Hartree/Bohr^3]:
|
515 | 1 | tkerber | # sigma(1 1)= 4.02063464E-04 sigma(3 2)= 0.00000000E+00
|
516 | 1 | tkerber | # sigma(2 2)= 4.02063464E-04 sigma(3 1)= 0.00000000E+00
|
517 | 1 | tkerber | # sigma(3 3)= 4.02063464E-04 sigma(2 1)= 0.00000000E+00
|
518 | 1 | tkerber | for line in lines: |
519 | 1 | tkerber | if line.rfind('cartesian components of stress tensor (hartree/bohr^3)') > -1: |
520 | 1 | tkerber | self.stress = np.empty((3, 3)) |
521 | 1 | tkerber | for i in range(3): |
522 | 1 | tkerber | entries = lines.next().split() |
523 | 1 | tkerber | self.stress[i,i] = float(entries[2]) |
524 | 1 | tkerber | self.stress[min(3, 4-i)-1, max(1, 2-i)-1] = float(entries[5]) |
525 | 1 | tkerber | self.stress[max(1, 2-i)-1, min(3, 4-i)-1] = float(entries[5]) |
526 | 1 | tkerber | self.stress = self.stress*Hartree/Bohr**3 |
527 | 1 | tkerber | break
|
528 | 1 | tkerber | else:
|
529 | 1 | tkerber | raise RuntimeError |
530 | 1 | tkerber | |
531 | 1 | tkerber | # Energy [Hartree]:
|
532 | 1 | tkerber | # Warning: Etotal could mean both electronic energy and free energy!
|
533 | 1 | tkerber | for line in iter(text.split('\n')): |
534 | 1 | tkerber | if line.rfind('>>>>> internal e=') > -1: |
535 | 1 | tkerber | self.etotal = float(line.split('=')[-1])*Hartree |
536 | 1 | tkerber | for line1 in iter(text.split('\n')): |
537 | 1 | tkerber | if line1.rfind('>>>>>>>>> etotal=') > -1: |
538 | 1 | tkerber | self.efree = float(line1.split('=')[-1])*Hartree |
539 | 1 | tkerber | break
|
540 | 1 | tkerber | else:
|
541 | 1 | tkerber | raise RuntimeError |
542 | 1 | tkerber | break
|
543 | 1 | tkerber | else:
|
544 | 1 | tkerber | for line2 in iter(text.split('\n')): |
545 | 1 | tkerber | if line2.rfind('>>>>>>>>> etotal=') > -1: |
546 | 1 | tkerber | self.etotal = float(line2.split('=')[-1])*Hartree |
547 | 1 | tkerber | self.efree = self.etotal |
548 | 1 | tkerber | break
|
549 | 1 | tkerber | else:
|
550 | 1 | tkerber | raise RuntimeError |
551 | 1 | tkerber | |
552 | 1 | tkerber | # Forces:
|
553 | 1 | tkerber | for line in lines: |
554 | 1 | tkerber | if line.rfind('cartesian forces (ev/angstrom) at end:') > -1: |
555 | 1 | tkerber | forces = [] |
556 | 1 | tkerber | for i in range(len(self.numbers)): |
557 | 1 | tkerber | forces.append(np.array([float(f) for f in lines.next().split()[1:]])) |
558 | 1 | tkerber | self.forces = np.array(forces)
|
559 | 1 | tkerber | break
|
560 | 1 | tkerber | else:
|
561 | 1 | tkerber | raise RuntimeError |
562 | 1 | tkerber | |
563 | 1 | tkerber | def inpify(key): |
564 | 1 | tkerber | return key.lower().replace('_', '').replace('.', '').replace('-', '') |
565 | 1 | tkerber | |
566 | 1 | tkerber | |
567 | 1 | tkerber | keys_with_units = { |
568 | 1 | tkerber | } |
569 | 1 | tkerber | #keys_with_units = {
|
570 | 1 | tkerber | # 'paoenergyshift': 'eV',
|
571 | 1 | tkerber | # 'zmunitslength': 'Bohr',
|
572 | 1 | tkerber | # 'zmunitsangle': 'rad',
|
573 | 1 | tkerber | # 'zmforcetollength': 'eV/Ang',
|
574 | 1 | tkerber | # 'zmforcetolangle': 'eV/rad',
|
575 | 1 | tkerber | # 'zmmaxdispllength': 'Ang',
|
576 | 1 | tkerber | # 'zmmaxdisplangle': 'rad',
|
577 | 1 | tkerber | # 'ecut': 'eV',
|
578 | 1 | tkerber | # 'dmenergytolerance': 'eV',
|
579 | 1 | tkerber | # 'electronictemperature': 'eV',
|
580 | 1 | tkerber | # 'oneta': 'eV',
|
581 | 1 | tkerber | # 'onetaalpha': 'eV',
|
582 | 1 | tkerber | # 'onetabeta': 'eV',
|
583 | 1 | tkerber | # 'onrclwf': 'Ang',
|
584 | 1 | tkerber | # 'onchemicalpotentialrc': 'Ang',
|
585 | 1 | tkerber | # 'onchemicalpotentialtemperature': 'eV',
|
586 | 1 | tkerber | # 'mdmaxcgdispl': 'Ang',
|
587 | 1 | tkerber | # 'mdmaxforcetol': 'eV/Ang',
|
588 | 1 | tkerber | # 'mdmaxstresstol': 'eV/Ang**3',
|
589 | 1 | tkerber | # 'mdlengthtimestep': 'fs',
|
590 | 1 | tkerber | # 'mdinitialtemperature': 'eV',
|
591 | 1 | tkerber | # 'mdtargettemperature': 'eV',
|
592 | 1 | tkerber | # 'mdtargetpressure': 'eV/Ang**3',
|
593 | 1 | tkerber | # 'mdnosemass': 'eV*fs**2',
|
594 | 1 | tkerber | # 'mdparrinellorahmanmass': 'eV*fs**2',
|
595 | 1 | tkerber | # 'mdtaurelax': 'fs',
|
596 | 1 | tkerber | # 'mdbulkmodulus': 'eV/Ang**3',
|
597 | 1 | tkerber | # 'mdfcdispl': 'Ang',
|
598 | 1 | tkerber | # 'warningminimumatomicdistance': 'Ang',
|
599 | 1 | tkerber | # 'rcspatial': 'Ang',
|
600 | 1 | tkerber | # 'kgridcutoff': 'Ang',
|
601 | 1 | tkerber | # 'latticeconstant': 'Ang'} |