root / ase / calculators / vasp.py @ 4
Historique | Voir | Annoter | Télécharger (52 ko)
1 | 1 | tkerber | # Copyright (C) 2008 CSC - Scientific Computing Ltd.
|
---|---|---|---|
2 | 1 | tkerber | """This module defines an ASE interface to VASP.
|
3 | 1 | tkerber |
|
4 | 1 | tkerber | Developed on the basis of modules by Jussi Enkovaara and John
|
5 | 1 | tkerber | Kitchin. The path of the directory containing the pseudopotential
|
6 | 1 | tkerber | directories (potpaw,potpaw_GGA, potpaw_PBE, ...) should be set
|
7 | 1 | tkerber | by the environmental flag $VASP_PP_PATH.
|
8 | 1 | tkerber |
|
9 | 1 | tkerber | The user should also set the environmental flag $VASP_SCRIPT pointing
|
10 | 1 | tkerber | to a python script looking something like::
|
11 | 1 | tkerber |
|
12 | 1 | tkerber | import os
|
13 | 1 | tkerber | exitcode = os.system('vasp')
|
14 | 1 | tkerber |
|
15 | 1 | tkerber | Alternatively, user can set the environmental flag $VASP_COMMAND pointing
|
16 | 1 | tkerber | to the command use the launch vasp e.g. 'vasp' or 'mpirun -n 16 vasp'
|
17 | 1 | tkerber |
|
18 | 1 | tkerber | http://cms.mpi.univie.ac.at/vasp/
|
19 | 1 | tkerber |
|
20 | 1 | tkerber | -Jonas Bjork j.bjork@liverpool.ac.uk
|
21 | 1 | tkerber | """
|
22 | 1 | tkerber | |
23 | 1 | tkerber | import os |
24 | 1 | tkerber | import sys |
25 | 1 | tkerber | from general import Calculator |
26 | 1 | tkerber | from os.path import join, isfile, islink |
27 | 1 | tkerber | |
28 | 1 | tkerber | import numpy as np |
29 | 1 | tkerber | |
30 | 1 | tkerber | import ase |
31 | 1 | tkerber | |
32 | 1 | tkerber | # Parameters that can be set in INCAR. The values which are None
|
33 | 1 | tkerber | # are not written and default parameters of VASP are used for them.
|
34 | 1 | tkerber | |
35 | 1 | tkerber | float_keys = [ |
36 | 1 | tkerber | 'aexx', # Fraction of exact/DFT exchange |
37 | 1 | tkerber | 'aggac', # Fraction of gradient correction to correlation |
38 | 1 | tkerber | 'aggax', # Fraction of gradient correction to exchange |
39 | 1 | tkerber | 'aldac', # Fraction of LDA correlation energy |
40 | 1 | tkerber | 'amix', # |
41 | 1 | tkerber | 'bmix', # tags for mixing |
42 | 1 | tkerber | 'emax', # energy-range for DOSCAR file |
43 | 1 | tkerber | 'emin', # |
44 | 1 | tkerber | 'enaug', # Density cutoff |
45 | 1 | tkerber | 'encut', # Planewave cutoff |
46 | 1 | tkerber | 'encutfock', # FFT grid in the HF related routines |
47 | 1 | tkerber | 'hfscreen', # attribute to change from PBE0 to HSE |
48 | 1 | tkerber | 'potim', # time-step for ion-motion (fs) |
49 | 1 | tkerber | 'nelect', # total number of electrons |
50 | 1 | tkerber | 'pomass', # mass of ions in am |
51 | 1 | tkerber | 'sigma', # broadening in eV |
52 | 1 | tkerber | 'time', # special control tag |
53 | 1 | tkerber | 'zval', # ionic valence |
54 | 1 | tkerber | ] |
55 | 1 | tkerber | |
56 | 1 | tkerber | exp_keys = [ |
57 | 1 | tkerber | 'ediff', # stopping-criterion for electronic upd. |
58 | 1 | tkerber | 'ediffg', # stopping-criterion for ionic upd. |
59 | 1 | tkerber | 'symprec', # precession in symmetry routines |
60 | 1 | tkerber | ] |
61 | 1 | tkerber | |
62 | 1 | tkerber | string_keys = [ |
63 | 1 | tkerber | 'algo', # algorithm: Normal (Davidson) | Fast | Very_Fast (RMM-DIIS) |
64 | 1 | tkerber | 'gga', # xc-type: PW PB LM or 91 |
65 | 1 | tkerber | 'prec', # Precission of calculation (Low, Normal, Accurate) |
66 | 1 | tkerber | 'system', # name of System |
67 | 1 | tkerber | 'tebeg', # |
68 | 1 | tkerber | 'teend', # temperature during run |
69 | 1 | tkerber | ] |
70 | 1 | tkerber | |
71 | 1 | tkerber | int_keys = [ |
72 | 1 | tkerber | 'ialgo', # algorithm: use only 8 (CG) or 48 (RMM-DIIS) |
73 | 1 | tkerber | 'ibrion', # ionic relaxation: 0-MD 1-quasi-New 2-CG |
74 | 1 | tkerber | 'idipol', # monopol/dipol and quadropole corrections |
75 | 1 | tkerber | 'iniwav', # initial electr wf. : 0-lowe 1-rand |
76 | 1 | tkerber | 'isif', # calculate stress and what to relax |
77 | 1 | tkerber | 'ismear', # part. occupancies: -5 Blochl -4-tet -1-fermi 0-gaus >0 MP |
78 | 1 | tkerber | 'ispin', # spin-polarized calculation |
79 | 1 | tkerber | 'istart', # startjob: 0-new 1-cont 2-samecut |
80 | 1 | tkerber | 'isym', # symmetry: 0-nonsym 1-usesym |
81 | 1 | tkerber | 'iwavpr', # prediction of wf.: 0-non 1-charg 2-wave 3-comb |
82 | 1 | tkerber | 'lmaxmix', # |
83 | 1 | tkerber | 'lorbit', # create PROOUT |
84 | 1 | tkerber | 'ngx', # FFT mesh for wavefunctions, x |
85 | 1 | tkerber | 'ngxf', # FFT mesh for charges x |
86 | 1 | tkerber | 'ngy', # FFT mesh for wavefunctions, y |
87 | 1 | tkerber | 'ngyf', # FFT mesh for charges y |
88 | 1 | tkerber | 'ngz', # FFT mesh for wavefunctions, z |
89 | 1 | tkerber | 'ngzf', # FFT mesh for charges z |
90 | 1 | tkerber | 'nbands', # Number of bands |
91 | 1 | tkerber | 'nblk', # blocking for some BLAS calls (Sec. 6.5) |
92 | 1 | tkerber | 'nbmod', # specifies mode for partial charge calculation |
93 | 1 | tkerber | 'nelm', # |
94 | 1 | tkerber | 'nelmdl', # nr. of electronic steps |
95 | 1 | tkerber | 'nelmin',
|
96 | 1 | tkerber | 'nfree', # number of steps per DOF when calculting Hessian using finitite differences |
97 | 1 | tkerber | 'nkred', # define sub grid of q-points for HF with nkredx=nkredy=nkredz |
98 | 1 | tkerber | 'nkredx', # define sub grid of q-points in x direction for HF |
99 | 1 | tkerber | 'nkredy', # define sub grid of q-points in y direction for HF |
100 | 1 | tkerber | 'nkredz', # define sub grid of q-points in z direction for HF |
101 | 1 | tkerber | 'npar', # parallelization over bands |
102 | 1 | tkerber | 'nsim', # evaluate NSIM bands simultaneously if using RMM-DIIS |
103 | 1 | tkerber | 'nsw', # number of steps for ionic upd. |
104 | 1 | tkerber | 'nupdown', # fix spin moment to specified value |
105 | 1 | tkerber | 'nwrite', # verbosity write-flag (how much is written) |
106 | 1 | tkerber | 'smass', # Nose mass-parameter (am) |
107 | 1 | tkerber | 'voskown', # use Vosko, Wilk, Nusair interpolation |
108 | 1 | tkerber | ] |
109 | 1 | tkerber | |
110 | 1 | tkerber | bool_keys = [ |
111 | 1 | tkerber | 'addgrid', # finer grid for augmentation charge density |
112 | 1 | tkerber | 'icharg', # charge: 1-file 2-atom 10-const |
113 | 1 | tkerber | 'lasync', # overlap communcation with calculations |
114 | 1 | tkerber | 'lcharg', # |
115 | 1 | tkerber | 'lcorr', # Harris-correction to forces |
116 | 1 | tkerber | 'ldipol', # potential correction mode |
117 | 1 | tkerber | 'lelf', # create ELFCAR |
118 | 1 | tkerber | 'lhfcalc', # switch to turn on Hartree Fock calculations |
119 | 1 | tkerber | 'lpard', # evaluate partial (band and/or k-point) decomposed charge density |
120 | 1 | tkerber | 'lplane', # parallelisation over the FFT grid |
121 | 1 | tkerber | 'lscalapack', # switch off scaLAPACK |
122 | 1 | tkerber | 'lscalu', # switch of LU decomposition |
123 | 1 | tkerber | 'lsepb', # write out partial charge of each band seperately? |
124 | 1 | tkerber | 'lsepk', # write out partial charge of each k-point seperately? |
125 | 1 | tkerber | 'lthomas', # |
126 | 1 | tkerber | 'lvtot', # create WAVECAR/CHGCAR/LOCPOT |
127 | 1 | tkerber | 'lwave', # |
128 | 1 | tkerber | ] |
129 | 1 | tkerber | |
130 | 1 | tkerber | list_keys = [ |
131 | 1 | tkerber | 'dipol', # center of cell for dipol |
132 | 1 | tkerber | 'eint', # energy range to calculate partial charge for |
133 | 1 | tkerber | 'ferwe', # Fixed band occupation |
134 | 1 | tkerber | 'iband', # bands to calculate partial charge for |
135 | 1 | tkerber | 'magmom', # initial magnetic moments |
136 | 1 | tkerber | 'kpuse', # k-point to calculate partial charge for |
137 | 1 | tkerber | 'ropt', # number of grid points for non-local proj in real space |
138 | 1 | tkerber | 'rwigs', # Wigner-Seitz radii |
139 | 1 | tkerber | ] |
140 | 1 | tkerber | |
141 | 1 | tkerber | special_keys = [ |
142 | 1 | tkerber | 'lreal', # non-local projectors in real space |
143 | 1 | tkerber | ] |
144 | 1 | tkerber | |
145 | 1 | tkerber | keys = [ |
146 | 1 | tkerber | # 'NBLOCK' and KBLOCK inner block; outer block
|
147 | 1 | tkerber | # 'NPACO' and APACO distance and nr. of slots for P.C.
|
148 | 1 | tkerber | # 'WEIMIN, EBREAK, DEPER special control tags
|
149 | 1 | tkerber | ] |
150 | 1 | tkerber | |
151 | 1 | tkerber | class Vasp(Calculator): |
152 | 2 | tkerber | def __init__(self, restart=None, output_template='vasp', track_output=False, write_input = True, |
153 | 1 | tkerber | **kwargs): |
154 | 2 | tkerber | self.bool_write_input = write_input
|
155 | 1 | tkerber | self.name = 'Vasp' |
156 | 1 | tkerber | self.float_params = {}
|
157 | 1 | tkerber | self.exp_params = {}
|
158 | 1 | tkerber | self.string_params = {}
|
159 | 1 | tkerber | self.int_params = {}
|
160 | 1 | tkerber | self.bool_params = {}
|
161 | 1 | tkerber | self.list_params = {}
|
162 | 1 | tkerber | self.special_params = {}
|
163 | 1 | tkerber | for key in float_keys: |
164 | 1 | tkerber | self.float_params[key] = None |
165 | 1 | tkerber | for key in exp_keys: |
166 | 1 | tkerber | self.exp_params[key] = None |
167 | 1 | tkerber | for key in string_keys: |
168 | 1 | tkerber | self.string_params[key] = None |
169 | 1 | tkerber | for key in int_keys: |
170 | 1 | tkerber | self.int_params[key] = None |
171 | 1 | tkerber | for key in bool_keys: |
172 | 1 | tkerber | self.bool_params[key] = None |
173 | 1 | tkerber | for key in list_keys: |
174 | 1 | tkerber | self.list_params[key] = None |
175 | 1 | tkerber | for key in special_keys: |
176 | 1 | tkerber | self.special_params[key] = None |
177 | 1 | tkerber | self.string_params['prec'] = 'Normal' |
178 | 1 | tkerber | |
179 | 1 | tkerber | self.input_params = {
|
180 | 1 | tkerber | 'xc': 'PW91', # exchange correlation potential |
181 | 1 | tkerber | 'setups': None, # Special setups (e.g pv, sv, ...) |
182 | 1 | tkerber | 'txt': '-', # Where to send information |
183 | 1 | tkerber | 'kpts': (1,1,1), # k-points |
184 | 1 | tkerber | 'gamma': False, # Option to use gamma-sampling instead |
185 | 1 | tkerber | # of Monkhorst-Pack
|
186 | 1 | tkerber | } |
187 | 1 | tkerber | |
188 | 1 | tkerber | self.restart = restart
|
189 | 1 | tkerber | self.track_output = track_output
|
190 | 1 | tkerber | self.output_template = output_template
|
191 | 1 | tkerber | if restart:
|
192 | 1 | tkerber | self.restart_load()
|
193 | 1 | tkerber | return
|
194 | 1 | tkerber | |
195 | 1 | tkerber | if self.input_params['xc'] not in ['PW91','LDA','PBE']: |
196 | 1 | tkerber | raise ValueError( |
197 | 1 | tkerber | '%s not supported for xc! use one of: PW91, LDA or PBE.' %
|
198 | 1 | tkerber | kwargs['xc'])
|
199 | 1 | tkerber | self.nbands = self.int_params['nbands'] |
200 | 1 | tkerber | self.atoms = None |
201 | 1 | tkerber | self.positions = None |
202 | 1 | tkerber | self.run_counts = 0 |
203 | 1 | tkerber | self.set(**kwargs)
|
204 | 1 | tkerber | |
205 | 1 | tkerber | def set(self, **kwargs): |
206 | 1 | tkerber | for key in kwargs: |
207 | 1 | tkerber | if self.float_params.has_key(key): |
208 | 1 | tkerber | self.float_params[key] = kwargs[key]
|
209 | 1 | tkerber | elif self.exp_params.has_key(key): |
210 | 1 | tkerber | self.exp_params[key] = kwargs[key]
|
211 | 1 | tkerber | elif self.string_params.has_key(key): |
212 | 1 | tkerber | self.string_params[key] = kwargs[key]
|
213 | 1 | tkerber | elif self.int_params.has_key(key): |
214 | 1 | tkerber | self.int_params[key] = kwargs[key]
|
215 | 1 | tkerber | elif self.bool_params.has_key(key): |
216 | 1 | tkerber | self.bool_params[key] = kwargs[key]
|
217 | 1 | tkerber | elif self.list_params.has_key(key): |
218 | 1 | tkerber | self.list_params[key] = kwargs[key]
|
219 | 1 | tkerber | elif self.special_params.has_key(key): |
220 | 1 | tkerber | self.special_params[key] = kwargs[key]
|
221 | 1 | tkerber | elif self.input_params.has_key(key): |
222 | 1 | tkerber | self.input_params[key] = kwargs[key]
|
223 | 1 | tkerber | else:
|
224 | 1 | tkerber | raise TypeError('Parameter not defined: ' + key) |
225 | 1 | tkerber | |
226 | 1 | tkerber | def update(self, atoms): |
227 | 1 | tkerber | if self.calculation_required(atoms, ['energy']): |
228 | 1 | tkerber | if (self.atoms is None or |
229 | 1 | tkerber | self.atoms.positions.shape != atoms.positions.shape):
|
230 | 1 | tkerber | # Completely new calculation just reusing the same
|
231 | 1 | tkerber | # calculator, so delete any old VASP files found.
|
232 | 1 | tkerber | self.clean()
|
233 | 1 | tkerber | self.calculate(atoms)
|
234 | 1 | tkerber | |
235 | 1 | tkerber | def initialize(self, atoms): |
236 | 1 | tkerber | """Initialize a VASP calculation
|
237 | 1 | tkerber |
|
238 | 1 | tkerber | Constructs the POTCAR file. User should specify the PATH
|
239 | 1 | tkerber | to the pseudopotentials in VASP_PP_PATH environment variable"""
|
240 | 1 | tkerber | |
241 | 1 | tkerber | p = self.input_params
|
242 | 1 | tkerber | |
243 | 1 | tkerber | self.all_symbols = atoms.get_chemical_symbols()
|
244 | 1 | tkerber | self.natoms = len(atoms) |
245 | 1 | tkerber | self.spinpol = atoms.get_initial_magnetic_moments().any()
|
246 | 1 | tkerber | atomtypes = atoms.get_chemical_symbols() |
247 | 1 | tkerber | |
248 | 1 | tkerber | # Determine the number of atoms of each atomic species
|
249 | 1 | tkerber | # sorted after atomic species
|
250 | 1 | tkerber | special_setups = [] |
251 | 1 | tkerber | symbols = {} |
252 | 1 | tkerber | if self.input_params['setups']: |
253 | 1 | tkerber | for m in self.input_params['setups']: |
254 | 1 | tkerber | try :
|
255 | 1 | tkerber | #special_setup[self.input_params['setups'][m]] = int(m)
|
256 | 1 | tkerber | special_setups.append(int(m))
|
257 | 1 | tkerber | except:
|
258 | 1 | tkerber | #print 'setup ' + m + ' is a groups setup'
|
259 | 1 | tkerber | continue
|
260 | 1 | tkerber | #print 'special_setups' , special_setups
|
261 | 1 | tkerber | |
262 | 1 | tkerber | for m,atom in enumerate(atoms): |
263 | 1 | tkerber | symbol = atom.get_symbol() |
264 | 1 | tkerber | if m in special_setups: |
265 | 1 | tkerber | pass
|
266 | 1 | tkerber | else:
|
267 | 1 | tkerber | if not symbols.has_key(symbol): |
268 | 1 | tkerber | symbols[symbol] = 1
|
269 | 1 | tkerber | else:
|
270 | 1 | tkerber | symbols[symbol] += 1
|
271 | 1 | tkerber | |
272 | 1 | tkerber | # Build the sorting list
|
273 | 1 | tkerber | self.sort = []
|
274 | 1 | tkerber | self.sort.extend(special_setups)
|
275 | 1 | tkerber | |
276 | 1 | tkerber | for symbol in symbols: |
277 | 1 | tkerber | for m,atom in enumerate(atoms): |
278 | 1 | tkerber | if m in special_setups: |
279 | 1 | tkerber | pass
|
280 | 1 | tkerber | else:
|
281 | 1 | tkerber | if atom.get_symbol() == symbol:
|
282 | 1 | tkerber | self.sort.append(m)
|
283 | 1 | tkerber | self.resort = range(len(self.sort)) |
284 | 1 | tkerber | for n in range(len(self.resort)): |
285 | 1 | tkerber | self.resort[self.sort[n]] = n |
286 | 1 | tkerber | self.atoms_sorted = atoms[self.sort] |
287 | 1 | tkerber | |
288 | 1 | tkerber | # Check if the necessary POTCAR files exists and
|
289 | 1 | tkerber | # create a list of their paths.
|
290 | 1 | tkerber | self.symbol_count = []
|
291 | 1 | tkerber | for m in special_setups: |
292 | 1 | tkerber | self.symbol_count.append([atomtypes[m],1]) |
293 | 1 | tkerber | for m in symbols: |
294 | 1 | tkerber | self.symbol_count.append([m,symbols[m]])
|
295 | 1 | tkerber | #print 'self.symbol_count',self.symbol_count
|
296 | 1 | tkerber | sys.stdout.flush() |
297 | 1 | tkerber | xc = '/'
|
298 | 1 | tkerber | #print 'p[xc]',p['xc']
|
299 | 1 | tkerber | if p['xc'] == 'PW91': |
300 | 1 | tkerber | xc = '_gga/'
|
301 | 1 | tkerber | elif p['xc'] == 'PBE': |
302 | 1 | tkerber | xc = '_pbe/'
|
303 | 1 | tkerber | if 'VASP_PP_PATH' in os.environ: |
304 | 1 | tkerber | pppaths = os.environ['VASP_PP_PATH'].split(':') |
305 | 1 | tkerber | else:
|
306 | 1 | tkerber | pppaths = [] |
307 | 1 | tkerber | self.ppp_list = []
|
308 | 1 | tkerber | # Setting the pseudopotentials, first special setups and
|
309 | 1 | tkerber | # then according to symbols
|
310 | 1 | tkerber | for m in special_setups: |
311 | 1 | tkerber | name = 'potpaw'+xc.upper() + p['setups'][str(m)] + '/POTCAR' |
312 | 1 | tkerber | found = False
|
313 | 1 | tkerber | for path in pppaths: |
314 | 1 | tkerber | filename = join(path, name) |
315 | 1 | tkerber | #print 'filename', filename
|
316 | 1 | tkerber | if isfile(filename) or islink(filename): |
317 | 1 | tkerber | found = True
|
318 | 1 | tkerber | self.ppp_list.append(filename)
|
319 | 1 | tkerber | break
|
320 | 1 | tkerber | elif isfile(filename + '.Z') or islink(filename + '.Z'): |
321 | 1 | tkerber | found = True
|
322 | 1 | tkerber | self.ppp_list.append(filename+'.Z') |
323 | 1 | tkerber | break
|
324 | 1 | tkerber | if not found: |
325 | 1 | tkerber | raise RuntimeError('No pseudopotential for %s!' % symbol) |
326 | 1 | tkerber | #print 'symbols', symbols
|
327 | 1 | tkerber | for symbol in symbols: |
328 | 1 | tkerber | try:
|
329 | 1 | tkerber | name = 'potpaw'+xc.upper()+symbol + p['setups'][symbol] |
330 | 1 | tkerber | except (TypeError, KeyError): |
331 | 1 | tkerber | name = 'potpaw' + xc.upper() + symbol
|
332 | 1 | tkerber | name += '/POTCAR'
|
333 | 1 | tkerber | found = False
|
334 | 1 | tkerber | for path in pppaths: |
335 | 1 | tkerber | filename = join(path, name) |
336 | 1 | tkerber | #print 'filename', filename
|
337 | 1 | tkerber | if isfile(filename) or islink(filename): |
338 | 1 | tkerber | found = True
|
339 | 1 | tkerber | self.ppp_list.append(filename)
|
340 | 1 | tkerber | break
|
341 | 1 | tkerber | elif isfile(filename + '.Z') or islink(filename + '.Z'): |
342 | 1 | tkerber | found = True
|
343 | 1 | tkerber | self.ppp_list.append(filename+'.Z') |
344 | 1 | tkerber | break
|
345 | 1 | tkerber | if not found: |
346 | 1 | tkerber | raise RuntimeError('No pseudopotential for %s!' % symbol) |
347 | 1 | tkerber | self.converged = None |
348 | 1 | tkerber | self.setups_changed = None |
349 | 1 | tkerber | |
350 | 1 | tkerber | def calculate(self, atoms): |
351 | 1 | tkerber | """Generate necessary files in the working directory and run VASP.
|
352 | 1 | tkerber |
|
353 | 1 | tkerber | The method first write VASP input files, then calls the method
|
354 | 1 | tkerber | which executes VASP. When the VASP run is finished energy, forces,
|
355 | 1 | tkerber | etc. are read from the VASP output.
|
356 | 1 | tkerber | """
|
357 | 1 | tkerber | |
358 | 1 | tkerber | # Write input
|
359 | 1 | tkerber | from ase.io.vasp import write_vasp |
360 | 1 | tkerber | self.initialize(atoms)
|
361 | 1 | tkerber | write_vasp('POSCAR', self.atoms_sorted, symbol_count = self.symbol_count) |
362 | 2 | tkerber | if self.bool_write_input: |
363 | 2 | tkerber | self.write_incar(atoms)
|
364 | 2 | tkerber | self.write_potcar()
|
365 | 2 | tkerber | self.write_kpoints()
|
366 | 1 | tkerber | self.write_sort_file()
|
367 | 1 | tkerber | |
368 | 1 | tkerber | # Execute VASP
|
369 | 1 | tkerber | self.run()
|
370 | 1 | tkerber | # Read output
|
371 | 1 | tkerber | atoms_sorted = ase.io.read('CONTCAR', format='vasp') |
372 | 1 | tkerber | if self.int_params['ibrion']>-1 and self.int_params['nsw']>0: |
373 | 1 | tkerber | # Replace entire atoms object with the one read from CONTCAR.
|
374 | 1 | tkerber | atoms.__dict__ = atoms_sorted[self.resort].__dict__
|
375 | 1 | tkerber | self.converged = self.read_convergence() |
376 | 1 | tkerber | self.set_results(atoms)
|
377 | 1 | tkerber | |
378 | 1 | tkerber | def set_results(self, atoms): |
379 | 1 | tkerber | self.read(atoms)
|
380 | 1 | tkerber | if self.spinpol: |
381 | 1 | tkerber | self.magnetic_moment = self.read_magnetic_moment() |
382 | 1 | tkerber | if self.int_params['lorbit']>=10 or (self.int_params['lorbit']!=None and self.list_params['rwigs']): |
383 | 1 | tkerber | self.magnetic_moments = self.read_magnetic_moments(atoms) |
384 | 1 | tkerber | self.old_float_params = self.float_params.copy() |
385 | 1 | tkerber | self.old_exp_params = self.exp_params.copy() |
386 | 1 | tkerber | self.old_string_params = self.string_params.copy() |
387 | 1 | tkerber | self.old_int_params = self.int_params.copy() |
388 | 1 | tkerber | self.old_input_params = self.input_params.copy() |
389 | 1 | tkerber | self.old_bool_params = self.bool_params.copy() |
390 | 1 | tkerber | self.old_list_params = self.list_params.copy() |
391 | 1 | tkerber | self.atoms = atoms.copy()
|
392 | 1 | tkerber | |
393 | 1 | tkerber | def run(self): |
394 | 1 | tkerber | """Method which explicitely runs VASP."""
|
395 | 1 | tkerber | |
396 | 1 | tkerber | if self.track_output: |
397 | 1 | tkerber | self.out = self.output_template+str(self.run_counts)+'.out' |
398 | 1 | tkerber | self.run_counts += 1 |
399 | 1 | tkerber | else:
|
400 | 1 | tkerber | self.out = self.output_template+'.out' |
401 | 1 | tkerber | stderr = sys.stderr |
402 | 1 | tkerber | p=self.input_params
|
403 | 1 | tkerber | if p['txt'] is None: |
404 | 1 | tkerber | sys.stderr = devnull |
405 | 1 | tkerber | elif p['txt'] == '-': |
406 | 1 | tkerber | pass
|
407 | 1 | tkerber | elif isinstance(p['txt'], str): |
408 | 1 | tkerber | sys.stderr = open(p['txt'], 'w') |
409 | 1 | tkerber | if os.environ.has_key('VASP_COMMAND'): |
410 | 1 | tkerber | vasp = os.environ['VASP_COMMAND']
|
411 | 1 | tkerber | exitcode = os.system('%s > %s' % (vasp, self.out)) |
412 | 1 | tkerber | elif os.environ.has_key('VASP_SCRIPT'): |
413 | 1 | tkerber | vasp = os.environ['VASP_SCRIPT']
|
414 | 1 | tkerber | locals={} |
415 | 1 | tkerber | execfile(vasp, {}, locals) |
416 | 1 | tkerber | exitcode = locals['exitcode'] |
417 | 1 | tkerber | else:
|
418 | 1 | tkerber | raise RuntimeError('Please set either VASP_COMMAND or VASP_SCRIPT environment variable') |
419 | 1 | tkerber | sys.stderr = stderr |
420 | 1 | tkerber | if exitcode != 0: |
421 | 1 | tkerber | raise RuntimeError('Vasp exited with exit code: %d. ' % exitcode) |
422 | 1 | tkerber | |
423 | 1 | tkerber | def restart_load(self): |
424 | 1 | tkerber | """Method which is called upon restart."""
|
425 | 1 | tkerber | |
426 | 1 | tkerber | # Try to read sorting file
|
427 | 1 | tkerber | if os.path.isfile('ase-sort.dat'): |
428 | 1 | tkerber | self.sort = []
|
429 | 1 | tkerber | self.resort = []
|
430 | 1 | tkerber | file = open('ase-sort.dat', 'r') |
431 | 1 | tkerber | lines = file.readlines()
|
432 | 1 | tkerber | file.close()
|
433 | 1 | tkerber | for line in lines: |
434 | 1 | tkerber | data = line.split() |
435 | 1 | tkerber | self.sort.append(int(data[0])) |
436 | 1 | tkerber | self.resort.append(int(data[1])) |
437 | 1 | tkerber | atoms = ase.io.read('CONTCAR', format='vasp')[self.resort] |
438 | 1 | tkerber | else:
|
439 | 1 | tkerber | atoms = ase.io.read('CONTCAR', format='vasp') |
440 | 1 | tkerber | self.sort = range(len(atoms)) |
441 | 1 | tkerber | self.resort = range(len(atoms)) |
442 | 1 | tkerber | self.atoms = atoms.copy()
|
443 | 1 | tkerber | self.read_incar()
|
444 | 1 | tkerber | self.read_outcar()
|
445 | 1 | tkerber | self.set_results(atoms)
|
446 | 1 | tkerber | self.read_kpoints()
|
447 | 1 | tkerber | self.read_potcar()
|
448 | 1 | tkerber | # self.old_incar_params = self.incar_params.copy()
|
449 | 1 | tkerber | self.old_input_params = self.input_params.copy() |
450 | 1 | tkerber | self.converged = self.read_convergence() |
451 | 1 | tkerber | |
452 | 1 | tkerber | def clean(self): |
453 | 1 | tkerber | """Method which cleans up after a calculation.
|
454 | 1 | tkerber |
|
455 | 1 | tkerber | The default files generated by Vasp will be deleted IF this
|
456 | 1 | tkerber | method is called.
|
457 | 1 | tkerber |
|
458 | 1 | tkerber | """
|
459 | 1 | tkerber | files = ['CHG', 'CHGCAR', 'POSCAR', 'INCAR', 'CONTCAR', 'DOSCAR', |
460 | 1 | tkerber | 'EIGENVAL', 'IBZKPT', 'KPOINTS', 'OSZICAR', 'OUTCAR', 'PCDAT', |
461 | 1 | tkerber | 'POTCAR', 'vasprun.xml', 'WAVECAR', 'XDATCAR', |
462 | 1 | tkerber | 'PROCAR', 'ase-sort.dat'] |
463 | 1 | tkerber | for f in files: |
464 | 1 | tkerber | try:
|
465 | 1 | tkerber | os.remove(f) |
466 | 1 | tkerber | except OSError: |
467 | 1 | tkerber | pass
|
468 | 1 | tkerber | |
469 | 1 | tkerber | def set_atoms(self, atoms): |
470 | 1 | tkerber | if (atoms != self.atoms): |
471 | 1 | tkerber | self.converged = None |
472 | 1 | tkerber | self.atoms = atoms.copy()
|
473 | 1 | tkerber | |
474 | 1 | tkerber | def get_atoms(self): |
475 | 1 | tkerber | atoms = self.atoms.copy()
|
476 | 1 | tkerber | atoms.set_calculator(self)
|
477 | 1 | tkerber | return atoms
|
478 | 1 | tkerber | |
479 | 1 | tkerber | def get_potential_energy(self, atoms, force_consistent=False): |
480 | 1 | tkerber | self.update(atoms)
|
481 | 1 | tkerber | if force_consistent:
|
482 | 1 | tkerber | return self.energy_free |
483 | 1 | tkerber | else:
|
484 | 1 | tkerber | return self.energy_zero |
485 | 1 | tkerber | |
486 | 1 | tkerber | def get_forces(self, atoms): |
487 | 1 | tkerber | self.update(atoms)
|
488 | 1 | tkerber | return self.forces |
489 | 1 | tkerber | |
490 | 1 | tkerber | def get_stress(self, atoms): |
491 | 1 | tkerber | self.update(atoms)
|
492 | 1 | tkerber | return self.stress |
493 | 1 | tkerber | |
494 | 1 | tkerber | def read_stress(self): |
495 | 1 | tkerber | for line in open('OUTCAR'): |
496 | 1 | tkerber | if line.find(' in kB ') != -1: |
497 | 1 | tkerber | stress = -np.array([float(a) for a in line.split()[2:]]) \ |
498 | 1 | tkerber | [[0, 1, 2, 4, 5, 3]] \ |
499 | 1 | tkerber | * 1e-1 * ase.units.GPa
|
500 | 1 | tkerber | return stress
|
501 | 1 | tkerber | |
502 | 1 | tkerber | def calculation_required(self, atoms, quantities): |
503 | 1 | tkerber | if (self.positions is None or |
504 | 1 | tkerber | (self.atoms != atoms) or |
505 | 1 | tkerber | (self.float_params != self.old_float_params) or |
506 | 1 | tkerber | (self.exp_params != self.old_exp_params) or |
507 | 1 | tkerber | (self.string_params != self.old_string_params) or |
508 | 1 | tkerber | (self.int_params != self.old_int_params) or |
509 | 1 | tkerber | (self.bool_params != self.old_bool_params) or |
510 | 1 | tkerber | (self.list_params != self.old_list_params) or |
511 | 1 | tkerber | (self.input_params != self.old_input_params) |
512 | 1 | tkerber | or not self.converged): |
513 | 1 | tkerber | return True |
514 | 1 | tkerber | if 'magmom' in quantities: |
515 | 1 | tkerber | return not hasattr(self, 'magnetic_moment') |
516 | 1 | tkerber | return False |
517 | 1 | tkerber | |
518 | 1 | tkerber | def get_number_of_bands(self): |
519 | 1 | tkerber | return self.nbands |
520 | 1 | tkerber | |
521 | 1 | tkerber | def get_k_point_weights(self): |
522 | 1 | tkerber | self.update(self.atoms) |
523 | 1 | tkerber | return self.read_k_point_weights() |
524 | 1 | tkerber | |
525 | 1 | tkerber | def get_number_of_spins(self): |
526 | 1 | tkerber | return 1 + int(self.spinpol) |
527 | 1 | tkerber | |
528 | 1 | tkerber | def get_eigenvalues(self, kpt=0, spin=0): |
529 | 1 | tkerber | self.update(self.atoms) |
530 | 1 | tkerber | return self.read_eigenvalues(kpt, spin) |
531 | 1 | tkerber | |
532 | 1 | tkerber | def get_fermi_level(self): |
533 | 1 | tkerber | return self.fermi |
534 | 1 | tkerber | |
535 | 1 | tkerber | def get_number_of_grid_points(self): |
536 | 1 | tkerber | raise NotImplementedError |
537 | 1 | tkerber | |
538 | 1 | tkerber | def get_pseudo_density(self): |
539 | 1 | tkerber | raise NotImplementedError |
540 | 1 | tkerber | |
541 | 1 | tkerber | def get_pseudo_wavefunction(self, n=0, k=0, s=0, pad=True): |
542 | 1 | tkerber | raise NotImplementedError |
543 | 1 | tkerber | |
544 | 1 | tkerber | def get_bz_k_points(self): |
545 | 1 | tkerber | raise NotImplementedError |
546 | 1 | tkerber | |
547 | 1 | tkerber | def get_ibz_kpoints(self): |
548 | 1 | tkerber | self.update(self.atoms) |
549 | 1 | tkerber | return self.read_ibz_kpoints() |
550 | 1 | tkerber | |
551 | 1 | tkerber | def get_spin_polarized(self): |
552 | 1 | tkerber | if not hasattr(self, 'spinpol'): |
553 | 1 | tkerber | self.spinpol = self.atoms.get_initial_magnetic_moments().any() |
554 | 1 | tkerber | return self.spinpol |
555 | 1 | tkerber | |
556 | 1 | tkerber | def get_magnetic_moment(self, atoms): |
557 | 1 | tkerber | self.update(atoms)
|
558 | 1 | tkerber | return self.magnetic_moment |
559 | 1 | tkerber | |
560 | 1 | tkerber | def get_magnetic_moments(self, atoms): |
561 | 1 | tkerber | if self.int_params['lorbit']>=10 or self.list_params['rwigs']: |
562 | 1 | tkerber | self.update(atoms)
|
563 | 1 | tkerber | return self.magnetic_moments |
564 | 1 | tkerber | else:
|
565 | 1 | tkerber | raise RuntimeError( |
566 | 1 | tkerber | "The combination %s for lorbit with %s for rwigs not supported to calculate magnetic moments" % (p['lorbit'], p['rwigs'])) |
567 | 1 | tkerber | |
568 | 1 | tkerber | def get_dipole_moment(self, atoms): |
569 | 1 | tkerber | """Returns total dipole moment of the system."""
|
570 | 1 | tkerber | self.update(atoms)
|
571 | 1 | tkerber | return self.dipole |
572 | 1 | tkerber | |
573 | 1 | tkerber | def get_xc_functional(self): |
574 | 1 | tkerber | return self.input_params['xc'] |
575 | 1 | tkerber | |
576 | 1 | tkerber | def write_incar(self, atoms, **kwargs): |
577 | 1 | tkerber | """Writes the INCAR file."""
|
578 | 1 | tkerber | incar = open('INCAR', 'w') |
579 | 1 | tkerber | incar.write('INCAR created by Atomic Simulation Environment\n')
|
580 | 1 | tkerber | for key, val in self.float_params.items(): |
581 | 1 | tkerber | if val is not None: |
582 | 1 | tkerber | incar.write(' %s = %5.6f\n' % (key.upper(), val))
|
583 | 1 | tkerber | for key, val in self.exp_params.items(): |
584 | 1 | tkerber | if val is not None: |
585 | 1 | tkerber | incar.write(' %s = %5.2e\n' % (key.upper(), val))
|
586 | 1 | tkerber | for key, val in self.string_params.items(): |
587 | 1 | tkerber | if val is not None: |
588 | 1 | tkerber | incar.write(' %s = %s\n' % (key.upper(), val))
|
589 | 1 | tkerber | for key, val in self.int_params.items(): |
590 | 1 | tkerber | if val is not None: |
591 | 1 | tkerber | incar.write(' %s = %d\n' % (key.upper(), val))
|
592 | 1 | tkerber | for key, val in self.list_params.items(): |
593 | 1 | tkerber | if val is not None: |
594 | 1 | tkerber | incar.write(' %s = ' % key.upper())
|
595 | 1 | tkerber | if key in ('dipol', 'eint', 'ferwe', 'ropt', 'rwigs'): |
596 | 1 | tkerber | [incar.write('%.4f ' % x) for x in val] |
597 | 1 | tkerber | elif key in ('iband', 'kpuse'): |
598 | 1 | tkerber | [incar.write('%i ' % x) for x in val] |
599 | 1 | tkerber | elif key == 'magmom': |
600 | 1 | tkerber | list = [[1, val[0]]] |
601 | 1 | tkerber | for n in range(1, len(val)): |
602 | 1 | tkerber | if val[n] == val[n-1]: |
603 | 1 | tkerber | list[-1][0] += 1 |
604 | 1 | tkerber | else:
|
605 | 1 | tkerber | list.append([1, val[n]]) |
606 | 1 | tkerber | [incar.write('%i*%.4f ' % (mom[0], mom[1])) for mom in list] |
607 | 1 | tkerber | incar.write('\n')
|
608 | 1 | tkerber | for key, val in self.bool_params.items(): |
609 | 1 | tkerber | if val is not None: |
610 | 1 | tkerber | incar.write(' %s = ' % key.upper())
|
611 | 1 | tkerber | if val:
|
612 | 1 | tkerber | incar.write('.TRUE.\n')
|
613 | 1 | tkerber | else:
|
614 | 1 | tkerber | incar.write('.FALSE.\n')
|
615 | 1 | tkerber | for key, val in self.special_params.items(): |
616 | 1 | tkerber | if val is not None: |
617 | 1 | tkerber | incar.write(' %s = ' % key.upper())
|
618 | 1 | tkerber | if key is 'lreal': |
619 | 1 | tkerber | if type(val)==type('str'): |
620 | 1 | tkerber | incar.write(val+'\n')
|
621 | 1 | tkerber | elif type(val)==type(True): |
622 | 1 | tkerber | if val:
|
623 | 1 | tkerber | incar.write('.TRUE.\n')
|
624 | 1 | tkerber | else:
|
625 | 1 | tkerber | incar.write('.FALSE.\n')
|
626 | 1 | tkerber | if self.spinpol and not self.int_params['ispin']: |
627 | 1 | tkerber | incar.write(' ispin = 2\n'.upper())
|
628 | 1 | tkerber | # Write out initial magnetic moments
|
629 | 1 | tkerber | magmom = atoms.get_initial_magnetic_moments()[self.sort]
|
630 | 1 | tkerber | list = [[1, magmom[0]]] |
631 | 1 | tkerber | for n in range(1, len(magmom)): |
632 | 1 | tkerber | if magmom[n] == magmom[n-1]: |
633 | 1 | tkerber | list[-1][0] += 1 |
634 | 1 | tkerber | else:
|
635 | 1 | tkerber | list.append([1, magmom[n]]) |
636 | 1 | tkerber | incar.write(' magmom = '.upper())
|
637 | 1 | tkerber | [incar.write('%i*%.4f ' % (mom[0], mom[1])) for mom in list] |
638 | 1 | tkerber | incar.write('\n')
|
639 | 1 | tkerber | incar.close() |
640 | 1 | tkerber | |
641 | 1 | tkerber | def write_kpoints(self, **kwargs): |
642 | 1 | tkerber | """Writes the KPOINTS file."""
|
643 | 1 | tkerber | p = self.input_params
|
644 | 1 | tkerber | kpoints = open('KPOINTS', 'w') |
645 | 1 | tkerber | kpoints.write('KPOINTS created by Atomic Simulation Environemnt\n')
|
646 | 1 | tkerber | shape=np.array(p['kpts']).shape
|
647 | 1 | tkerber | if len(shape)==1: |
648 | 1 | tkerber | kpoints.write('0\n')
|
649 | 1 | tkerber | if p['gamma']: |
650 | 1 | tkerber | kpoints.write('Gamma\n')
|
651 | 1 | tkerber | else:
|
652 | 1 | tkerber | kpoints.write('Monkhorst-Pack\n')
|
653 | 1 | tkerber | [kpoints.write('%i ' % kpt) for kpt in p['kpts']] |
654 | 1 | tkerber | kpoints.write('\n0 0 0')
|
655 | 1 | tkerber | elif len(shape)==2: |
656 | 1 | tkerber | kpoints.write('%i \n' % (len(p['kpts']))) |
657 | 1 | tkerber | kpoints.write('Cartesian\n')
|
658 | 1 | tkerber | for n in range(len(p['kpts'])): |
659 | 1 | tkerber | [kpoints.write('%f ' % kpt) for kpt in p['kpts'][n]] |
660 | 1 | tkerber | if shape[1]==4: |
661 | 1 | tkerber | kpoints.write('\n')
|
662 | 1 | tkerber | elif shape[1]==3: |
663 | 1 | tkerber | kpoints.write('1.0 \n')
|
664 | 1 | tkerber | kpoints.close() |
665 | 1 | tkerber | |
666 | 1 | tkerber | def write_potcar(self,suffix = ""): |
667 | 1 | tkerber | """Writes the POTCAR file."""
|
668 | 1 | tkerber | import tempfile |
669 | 1 | tkerber | potfile = open('POTCAR'+suffix,'w') |
670 | 1 | tkerber | for filename in self.ppp_list: |
671 | 1 | tkerber | if filename.endswith('R'): |
672 | 1 | tkerber | for line in open(filename, 'r'): |
673 | 1 | tkerber | potfile.write(line) |
674 | 1 | tkerber | elif filename.endswith('.Z'): |
675 | 1 | tkerber | file_tmp = tempfile.NamedTemporaryFile() |
676 | 1 | tkerber | os.system('gunzip -c %s > %s' % (filename, file_tmp.name))
|
677 | 1 | tkerber | for line in file_tmp.readlines(): |
678 | 1 | tkerber | potfile.write(line) |
679 | 1 | tkerber | file_tmp.close() |
680 | 1 | tkerber | potfile.close() |
681 | 1 | tkerber | |
682 | 1 | tkerber | def write_sort_file(self): |
683 | 1 | tkerber | """Writes a sortings file.
|
684 | 1 | tkerber |
|
685 | 1 | tkerber | This file contains information about how the atoms are sorted in
|
686 | 1 | tkerber | the first column and how they should be resorted in the second
|
687 | 1 | tkerber | column. It is used for restart purposes to get sorting right
|
688 | 1 | tkerber | when reading in an old calculation to ASE."""
|
689 | 1 | tkerber | |
690 | 1 | tkerber | file = open('ase-sort.dat', 'w') |
691 | 1 | tkerber | for n in range(len(self.sort)): |
692 | 1 | tkerber | file.write('%5i %5i \n' % (self.sort[n], self.resort[n])) |
693 | 1 | tkerber | |
694 | 1 | tkerber | # Methods for reading information from OUTCAR files:
|
695 | 1 | tkerber | def read_energy(self, all=None): |
696 | 1 | tkerber | [energy_free, energy_zero]=[0, 0] |
697 | 1 | tkerber | if all: |
698 | 1 | tkerber | energy_free = [] |
699 | 1 | tkerber | energy_zero = [] |
700 | 1 | tkerber | for line in open('OUTCAR', 'r'): |
701 | 1 | tkerber | # Free energy
|
702 | 1 | tkerber | if line.startswith(' free energy toten'): |
703 | 1 | tkerber | if all: |
704 | 1 | tkerber | energy_free.append(float(line.split()[-2])) |
705 | 1 | tkerber | else:
|
706 | 1 | tkerber | energy_free = float(line.split()[-2]) |
707 | 1 | tkerber | # Extrapolated zero point energy
|
708 | 1 | tkerber | if line.startswith(' energy without entropy'): |
709 | 1 | tkerber | if all: |
710 | 1 | tkerber | energy_zero.append(float(line.split()[-1])) |
711 | 1 | tkerber | else:
|
712 | 1 | tkerber | energy_zero = float(line.split()[-1]) |
713 | 1 | tkerber | return [energy_free, energy_zero]
|
714 | 1 | tkerber | |
715 | 1 | tkerber | def read_forces(self, atoms, all=False): |
716 | 1 | tkerber | """Method that reads forces from OUTCAR file.
|
717 | 1 | tkerber |
|
718 | 1 | tkerber | If 'all' is switched on, the forces for all ionic steps
|
719 | 1 | tkerber | in the OUTCAR file be returned, in other case only the
|
720 | 1 | tkerber | forces for the last ionic configuration is returned."""
|
721 | 1 | tkerber | |
722 | 1 | tkerber | file = open('OUTCAR','r') |
723 | 1 | tkerber | lines = file.readlines()
|
724 | 1 | tkerber | file.close()
|
725 | 1 | tkerber | n=0
|
726 | 1 | tkerber | if all: |
727 | 1 | tkerber | all_forces = [] |
728 | 1 | tkerber | for line in lines: |
729 | 1 | tkerber | if line.rfind('TOTAL-FORCE') > -1: |
730 | 1 | tkerber | forces=[] |
731 | 1 | tkerber | for i in range(len(atoms)): |
732 | 1 | tkerber | forces.append(np.array([float(f) for f in lines[n+2+i].split()[3:6]])) |
733 | 1 | tkerber | if all: |
734 | 1 | tkerber | all_forces.append(np.array(forces)[self.resort])
|
735 | 1 | tkerber | n+=1
|
736 | 1 | tkerber | if all: |
737 | 1 | tkerber | return np.array(all_forces)
|
738 | 1 | tkerber | else:
|
739 | 1 | tkerber | return np.array(forces)[self.resort] |
740 | 1 | tkerber | |
741 | 1 | tkerber | def read_fermi(self): |
742 | 1 | tkerber | """Method that reads Fermi energy from OUTCAR file"""
|
743 | 1 | tkerber | E_f=None
|
744 | 1 | tkerber | for line in open('OUTCAR', 'r'): |
745 | 1 | tkerber | if line.rfind('E-fermi') > -1: |
746 | 1 | tkerber | E_f=float(line.split()[2]) |
747 | 1 | tkerber | return E_f
|
748 | 1 | tkerber | |
749 | 1 | tkerber | def read_dipole(self): |
750 | 1 | tkerber | dipolemoment=np.zeros([1,3]) |
751 | 1 | tkerber | for line in open('OUTCAR', 'r'): |
752 | 1 | tkerber | if line.rfind('dipolmoment') > -1: |
753 | 1 | tkerber | dipolemoment=np.array([float(f) for f in line.split()[1:4]]) |
754 | 1 | tkerber | return dipolemoment
|
755 | 1 | tkerber | |
756 | 1 | tkerber | def read_magnetic_moments(self, atoms): |
757 | 1 | tkerber | magnetic_moments = np.zeros(len(atoms))
|
758 | 1 | tkerber | n = 0
|
759 | 1 | tkerber | lines = open('OUTCAR', 'r').readlines() |
760 | 1 | tkerber | for line in lines: |
761 | 1 | tkerber | if line.rfind('magnetization (x)') > -1: |
762 | 1 | tkerber | for m in range(len(atoms)): |
763 | 1 | tkerber | magnetic_moments[m] = float(lines[n + m + 4].split()[4]) |
764 | 1 | tkerber | n += 1
|
765 | 1 | tkerber | return np.array(magnetic_moments)[self.resort] |
766 | 1 | tkerber | |
767 | 1 | tkerber | def read_magnetic_moment(self): |
768 | 1 | tkerber | n=0
|
769 | 1 | tkerber | for line in open('OUTCAR','r'): |
770 | 1 | tkerber | if line.rfind('number of electron ') > -1: |
771 | 1 | tkerber | magnetic_moment=float(line.split()[-1]) |
772 | 1 | tkerber | n+=1
|
773 | 1 | tkerber | return magnetic_moment
|
774 | 1 | tkerber | |
775 | 1 | tkerber | def read_nbands(self): |
776 | 1 | tkerber | for line in open('OUTCAR', 'r'): |
777 | 1 | tkerber | if line.rfind('NBANDS') > -1: |
778 | 1 | tkerber | return int(line.split()[-1]) |
779 | 1 | tkerber | |
780 | 1 | tkerber | def read_convergence(self): |
781 | 1 | tkerber | """Method that checks whether a calculation has converged."""
|
782 | 1 | tkerber | converged = None
|
783 | 1 | tkerber | # First check electronic convergence
|
784 | 1 | tkerber | for line in open('OUTCAR', 'r'): |
785 | 1 | tkerber | if line.rfind('EDIFF ') > -1: |
786 | 1 | tkerber | ediff = float(line.split()[2]) |
787 | 1 | tkerber | if line.rfind('total energy-change')>-1: |
788 | 1 | tkerber | split = line.split(':')
|
789 | 1 | tkerber | a = float(split[1].split('(')[0]) |
790 | 1 | tkerber | b = float(split[1].split('(')[1][0:-2]) |
791 | 1 | tkerber | if [abs(a), abs(b)] < [ediff, ediff]: |
792 | 1 | tkerber | converged = True
|
793 | 1 | tkerber | else:
|
794 | 1 | tkerber | converged = False
|
795 | 1 | tkerber | continue
|
796 | 1 | tkerber | # Then if ibrion > 0, check whether ionic relaxation condition been fulfilled
|
797 | 1 | tkerber | if self.int_params['ibrion'] > 0: |
798 | 1 | tkerber | if not self.read_relaxed(): |
799 | 1 | tkerber | converged = False
|
800 | 1 | tkerber | else:
|
801 | 1 | tkerber | converged = True
|
802 | 1 | tkerber | return converged
|
803 | 1 | tkerber | |
804 | 1 | tkerber | def read_ibz_kpoints(self): |
805 | 1 | tkerber | lines = open('OUTCAR', 'r').readlines() |
806 | 1 | tkerber | ibz_kpts = [] |
807 | 1 | tkerber | n = 0
|
808 | 1 | tkerber | i = 0
|
809 | 1 | tkerber | for line in lines: |
810 | 1 | tkerber | if line.rfind('Following cartesian coordinates')>-1: |
811 | 1 | tkerber | m = n+2
|
812 | 1 | tkerber | while i==0: |
813 | 1 | tkerber | ibz_kpts.append([float(lines[m].split()[p]) for p in range(3)]) |
814 | 1 | tkerber | m += 1
|
815 | 1 | tkerber | if lines[m]==' \n': |
816 | 1 | tkerber | i = 1
|
817 | 1 | tkerber | if i == 1: |
818 | 1 | tkerber | continue
|
819 | 1 | tkerber | n += 1
|
820 | 1 | tkerber | ibz_kpts = np.array(ibz_kpts) |
821 | 1 | tkerber | return np.array(ibz_kpts)
|
822 | 1 | tkerber | |
823 | 1 | tkerber | def read_k_point_weights(self): |
824 | 1 | tkerber | file = open('IBZKPT') |
825 | 1 | tkerber | lines = file.readlines()
|
826 | 1 | tkerber | file.close()
|
827 | 1 | tkerber | kpt_weights = [] |
828 | 1 | tkerber | for n in range(3, len(lines)): |
829 | 1 | tkerber | kpt_weights.append(float(lines[n].split()[3])) |
830 | 1 | tkerber | kpt_weights = np.array(kpt_weights) |
831 | 1 | tkerber | kpt_weights /= np.sum(kpt_weights) |
832 | 1 | tkerber | return kpt_weights
|
833 | 1 | tkerber | |
834 | 1 | tkerber | def read_eigenvalues(self, kpt=0, spin=0): |
835 | 1 | tkerber | file = open('EIGENVAL', 'r') |
836 | 1 | tkerber | lines = file.readlines()
|
837 | 1 | tkerber | file.close()
|
838 | 1 | tkerber | eigs = [] |
839 | 1 | tkerber | for n in range(8+kpt*(self.nbands+2), 8+kpt*(self.nbands+2)+self.nbands): |
840 | 1 | tkerber | eigs.append(float(lines[n].split()[spin+1])) |
841 | 1 | tkerber | return np.array(eigs)
|
842 | 1 | tkerber | |
843 | 1 | tkerber | def read_relaxed(self): |
844 | 1 | tkerber | for line in open('OUTCAR', 'r'): |
845 | 1 | tkerber | if line.rfind('reached required accuracy') > -1: |
846 | 1 | tkerber | return True |
847 | 1 | tkerber | return False |
848 | 1 | tkerber | |
849 | 1 | tkerber | # The below functions are used to restart a calculation and are under early constructions
|
850 | 1 | tkerber | |
851 | 1 | tkerber | def read_incar(self, filename='INCAR'): |
852 | 1 | tkerber | """Method that imports settings from INCAR file."""
|
853 | 1 | tkerber | |
854 | 1 | tkerber | self.spinpol = False |
855 | 1 | tkerber | file=open(filename, 'r') |
856 | 1 | tkerber | file.readline()
|
857 | 1 | tkerber | lines=file.readlines()
|
858 | 1 | tkerber | for line in lines: |
859 | 1 | tkerber | try:
|
860 | 1 | tkerber | data = line.split() |
861 | 1 | tkerber | # Skip empty and commented lines.
|
862 | 1 | tkerber | if len(data) == 0: |
863 | 1 | tkerber | continue
|
864 | 1 | tkerber | elif data[0][0] in ['#', '!']: |
865 | 1 | tkerber | continue
|
866 | 1 | tkerber | key = data[0].lower()
|
867 | 1 | tkerber | if key in float_keys: |
868 | 1 | tkerber | self.float_params[key] = float(data[2]) |
869 | 1 | tkerber | elif key in exp_keys: |
870 | 1 | tkerber | self.exp_params[key] = float(data[2]) |
871 | 1 | tkerber | elif key in string_keys: |
872 | 1 | tkerber | self.string_params[key] = str(data[2]) |
873 | 1 | tkerber | elif key in int_keys: |
874 | 1 | tkerber | if key == 'ispin': |
875 | 1 | tkerber | if int(data[2]) == 2: |
876 | 1 | tkerber | self.spinpol = True |
877 | 1 | tkerber | else:
|
878 | 1 | tkerber | self.int_params[key] = int(data[2]) |
879 | 1 | tkerber | elif key in bool_keys: |
880 | 1 | tkerber | if 'true' in data[2].lower(): |
881 | 1 | tkerber | self.bool_params[key] = True |
882 | 1 | tkerber | elif 'false' in data[2].lower(): |
883 | 1 | tkerber | self.bool_params[key] = False |
884 | 1 | tkerber | elif key in list_keys: |
885 | 1 | tkerber | list = [] |
886 | 1 | tkerber | if key in ('dipol', 'eint', 'ferwe', 'ropt', 'rwigs'): |
887 | 1 | tkerber | for a in data[2:]: |
888 | 1 | tkerber | list.append(float(a)) |
889 | 1 | tkerber | elif key in ('iband', 'kpuse'): |
890 | 1 | tkerber | for a in data[2:]: |
891 | 1 | tkerber | list.append(int(a)) |
892 | 1 | tkerber | self.list_params[key] = list |
893 | 1 | tkerber | if key == 'magmom': |
894 | 1 | tkerber | done = False
|
895 | 1 | tkerber | for a in data[2:]: |
896 | 1 | tkerber | if '!' in a or done: |
897 | 1 | tkerber | done = True
|
898 | 1 | tkerber | elif '*' in a: |
899 | 1 | tkerber | a = a.split('*')
|
900 | 1 | tkerber | for b in range(int(a[0])): |
901 | 1 | tkerber | list.append(float(a[1])) |
902 | 1 | tkerber | else:
|
903 | 1 | tkerber | list.append(float(a)) |
904 | 1 | tkerber | list = np.array(list)
|
905 | 1 | tkerber | self.atoms.set_initial_magnetic_moments(list[self.resort]) |
906 | 1 | tkerber | except KeyError: |
907 | 1 | tkerber | raise IOError('Keyword "%s" in INCAR is not known by calculator.' % key) |
908 | 1 | tkerber | except IndexError: |
909 | 1 | tkerber | raise IOError('Value missing for keyword "%s".' % key) |
910 | 1 | tkerber | |
911 | 1 | tkerber | def read_outcar(self): |
912 | 1 | tkerber | # Spin polarized calculation?
|
913 | 1 | tkerber | file = open('OUTCAR', 'r') |
914 | 1 | tkerber | lines = file.readlines()
|
915 | 1 | tkerber | file.close()
|
916 | 1 | tkerber | for line in lines: |
917 | 1 | tkerber | if line.rfind('ISPIN') > -1: |
918 | 1 | tkerber | if int(line.split()[2])==2: |
919 | 1 | tkerber | self.spinpol = True |
920 | 1 | tkerber | else:
|
921 | 1 | tkerber | self.spinpol = None |
922 | 1 | tkerber | self.energy_free, self.energy_zero = self.read_energy() |
923 | 1 | tkerber | self.forces = self.read_forces(self.atoms) |
924 | 1 | tkerber | self.dipole = self.read_dipole() |
925 | 1 | tkerber | self.fermi = self.read_fermi() |
926 | 1 | tkerber | self.stress = self.read_stress() |
927 | 1 | tkerber | self.nbands = self.read_nbands() |
928 | 1 | tkerber | p=self.int_params
|
929 | 1 | tkerber | q=self.list_params
|
930 | 1 | tkerber | if self.spinpol: |
931 | 1 | tkerber | self.magnetic_moment = self.read_magnetic_moment() |
932 | 1 | tkerber | if p['lorbit']>=10 or (p['lorbit']!=None and q['rwigs']): |
933 | 1 | tkerber | self.magnetic_moments = self.read_magnetic_moments(self.atoms) |
934 | 1 | tkerber | self.set(nbands=self.nbands) |
935 | 1 | tkerber | |
936 | 1 | tkerber | def read_kpoints(self, filename='KPOINTS'): |
937 | 1 | tkerber | file = open(filename, 'r') |
938 | 1 | tkerber | lines = file.readlines()
|
939 | 1 | tkerber | file.close()
|
940 | 1 | tkerber | type = lines[2].split()[0].lower()[0] |
941 | 1 | tkerber | if type in ['g', 'm']: |
942 | 1 | tkerber | if type=='g': |
943 | 1 | tkerber | self.set(gamma=True) |
944 | 1 | tkerber | kpts = np.array([int(lines[3].split()[i]) for i in range(3)]) |
945 | 1 | tkerber | self.set(kpts=kpts)
|
946 | 1 | tkerber | elif type in ['c', 'k']: |
947 | 1 | tkerber | raise NotImplementedError('Only Monkhorst-Pack and gamma centered grid supported for restart.') |
948 | 1 | tkerber | else:
|
949 | 1 | tkerber | raise NotImplementedError('Only Monkhorst-Pack and gamma centered grid supported for restart.') |
950 | 1 | tkerber | |
951 | 1 | tkerber | def read_potcar(self): |
952 | 1 | tkerber | """ Method that reads the Exchange Correlation functional from POTCAR file.
|
953 | 1 | tkerber | """
|
954 | 1 | tkerber | file = open('POTCAR', 'r') |
955 | 1 | tkerber | lines = file.readlines()
|
956 | 1 | tkerber | file.close()
|
957 | 1 | tkerber | |
958 | 1 | tkerber | # Search for key 'LEXCH' in POTCAR
|
959 | 1 | tkerber | xc_flag = None
|
960 | 1 | tkerber | for line in lines: |
961 | 1 | tkerber | key = line.split()[0].upper()
|
962 | 1 | tkerber | if key == 'LEXCH': |
963 | 1 | tkerber | xc_flag = line.split()[-1].upper()
|
964 | 1 | tkerber | break
|
965 | 1 | tkerber | |
966 | 1 | tkerber | if xc_flag is None: |
967 | 1 | tkerber | raise ValueError('LEXCH flag not found in POTCAR file.') |
968 | 1 | tkerber | |
969 | 1 | tkerber | # Values of parameter LEXCH and corresponding XC-functional
|
970 | 1 | tkerber | xc_dict = {'PE':'PBE', '91':'PW91', 'CA':'LDA'} |
971 | 1 | tkerber | |
972 | 1 | tkerber | if xc_flag not in xc_dict.keys(): |
973 | 1 | tkerber | raise ValueError( |
974 | 1 | tkerber | 'Unknown xc-functional flag found in POTCAR, LEXCH=%s' % xc_flag)
|
975 | 1 | tkerber | |
976 | 1 | tkerber | self.input_params['xc'] = xc_dict[xc_flag] |
977 | 1 | tkerber | |
978 | 1 | tkerber | |
979 | 1 | tkerber | class VaspChargeDensity(object): |
980 | 1 | tkerber | """Class for representing VASP charge density"""
|
981 | 1 | tkerber | |
982 | 1 | tkerber | def __init__(self, filename='CHG'): |
983 | 1 | tkerber | # Instance variables
|
984 | 1 | tkerber | self.atoms = [] # List of Atoms objects |
985 | 1 | tkerber | self.chg = [] # Charge density |
986 | 1 | tkerber | self.chgdiff = [] # Charge density difference, if spin polarized |
987 | 1 | tkerber | self.aug = '' # Augmentation charges, not parsed just a big string |
988 | 1 | tkerber | self.augdiff = '' # Augmentation charge differece, is spin polarized |
989 | 1 | tkerber | |
990 | 1 | tkerber | # Note that the augmentation charge is not a list, since they
|
991 | 1 | tkerber | # are needed only for CHGCAR files which store only a single
|
992 | 1 | tkerber | # image.
|
993 | 1 | tkerber | if filename != None: |
994 | 1 | tkerber | self.read(filename)
|
995 | 1 | tkerber | |
996 | 1 | tkerber | def is_spin_polarized(self): |
997 | 1 | tkerber | if len(self.chgdiff) > 0: |
998 | 1 | tkerber | return True |
999 | 1 | tkerber | return False |
1000 | 1 | tkerber | |
1001 | 1 | tkerber | def _read_chg(self, fobj, chg, volume): |
1002 | 1 | tkerber | """Read charge from file object
|
1003 | 1 | tkerber |
|
1004 | 1 | tkerber | Utility method for reading the actual charge density (or
|
1005 | 1 | tkerber | charge density difference) from a file object. On input, the
|
1006 | 1 | tkerber | file object must be at the beginning of the charge block, on
|
1007 | 1 | tkerber | output the file position will be left at the end of the
|
1008 | 1 | tkerber | block. The chg array must be of the correct dimensions.
|
1009 | 1 | tkerber |
|
1010 | 1 | tkerber | """
|
1011 | 1 | tkerber | # VASP writes charge density as
|
1012 | 1 | tkerber | # WRITE(IU,FORM) (((C(NX,NY,NZ),NX=1,NGXC),NY=1,NGYZ),NZ=1,NGZC)
|
1013 | 1 | tkerber | # Fortran nested implied do loops; innermost index fastest
|
1014 | 1 | tkerber | # First, just read it in
|
1015 | 1 | tkerber | for zz in range(chg.shape[2]): |
1016 | 1 | tkerber | for yy in range(chg.shape[1]): |
1017 | 1 | tkerber | chg[:, yy, zz] = np.fromfile(fobj, count = chg.shape[0],
|
1018 | 1 | tkerber | sep=' ')
|
1019 | 1 | tkerber | chg /= volume |
1020 | 1 | tkerber | |
1021 | 1 | tkerber | def read(self, filename='CHG'): |
1022 | 1 | tkerber | """Read CHG or CHGCAR file.
|
1023 | 1 | tkerber |
|
1024 | 1 | tkerber | If CHG contains charge density from multiple steps all the
|
1025 | 1 | tkerber | steps are read and stored in the object. By default VASP
|
1026 | 1 | tkerber | writes out the charge density every 10 steps.
|
1027 | 1 | tkerber |
|
1028 | 1 | tkerber | chgdiff is the difference between the spin up charge density
|
1029 | 1 | tkerber | and the spin down charge density and is thus only read for a
|
1030 | 1 | tkerber | spin-polarized calculation.
|
1031 | 1 | tkerber |
|
1032 | 1 | tkerber | aug is the PAW augmentation charges found in CHGCAR. These are
|
1033 | 1 | tkerber | not parsed, they are just stored as a string so that they can
|
1034 | 1 | tkerber | be written again to a CHGCAR format file.
|
1035 | 1 | tkerber |
|
1036 | 1 | tkerber | """
|
1037 | 1 | tkerber | import ase.io.vasp as aiv |
1038 | 1 | tkerber | f = open(filename)
|
1039 | 1 | tkerber | self.atoms = []
|
1040 | 1 | tkerber | self.chg = []
|
1041 | 1 | tkerber | self.chgdiff = []
|
1042 | 1 | tkerber | self.aug = '' |
1043 | 1 | tkerber | self.augdiff = '' |
1044 | 1 | tkerber | while True: |
1045 | 1 | tkerber | try:
|
1046 | 1 | tkerber | atoms = aiv.read_vasp(f) |
1047 | 1 | tkerber | except ValueError, e: |
1048 | 1 | tkerber | # Probably an empty line, or we tried to read the
|
1049 | 1 | tkerber | # augmentation occupancies in CHGCAR
|
1050 | 1 | tkerber | break
|
1051 | 1 | tkerber | f.readline() |
1052 | 1 | tkerber | ngr = f.readline().split() |
1053 | 1 | tkerber | ng = (int(ngr[0]), int(ngr[1]), int(ngr[2])) |
1054 | 1 | tkerber | chg = np.empty(ng) |
1055 | 1 | tkerber | self._read_chg(f, chg, atoms.get_volume())
|
1056 | 1 | tkerber | self.chg.append(chg)
|
1057 | 1 | tkerber | self.atoms.append(atoms)
|
1058 | 1 | tkerber | # Check if the file has a spin-polarized charge density part, and
|
1059 | 1 | tkerber | # if so, read it in.
|
1060 | 1 | tkerber | fl = f.tell() |
1061 | 1 | tkerber | # First check if the file has an augmentation charge part (CHGCAR file.)
|
1062 | 1 | tkerber | line1 = f.readline() |
1063 | 1 | tkerber | if line1=='': |
1064 | 1 | tkerber | break
|
1065 | 1 | tkerber | elif line1.find('augmentation') != -1: |
1066 | 1 | tkerber | augs = [line1] |
1067 | 1 | tkerber | while True: |
1068 | 1 | tkerber | line2 = f.readline() |
1069 | 1 | tkerber | if line2.split() == ngr:
|
1070 | 1 | tkerber | self.aug = ''.join(augs) |
1071 | 1 | tkerber | augs = [] |
1072 | 1 | tkerber | chgdiff = np.empty(ng) |
1073 | 1 | tkerber | self._read_chg(f, chgdiff, atoms.get_volume())
|
1074 | 1 | tkerber | self.chgdiff.append(chgdiff)
|
1075 | 1 | tkerber | elif line2 == '': |
1076 | 1 | tkerber | break
|
1077 | 1 | tkerber | else:
|
1078 | 1 | tkerber | augs.append(line2) |
1079 | 1 | tkerber | if len(self.aug) == 0: |
1080 | 1 | tkerber | self.aug = ''.join(augs) |
1081 | 1 | tkerber | augs = [] |
1082 | 1 | tkerber | else:
|
1083 | 1 | tkerber | self.augdiff = ''.join(augs) |
1084 | 1 | tkerber | augs = [] |
1085 | 1 | tkerber | elif line1.split() == ngr:
|
1086 | 1 | tkerber | chgdiff = np.empty(ng) |
1087 | 1 | tkerber | self._read_chg(f, chgdiff, atoms.get_volume())
|
1088 | 1 | tkerber | self.chgdiff.append(chgdiff)
|
1089 | 1 | tkerber | else:
|
1090 | 1 | tkerber | f.seek(fl) |
1091 | 1 | tkerber | f.close() |
1092 | 1 | tkerber | |
1093 | 1 | tkerber | def _write_chg(self, fobj, chg, volume, format='chg'): |
1094 | 1 | tkerber | """Write charge density
|
1095 | 1 | tkerber |
|
1096 | 1 | tkerber | Utility function similar to _read_chg but for writing.
|
1097 | 1 | tkerber |
|
1098 | 1 | tkerber | """
|
1099 | 1 | tkerber | # Make a 1D copy of chg, must take transpose to get ordering right
|
1100 | 1 | tkerber | chgtmp=chg.T.ravel() |
1101 | 1 | tkerber | # Multiply by volume
|
1102 | 1 | tkerber | chgtmp=chgtmp*volume |
1103 | 1 | tkerber | # Must be a tuple to pass to string conversion
|
1104 | 1 | tkerber | chgtmp=tuple(chgtmp)
|
1105 | 1 | tkerber | # CHG format - 10 columns
|
1106 | 1 | tkerber | if format.lower() == 'chg': |
1107 | 1 | tkerber | # Write all but the last row
|
1108 | 1 | tkerber | for ii in range((len(chgtmp)-1)/10): |
1109 | 1 | tkerber | fobj.write(' %#11.5G %#11.5G %#11.5G %#11.5G %#11.5G\
|
1110 | 1 | tkerber | %#11.5G %#11.5G %#11.5G %#11.5G %#11.5G\n' % chgtmp[ii*10:(ii+1)*10] |
1111 | 1 | tkerber | ) |
1112 | 1 | tkerber | # If the last row contains 10 values then write them without a newline
|
1113 | 1 | tkerber | if len(chgtmp)%10==0: |
1114 | 1 | tkerber | fobj.write(' %#11.5G %#11.5G %#11.5G %#11.5G %#11.5G\
|
1115 | 1 | tkerber | %#11.5G %#11.5G %#11.5G %#11.5G %#11.5G' % chgtmp[len(chgtmp)-10:len(chgtmp)]) |
1116 | 1 | tkerber | # Otherwise write fewer columns without a newline
|
1117 | 1 | tkerber | else:
|
1118 | 1 | tkerber | for ii in range(len(chgtmp)%10): |
1119 | 1 | tkerber | fobj.write((' %#11.5G') % chgtmp[len(chgtmp)-len(chgtmp)%10+ii]) |
1120 | 1 | tkerber | # Other formats - 5 columns
|
1121 | 1 | tkerber | else:
|
1122 | 1 | tkerber | # Write all but the last row
|
1123 | 1 | tkerber | for ii in range((len(chgtmp)-1)/5): |
1124 | 1 | tkerber | fobj.write(' %17.10E %17.10E %17.10E %17.10E %17.10E\n' % chgtmp[ii*5:(ii+1)*5]) |
1125 | 1 | tkerber | # If the last row contains 5 values then write them without a newline
|
1126 | 1 | tkerber | if len(chgtmp)%5==0: |
1127 | 1 | tkerber | fobj.write(' %17.10E %17.10E %17.10E %17.10E %17.10E' % chgtmp[len(chgtmp)-5:len(chgtmp)]) |
1128 | 1 | tkerber | # Otherwise write fewer columns without a newline
|
1129 | 1 | tkerber | else:
|
1130 | 1 | tkerber | for ii in range(len(chgtmp)%5): |
1131 | 1 | tkerber | fobj.write((' %17.10E') % chgtmp[len(chgtmp)-len(chgtmp)%5+ii]) |
1132 | 1 | tkerber | # Write a newline whatever format it is
|
1133 | 1 | tkerber | fobj.write('\n')
|
1134 | 1 | tkerber | # Clean up
|
1135 | 1 | tkerber | del chgtmp
|
1136 | 1 | tkerber | |
1137 | 1 | tkerber | def write(self, filename='CHG', format=None): |
1138 | 1 | tkerber | """Write VASP charge density in CHG format.
|
1139 | 1 | tkerber |
|
1140 | 1 | tkerber | filename: str
|
1141 | 1 | tkerber | Name of file to write to.
|
1142 | 1 | tkerber | format: str
|
1143 | 1 | tkerber | String specifying whether to write in CHGCAR or CHG
|
1144 | 1 | tkerber | format.
|
1145 | 1 | tkerber |
|
1146 | 1 | tkerber | """
|
1147 | 1 | tkerber | import ase.io.vasp as aiv |
1148 | 1 | tkerber | if format == None: |
1149 | 1 | tkerber | if filename.lower().find('chgcar') != -1: |
1150 | 1 | tkerber | format = 'chgcar'
|
1151 | 1 | tkerber | elif filename.lower().find('chg') != -1: |
1152 | 1 | tkerber | format = 'chg'
|
1153 | 1 | tkerber | elif len(self.chg) == 1: |
1154 | 1 | tkerber | format = 'chgcar'
|
1155 | 1 | tkerber | else:
|
1156 | 1 | tkerber | format = 'chg'
|
1157 | 1 | tkerber | f = open(filename, 'w') |
1158 | 1 | tkerber | for ii, chg in enumerate(self.chg): |
1159 | 1 | tkerber | if format == 'chgcar' and ii != len(self.chg) - 1: |
1160 | 1 | tkerber | continue # Write only the last image for CHGCAR |
1161 | 1 | tkerber | aiv.write_vasp(f, self.atoms[ii], direct=True) |
1162 | 1 | tkerber | f.write('\n')
|
1163 | 1 | tkerber | for dim in chg.shape: |
1164 | 1 | tkerber | f.write(' %4i' % dim)
|
1165 | 1 | tkerber | f.write('\n')
|
1166 | 1 | tkerber | vol = self.atoms[ii].get_volume()
|
1167 | 1 | tkerber | self._write_chg(f, chg, vol, format)
|
1168 | 1 | tkerber | if format == 'chgcar': |
1169 | 1 | tkerber | f.write(self.aug)
|
1170 | 1 | tkerber | if self.is_spin_polarized(): |
1171 | 1 | tkerber | if format == 'chg': |
1172 | 1 | tkerber | f.write('\n')
|
1173 | 1 | tkerber | for dim in chg.shape: |
1174 | 1 | tkerber | f.write(' %4i' % dim)
|
1175 | 1 | tkerber | self._write_chg(f, self.chgdiff[ii], vol, format) |
1176 | 1 | tkerber | if format == 'chgcar': |
1177 | 1 | tkerber | f.write('\n')
|
1178 | 1 | tkerber | f.write(self.augdiff)
|
1179 | 1 | tkerber | if format == 'chg' and len(self.chg) > 1: |
1180 | 1 | tkerber | f.write('\n')
|
1181 | 1 | tkerber | f.close() |
1182 | 1 | tkerber | |
1183 | 1 | tkerber | |
1184 | 1 | tkerber | class VaspDos(object): |
1185 | 1 | tkerber | """Class for representing density-of-states produced by VASP
|
1186 | 1 | tkerber |
|
1187 | 1 | tkerber | The energies are in property self.energy
|
1188 | 1 | tkerber |
|
1189 | 1 | tkerber | Site-projected DOS is accesible via the self.site_dos method.
|
1190 | 1 | tkerber |
|
1191 | 1 | tkerber | Total and integrated DOS is accessible as numpy.ndarray's in the
|
1192 | 1 | tkerber | properties self.dos and self.integrated_dos. If the calculation is
|
1193 | 1 | tkerber | spin polarized, the arrays will be of shape (2, NDOS), else (1,
|
1194 | 1 | tkerber | NDOS).
|
1195 | 1 | tkerber |
|
1196 | 1 | tkerber | The self.efermi property contains the currently set Fermi
|
1197 | 1 | tkerber | level. Changing this value shifts the energies.
|
1198 | 1 | tkerber |
|
1199 | 1 | tkerber | """
|
1200 | 1 | tkerber | |
1201 | 1 | tkerber | def __init__(self, doscar='DOSCAR', efermi=0.0): |
1202 | 1 | tkerber | """Initialize"""
|
1203 | 1 | tkerber | self._efermi = 0.0 |
1204 | 1 | tkerber | self.read_doscar(doscar)
|
1205 | 1 | tkerber | self.efermi = efermi
|
1206 | 1 | tkerber | |
1207 | 1 | tkerber | def _set_efermi(self, efermi): |
1208 | 1 | tkerber | """Set the Fermi level."""
|
1209 | 1 | tkerber | ef = efermi - self._efermi
|
1210 | 1 | tkerber | self._efermi = efermi
|
1211 | 1 | tkerber | self._total_dos[0, :] = self._total_dos[0, :] - ef |
1212 | 1 | tkerber | try:
|
1213 | 1 | tkerber | self._site_dos[:, 0, :] = self._site_dos[:, 0, :] - ef |
1214 | 1 | tkerber | except IndexError: |
1215 | 1 | tkerber | pass
|
1216 | 1 | tkerber | |
1217 | 1 | tkerber | def _get_efermi(self): |
1218 | 1 | tkerber | return self._efermi |
1219 | 1 | tkerber | |
1220 | 1 | tkerber | efermi = property(_get_efermi, _set_efermi, None, "Fermi energy.") |
1221 | 1 | tkerber | |
1222 | 1 | tkerber | def _get_energy(self): |
1223 | 1 | tkerber | """Return the array with the energies."""
|
1224 | 1 | tkerber | return self._total_dos[0, :] |
1225 | 1 | tkerber | energy = property(_get_energy, None, None, "Array of energies") |
1226 | 1 | tkerber | |
1227 | 1 | tkerber | def site_dos(self, atom, orbital): |
1228 | 1 | tkerber | """Return an NDOSx1 array with dos for the chosen atom and orbital.
|
1229 | 1 | tkerber |
|
1230 | 1 | tkerber | atom: int
|
1231 | 1 | tkerber | Atom index
|
1232 | 1 | tkerber | orbital: int or str
|
1233 | 1 | tkerber | Which orbital to plot
|
1234 | 1 | tkerber |
|
1235 | 1 | tkerber | If the orbital is given as an integer:
|
1236 | 1 | tkerber | If spin-unpolarized calculation, no phase factors:
|
1237 | 1 | tkerber | s = 0, p = 1, d = 2
|
1238 | 1 | tkerber | Spin-polarized, no phase factors:
|
1239 | 1 | tkerber | s-up = 0, s-down = 1, p-up = 2, p-down = 3, d-up = 4, d-down = 5
|
1240 | 1 | tkerber | If phase factors have been calculated, orbitals are
|
1241 | 1 | tkerber | s, py, pz, px, dxy, dyz, dz2, dxz, dx2
|
1242 | 1 | tkerber | double in the above fashion if spin polarized.
|
1243 | 1 | tkerber |
|
1244 | 1 | tkerber | """
|
1245 | 1 | tkerber | # Integer indexing for orbitals starts from 1 in the _site_dos array
|
1246 | 1 | tkerber | # since the 0th column contains the energies
|
1247 | 1 | tkerber | if isinstance(orbital, int): |
1248 | 1 | tkerber | return self._site_dos[atom, orbital + 1, :] |
1249 | 1 | tkerber | n = self._site_dos.shape[1] |
1250 | 1 | tkerber | if n == 4: |
1251 | 1 | tkerber | norb = {'s':1, 'p':2, 'd':3} |
1252 | 1 | tkerber | elif n == 7: |
1253 | 1 | tkerber | norb = {'s+':1, 's-up':1, 's-':2, 's-down':2, |
1254 | 1 | tkerber | 'p+':3, 'p-up':3, 'p-':4, 'p-down':4, |
1255 | 1 | tkerber | 'd+':5, 'd-up':5, 'd-':6, 'd-down':6} |
1256 | 1 | tkerber | elif n == 10: |
1257 | 1 | tkerber | norb = {'s':1, 'py':2, 'pz':3, 'px':4, |
1258 | 1 | tkerber | 'dxy':5, 'dyz':6, 'dz2':7, 'dxz':8, |
1259 | 1 | tkerber | 'dx2':9} |
1260 | 1 | tkerber | elif n == 19: |
1261 | 1 | tkerber | norb = {'s+':1, 's-up':1, 's-':2, 's-down':2, |
1262 | 1 | tkerber | 'py+':3, 'py-up':3, 'py-':4, 'py-down':4, |
1263 | 1 | tkerber | 'pz+':5, 'pz-up':5, 'pz-':6, 'pz-down':6, |
1264 | 1 | tkerber | 'px+':7, 'px-up':7, 'px-':8, 'px-down':8, |
1265 | 1 | tkerber | 'dxy+':9, 'dxy-up':9, 'dxy-':10, 'dxy-down':10, |
1266 | 1 | tkerber | 'dyz+':11, 'dyz-up':11, 'dyz-':12, 'dyz-down':12, |
1267 | 1 | tkerber | 'dz2+':13, 'dz2-up':13, 'dz2-':14, 'dz2-down':14, |
1268 | 1 | tkerber | 'dxz+':15, 'dxz-up':15, 'dxz-':16, 'dxz-down':16, |
1269 | 1 | tkerber | 'dx2+':17, 'dx2-up':17, 'dx2-':18, 'dx2-down':18} |
1270 | 1 | tkerber | return self._site_dos[atom, norb[orbital.lower()], :] |
1271 | 1 | tkerber | |
1272 | 1 | tkerber | def _get_dos(self): |
1273 | 1 | tkerber | if self._total_dos.shape[0] == 3: |
1274 | 1 | tkerber | return self._total_dos[1, :] |
1275 | 1 | tkerber | elif self._total_dos.shape[0] == 5: |
1276 | 1 | tkerber | return self._total_dos[1:3, :] |
1277 | 1 | tkerber | dos = property(_get_dos, None, None, 'Average DOS in cell') |
1278 | 1 | tkerber | |
1279 | 1 | tkerber | def _get_integrated_dos(self): |
1280 | 1 | tkerber | if self._total_dos.shape[0] == 3: |
1281 | 1 | tkerber | return self._total_dos[2, :] |
1282 | 1 | tkerber | elif self._total_dos.shape[0] == 5: |
1283 | 1 | tkerber | return self._total_dos[3:5, :] |
1284 | 1 | tkerber | integrated_dos = property(_get_integrated_dos, None, None, |
1285 | 1 | tkerber | 'Integrated average DOS in cell')
|
1286 | 1 | tkerber | |
1287 | 1 | tkerber | def read_doscar(self, fname="DOSCAR"): |
1288 | 1 | tkerber | """Read a VASP DOSCAR file"""
|
1289 | 1 | tkerber | f = open(fname)
|
1290 | 1 | tkerber | natoms = int(f.readline().split()[0]) |
1291 | 1 | tkerber | [f.readline() for nn in range(4)] # Skip next 4 lines. |
1292 | 1 | tkerber | # First we have a block with total and total integrated DOS
|
1293 | 1 | tkerber | ndos = int(f.readline().split()[2]) |
1294 | 1 | tkerber | dos = [] |
1295 | 1 | tkerber | for nd in xrange(ndos): |
1296 | 1 | tkerber | dos.append(np.array([float(x) for x in f.readline().split()])) |
1297 | 1 | tkerber | self._total_dos = np.array(dos).T
|
1298 | 1 | tkerber | # Next we have one block per atom, if INCAR contains the stuff
|
1299 | 1 | tkerber | # necessary for generating site-projected DOS
|
1300 | 1 | tkerber | dos = [] |
1301 | 1 | tkerber | for na in xrange(natoms): |
1302 | 1 | tkerber | line = f.readline() |
1303 | 1 | tkerber | if line == '': |
1304 | 1 | tkerber | # No site-projected DOS
|
1305 | 1 | tkerber | break
|
1306 | 1 | tkerber | ndos = int(line.split()[2]) |
1307 | 1 | tkerber | line = f.readline().split() |
1308 | 1 | tkerber | cdos = np.empty((ndos, len(line)))
|
1309 | 1 | tkerber | cdos[0] = np.array(line)
|
1310 | 1 | tkerber | for nd in xrange(1, ndos): |
1311 | 1 | tkerber | line = f.readline().split() |
1312 | 1 | tkerber | cdos[nd] = np.array([float(x) for x in line]) |
1313 | 1 | tkerber | dos.append(cdos.T) |
1314 | 1 | tkerber | self._site_dos = np.array(dos)
|
1315 | 1 | tkerber | |
1316 | 1 | tkerber | |
1317 | 1 | tkerber | import pickle |
1318 | 1 | tkerber | |
1319 | 1 | tkerber | class xdat2traj: |
1320 | 1 | tkerber | def __init__(self, trajectory=None, atoms=None, poscar=None, |
1321 | 1 | tkerber | xdatcar=None, sort=None, calc=None): |
1322 | 1 | tkerber | if not poscar: |
1323 | 1 | tkerber | self.poscar = 'POSCAR' |
1324 | 1 | tkerber | else:
|
1325 | 1 | tkerber | self.poscar = poscar
|
1326 | 1 | tkerber | if not atoms: |
1327 | 1 | tkerber | self.atoms = ase.io.read(self.poscar, format='vasp') |
1328 | 1 | tkerber | else:
|
1329 | 1 | tkerber | self.atoms = atoms
|
1330 | 1 | tkerber | if not xdatcar: |
1331 | 1 | tkerber | self.xdatcar = 'XDATCAR' |
1332 | 1 | tkerber | else:
|
1333 | 1 | tkerber | self.xdatcar = xdatcar
|
1334 | 1 | tkerber | if not trajectory: |
1335 | 1 | tkerber | self.trajectory = 'out.traj' |
1336 | 1 | tkerber | else:
|
1337 | 1 | tkerber | self.trajectory = trajectory
|
1338 | 1 | tkerber | if not calc: |
1339 | 1 | tkerber | self.calc = Vasp()
|
1340 | 1 | tkerber | else:
|
1341 | 1 | tkerber | self.calc = calc
|
1342 | 1 | tkerber | if not sort: |
1343 | 1 | tkerber | if not hasattr(self.calc, 'sort'): |
1344 | 1 | tkerber | self.calc.sort = range(len(self.atoms)) |
1345 | 1 | tkerber | else:
|
1346 | 1 | tkerber | self.calc.sort = sort
|
1347 | 1 | tkerber | self.calc.resort = range(len(self.calc.sort)) |
1348 | 1 | tkerber | for n in range(len(self.calc.resort)): |
1349 | 1 | tkerber | self.calc.resort[self.calc.sort[n]] = n |
1350 | 1 | tkerber | self.out = ase.io.trajectory.PickleTrajectory(self.trajectory, mode='w') |
1351 | 1 | tkerber | self.energies = self.calc.read_energy(all=True)[1] |
1352 | 1 | tkerber | self.forces = self.calc.read_forces(self.atoms, all=True) |
1353 | 1 | tkerber | |
1354 | 1 | tkerber | def convert(self): |
1355 | 1 | tkerber | lines = open(self.xdatcar).readlines() |
1356 | 1 | tkerber | if len(lines[5].split())==0: |
1357 | 1 | tkerber | del(lines[0:6]) |
1358 | 1 | tkerber | elif len(lines[4].split())==0: |
1359 | 1 | tkerber | del(lines[0:5]) |
1360 | 1 | tkerber | step = 0
|
1361 | 1 | tkerber | iatom = 0
|
1362 | 1 | tkerber | scaled_pos = [] |
1363 | 1 | tkerber | for line in lines: |
1364 | 1 | tkerber | if iatom == len(self.atoms): |
1365 | 1 | tkerber | if step == 0: |
1366 | 1 | tkerber | self.out.write_header(self.atoms[self.calc.resort]) |
1367 | 1 | tkerber | scaled_pos = np.array(scaled_pos) |
1368 | 1 | tkerber | self.atoms.set_scaled_positions(scaled_pos)
|
1369 | 1 | tkerber | d = {'positions': self.atoms.get_positions()[self.calc.resort], |
1370 | 1 | tkerber | 'cell': self.atoms.get_cell(), |
1371 | 1 | tkerber | 'momenta': None, |
1372 | 1 | tkerber | 'energy': self.energies[step], |
1373 | 1 | tkerber | 'forces': self.forces[step], |
1374 | 1 | tkerber | 'stress': None} |
1375 | 1 | tkerber | pickle.dump(d, self.out.fd, protocol=-1) |
1376 | 1 | tkerber | scaled_pos = [] |
1377 | 1 | tkerber | iatom = 0
|
1378 | 1 | tkerber | step += 1
|
1379 | 1 | tkerber | else:
|
1380 | 1 | tkerber | |
1381 | 1 | tkerber | iatom += 1
|
1382 | 1 | tkerber | scaled_pos.append([float(line.split()[n]) for n in range(3)]) |
1383 | 1 | tkerber | |
1384 | 1 | tkerber | # Write also the last image
|
1385 | 1 | tkerber | # I'm sure there is also more clever fix...
|
1386 | 1 | tkerber | scaled_pos = np.array(scaled_pos) |
1387 | 1 | tkerber | self.atoms.set_scaled_positions(scaled_pos)
|
1388 | 1 | tkerber | d = {'positions': self.atoms.get_positions()[self.calc.resort], |
1389 | 1 | tkerber | 'cell': self.atoms.get_cell(), |
1390 | 1 | tkerber | 'momenta': None, |
1391 | 1 | tkerber | 'energy': self.energies[step], |
1392 | 1 | tkerber | 'forces': self.forces[step], |
1393 | 1 | tkerber | 'stress': None} |
1394 | 1 | tkerber | pickle.dump(d, self.out.fd, protocol=-1) |
1395 | 1 | tkerber | |
1396 | 1 | tkerber | self.out.fd.close() |