root / ase / io / vasp.py @ 13
Historique | Voir | Annoter | Télécharger (11,62 ko)
1 | 1 | tkerber | """
|
---|---|---|---|
2 | 1 | tkerber | This module contains functionality for reading and writing an ASE
|
3 | 1 | tkerber | Atoms object in VASP POSCAR format.
|
4 | 1 | tkerber |
|
5 | 1 | tkerber | """
|
6 | 1 | tkerber | |
7 | 1 | tkerber | import os |
8 | 1 | tkerber | |
9 | 1 | tkerber | def get_atomtypes(fname): |
10 | 1 | tkerber | """Given a file name, get the atomic symbols.
|
11 | 1 | tkerber |
|
12 | 1 | tkerber | The function can get this information from OUTCAR and POTCAR
|
13 | 1 | tkerber | format files. The files can also be compressed with gzip or
|
14 | 1 | tkerber | bzip2.
|
15 | 1 | tkerber |
|
16 | 1 | tkerber | """
|
17 | 1 | tkerber | atomtypes=[] |
18 | 1 | tkerber | if fname.find('.gz') != -1: |
19 | 1 | tkerber | import gzip |
20 | 1 | tkerber | f = gzip.open(fname) |
21 | 1 | tkerber | elif fname.find('.bz2') != -1: |
22 | 1 | tkerber | import bz2 |
23 | 1 | tkerber | f = bz2.BZ2File(fname) |
24 | 1 | tkerber | else:
|
25 | 1 | tkerber | f = open(fname)
|
26 | 1 | tkerber | for line in f: |
27 | 1 | tkerber | if line.find('TITEL') != -1: |
28 | 1 | tkerber | atomtypes.append(line.split()[3].split('_')[0].split('.')[0]) |
29 | 1 | tkerber | return atomtypes
|
30 | 1 | tkerber | |
31 | 1 | tkerber | def atomtypes_outpot(posfname, numsyms): |
32 | 1 | tkerber | """Try to retreive chemical symbols from OUTCAR or POTCAR
|
33 | 1 | tkerber |
|
34 | 1 | tkerber | If getting atomtypes from the first line in POSCAR/CONTCAR fails, it might
|
35 | 1 | tkerber | be possible to find the data in OUTCAR or POTCAR, if these files exist.
|
36 | 1 | tkerber |
|
37 | 1 | tkerber | posfname -- The filename of the POSCAR/CONTCAR file we're trying to read
|
38 | 1 | tkerber |
|
39 | 1 | tkerber | numsyms -- The number of symbols we must find
|
40 | 1 | tkerber |
|
41 | 1 | tkerber | """
|
42 | 1 | tkerber | import os.path as op |
43 | 1 | tkerber | import glob |
44 | 1 | tkerber | |
45 | 1 | tkerber | # First check files with exactly same name except POTCAR/OUTCAR instead
|
46 | 1 | tkerber | # of POSCAR/CONTCAR.
|
47 | 1 | tkerber | fnames = [posfname.replace('POSCAR', 'POTCAR').replace('CONTCAR', |
48 | 1 | tkerber | 'POTCAR')]
|
49 | 1 | tkerber | fnames.append(posfname.replace('POSCAR', 'OUTCAR').replace('CONTCAR', |
50 | 1 | tkerber | 'OUTCAR'))
|
51 | 1 | tkerber | # Try the same but with compressed files
|
52 | 1 | tkerber | fsc = [] |
53 | 1 | tkerber | for fn in fnames: |
54 | 1 | tkerber | fsc.append(fn + '.gz')
|
55 | 1 | tkerber | fsc.append(fn + '.bz2')
|
56 | 1 | tkerber | for f in fsc: |
57 | 1 | tkerber | fnames.append(f) |
58 | 1 | tkerber | # Finally try anything with POTCAR or OUTCAR in the name
|
59 | 1 | tkerber | vaspdir = op.dirname(posfname) |
60 | 1 | tkerber | fs = glob.glob(vaspdir + '*POTCAR*')
|
61 | 1 | tkerber | for f in fs: |
62 | 1 | tkerber | fnames.append(f) |
63 | 1 | tkerber | fs = glob.glob(vaspdir + '*OUTCAR*')
|
64 | 1 | tkerber | for f in fs: |
65 | 1 | tkerber | fnames.append(f) |
66 | 1 | tkerber | |
67 | 1 | tkerber | tried = [] |
68 | 1 | tkerber | files_in_dir = os.listdir('.')
|
69 | 1 | tkerber | for fn in fnames: |
70 | 1 | tkerber | tried.append(fn) |
71 | 1 | tkerber | if fn in files_in_dir: |
72 | 1 | tkerber | at = get_atomtypes(fn) |
73 | 1 | tkerber | if len(at) == numsyms: |
74 | 1 | tkerber | return at
|
75 | 1 | tkerber | |
76 | 1 | tkerber | raise IOError('Could not determine chemical symbols. Tried files ' |
77 | 1 | tkerber | + str(tried))
|
78 | 1 | tkerber | |
79 | 1 | tkerber | |
80 | 1 | tkerber | def read_vasp(filename='CONTCAR'): |
81 | 1 | tkerber | """Import POSCAR/CONTCAR type file.
|
82 | 1 | tkerber |
|
83 | 1 | tkerber | Reads unitcell, atom positions and constraints from the POSCAR/CONTCAR
|
84 | 1 | tkerber | file and tries to read atom types from POSCAR/CONTCAR header, if this fails
|
85 | 1 | tkerber | the atom types are read from OUTCAR or POTCAR file.
|
86 | 1 | tkerber | """
|
87 | 1 | tkerber | |
88 | 1 | tkerber | from ase import Atoms, Atom |
89 | 1 | tkerber | from ase.constraints import FixAtoms, FixScaled |
90 | 1 | tkerber | from ase.data import chemical_symbols |
91 | 1 | tkerber | import numpy as np |
92 | 1 | tkerber | |
93 | 1 | tkerber | if isinstance(filename, str): |
94 | 1 | tkerber | f = open(filename)
|
95 | 1 | tkerber | else: # Assume it's a file-like object |
96 | 1 | tkerber | f = filename |
97 | 1 | tkerber | |
98 | 1 | tkerber | # First line should contain the atom symbols , eg. "Ag Ge" in
|
99 | 1 | tkerber | # the same order
|
100 | 1 | tkerber | # as later in the file (and POTCAR for the full vasp run)
|
101 | 1 | tkerber | atomtypes = f.readline().split() |
102 | 1 | tkerber | |
103 | 1 | tkerber | lattice_constant = float(f.readline())
|
104 | 1 | tkerber | |
105 | 1 | tkerber | # Now the lattice vectors
|
106 | 1 | tkerber | a = [] |
107 | 1 | tkerber | for ii in range(3): |
108 | 1 | tkerber | s = f.readline().split() |
109 | 1 | tkerber | floatvect = float(s[0]), float(s[1]), float(s[2]) |
110 | 1 | tkerber | a.append(floatvect) |
111 | 1 | tkerber | |
112 | 1 | tkerber | basis_vectors = np.array(a) * lattice_constant |
113 | 1 | tkerber | |
114 | 1 | tkerber | # Number of atoms. Again this must be in the same order as
|
115 | 1 | tkerber | # in the first line
|
116 | 1 | tkerber | # or in the POTCAR or OUTCAR file
|
117 | 1 | tkerber | atom_symbols = [] |
118 | 1 | tkerber | numofatoms = f.readline().split() |
119 | 1 | tkerber | #vasp5.1 has an additional line which gives the atom types
|
120 | 1 | tkerber | #the following try statement skips this line
|
121 | 1 | tkerber | try:
|
122 | 1 | tkerber | int(numofatoms[0]) |
123 | 1 | tkerber | except ValueError: |
124 | 1 | tkerber | numofatoms = f.readline().split() |
125 | 1 | tkerber | |
126 | 1 | tkerber | numsyms = len(numofatoms)
|
127 | 1 | tkerber | if len(atomtypes) < numsyms: |
128 | 1 | tkerber | # First line in POSCAR/CONTCAR didn't contain enough symbols.
|
129 | 1 | tkerber | atomtypes = atomtypes_outpot(f.name, numsyms) |
130 | 1 | tkerber | else:
|
131 | 1 | tkerber | try:
|
132 | 1 | tkerber | for atype in atomtypes[:numsyms]: |
133 | 1 | tkerber | if not atype in chemical_symbols: |
134 | 1 | tkerber | raise KeyError |
135 | 1 | tkerber | except KeyError: |
136 | 1 | tkerber | atomtypes = atomtypes_outpot(f.name, numsyms) |
137 | 1 | tkerber | |
138 | 1 | tkerber | for i, num in enumerate(numofatoms): |
139 | 1 | tkerber | numofatoms[i] = int(num)
|
140 | 1 | tkerber | [atom_symbols.append(atomtypes[i]) for na in xrange(numofatoms[i])] |
141 | 1 | tkerber | |
142 | 1 | tkerber | # Check if Selective dynamics is switched on
|
143 | 1 | tkerber | sdyn = f.readline() |
144 | 1 | tkerber | selective_dynamics = sdyn[0].lower() == "s" |
145 | 1 | tkerber | |
146 | 1 | tkerber | # Check if atom coordinates are cartesian or direct
|
147 | 1 | tkerber | if selective_dynamics:
|
148 | 1 | tkerber | ac_type = f.readline() |
149 | 1 | tkerber | else:
|
150 | 1 | tkerber | ac_type = sdyn |
151 | 1 | tkerber | cartesian = ac_type[0].lower() == "c" or ac_type[0].lower() == "k" |
152 | 1 | tkerber | tot_natoms = sum(numofatoms)
|
153 | 1 | tkerber | atoms_pos = np.empty((tot_natoms, 3))
|
154 | 1 | tkerber | if selective_dynamics:
|
155 | 1 | tkerber | selective_flags = np.empty((tot_natoms, 3), dtype=bool) |
156 | 1 | tkerber | for atom in xrange(tot_natoms): |
157 | 1 | tkerber | ac = f.readline().split() |
158 | 1 | tkerber | atoms_pos[atom] = (float(ac[0]), float(ac[1]), float(ac[2])) |
159 | 1 | tkerber | if selective_dynamics:
|
160 | 1 | tkerber | curflag = [] |
161 | 1 | tkerber | for flag in ac[3:6]: |
162 | 1 | tkerber | curflag.append(flag == 'F')
|
163 | 1 | tkerber | selective_flags[atom] = curflag |
164 | 1 | tkerber | # Done with all reading
|
165 | 1 | tkerber | if type(filename) == str: |
166 | 1 | tkerber | f.close() |
167 | 1 | tkerber | if cartesian:
|
168 | 1 | tkerber | atoms_pos *= lattice_constant |
169 | 1 | tkerber | atoms = Atoms(symbols = atom_symbols, cell = basis_vectors, pbc = True)
|
170 | 1 | tkerber | if cartesian:
|
171 | 1 | tkerber | atoms.set_positions(atoms_pos) |
172 | 1 | tkerber | else:
|
173 | 1 | tkerber | atoms.set_scaled_positions(atoms_pos) |
174 | 1 | tkerber | if selective_dynamics:
|
175 | 1 | tkerber | constraints = [] |
176 | 1 | tkerber | indices = [] |
177 | 1 | tkerber | for ind, sflags in enumerate(selective_flags): |
178 | 1 | tkerber | if sflags.any() and not sflags.all(): |
179 | 1 | tkerber | constraints.append(FixScaled(atoms.get_cell(), ind, sflags)) |
180 | 1 | tkerber | elif sflags.all():
|
181 | 1 | tkerber | indices.append(ind) |
182 | 1 | tkerber | if indices:
|
183 | 1 | tkerber | constraints.append(FixAtoms(indices)) |
184 | 1 | tkerber | if constraints:
|
185 | 1 | tkerber | atoms.set_constraint(constraints) |
186 | 1 | tkerber | return atoms
|
187 | 1 | tkerber | |
188 | 1 | tkerber | def read_vasp_out(filename='OUTCAR',index = -1): |
189 | 1 | tkerber | """Import OUTCAR type file.
|
190 | 1 | tkerber |
|
191 | 1 | tkerber | Reads unitcell, atom positions, energies, and forces from the OUTCAR file.
|
192 | 1 | tkerber |
|
193 | 1 | tkerber | - does not (yet) read any constraints
|
194 | 1 | tkerber | """
|
195 | 1 | tkerber | import os |
196 | 1 | tkerber | import numpy as np |
197 | 1 | tkerber | from ase.calculators.singlepoint import SinglePointCalculator |
198 | 1 | tkerber | from ase import Atoms, Atom |
199 | 1 | tkerber | |
200 | 1 | tkerber | if isinstance(filename, str): |
201 | 1 | tkerber | f = open(filename)
|
202 | 1 | tkerber | else: # Assume it's a file-like object |
203 | 1 | tkerber | f = filename |
204 | 1 | tkerber | data = f.readlines() |
205 | 1 | tkerber | natoms = 0
|
206 | 1 | tkerber | images = [] |
207 | 1 | tkerber | atoms = Atoms(pbc = True)
|
208 | 1 | tkerber | energy = 0
|
209 | 1 | tkerber | species = [] |
210 | 1 | tkerber | species_num = [] |
211 | 1 | tkerber | symbols = [] |
212 | 1 | tkerber | for n,line in enumerate(data): |
213 | 1 | tkerber | if 'POSCAR:' in line: |
214 | 1 | tkerber | temp = line.split() |
215 | 1 | tkerber | species = temp[1:]
|
216 | 1 | tkerber | if 'ions per type' in line: |
217 | 1 | tkerber | temp = line.split() |
218 | 1 | tkerber | for ispecies in range(len(species)): |
219 | 1 | tkerber | species_num += [int(temp[ispecies+4])] |
220 | 1 | tkerber | natoms += species_num[-1]
|
221 | 1 | tkerber | for iatom in range(species_num[-1]): symbols += [species[ispecies]] |
222 | 1 | tkerber | if 'direct lattice vectors' in line: |
223 | 1 | tkerber | cell = [] |
224 | 1 | tkerber | for i in range(3): |
225 | 1 | tkerber | temp = data[n+1+i].split()
|
226 | 1 | tkerber | cell += [[float(temp[0]), float(temp[1]), float(temp[2])]] |
227 | 1 | tkerber | atoms.set_cell(cell) |
228 | 1 | tkerber | if 'FREE ENERGIE OF THE ION-ELECTRON SYSTEM' in line: |
229 | 1 | tkerber | energy = float(data[n+2].split()[4]) |
230 | 1 | tkerber | if 'POSITION ' in line: |
231 | 1 | tkerber | forces = [] |
232 | 1 | tkerber | for iatom in range(natoms): |
233 | 1 | tkerber | temp = data[n+2+iatom].split()
|
234 | 1 | tkerber | atoms += Atom(symbols[iatom],[float(temp[0]),float(temp[1]),float(temp[2])]) |
235 | 1 | tkerber | forces += [[float(temp[3]),float(temp[4]),float(temp[5])]] |
236 | 1 | tkerber | atoms.set_calculator(SinglePointCalculator(energy,forces,None,None,atoms)) |
237 | 1 | tkerber | images += [atoms] |
238 | 1 | tkerber | atoms = Atoms(pbc = True)
|
239 | 1 | tkerber | |
240 | 1 | tkerber | # return requested images, code borrowed from ase/io/trajectory.py
|
241 | 1 | tkerber | if isinstance(index, int): |
242 | 1 | tkerber | return images[index]
|
243 | 1 | tkerber | else:
|
244 | 1 | tkerber | step = index.step or 1 |
245 | 1 | tkerber | if step > 0: |
246 | 1 | tkerber | start = index.start or 0 |
247 | 1 | tkerber | if start < 0: |
248 | 1 | tkerber | start += len(images)
|
249 | 1 | tkerber | stop = index.stop or len(images) |
250 | 1 | tkerber | if stop < 0: |
251 | 1 | tkerber | stop += len(images)
|
252 | 1 | tkerber | else:
|
253 | 1 | tkerber | if index.start is None: |
254 | 1 | tkerber | start = len(images) - 1 |
255 | 1 | tkerber | else:
|
256 | 1 | tkerber | start = index.start |
257 | 1 | tkerber | if start < 0: |
258 | 1 | tkerber | start += len(images)
|
259 | 1 | tkerber | if index.stop is None: |
260 | 1 | tkerber | stop = -1
|
261 | 1 | tkerber | else:
|
262 | 1 | tkerber | stop = index.stop |
263 | 1 | tkerber | if stop < 0: |
264 | 1 | tkerber | stop += len(images)
|
265 | 1 | tkerber | return [images[i] for i in range(start, stop, step)] |
266 | 1 | tkerber | |
267 | 1 | tkerber | def write_vasp(filename, atoms, label='', direct=False, sort=None, symbol_count = None ): |
268 | 1 | tkerber | """Method to write VASP position (POSCAR/CONTCAR) files.
|
269 | 1 | tkerber |
|
270 | 1 | tkerber | Writes label, scalefactor, unitcell, # of various kinds of atoms,
|
271 | 1 | tkerber | positions in cartesian or scaled coordinates (Direct), and constraints
|
272 | 1 | tkerber | to file. Cartesian coordiantes is default and default label is the
|
273 | 1 | tkerber | atomic species, e.g. 'C N H Cu'.
|
274 | 1 | tkerber | """
|
275 | 1 | tkerber | |
276 | 1 | tkerber | import numpy as np |
277 | 1 | tkerber | from ase.constraints import FixAtoms, FixScaled |
278 | 1 | tkerber | |
279 | 1 | tkerber | if isinstance(filename, str): |
280 | 1 | tkerber | f = open(filename, 'w') |
281 | 1 | tkerber | else: # Assume it's a 'file-like object' |
282 | 1 | tkerber | f = filename |
283 | 1 | tkerber | |
284 | 1 | tkerber | if isinstance(atoms, (list, tuple)): |
285 | 1 | tkerber | if len(atoms) > 1: |
286 | 1 | tkerber | raise RuntimeError("Don't know how to save more than "+ |
287 | 1 | tkerber | "one image to VASP input")
|
288 | 1 | tkerber | else:
|
289 | 1 | tkerber | atoms = atoms[0]
|
290 | 1 | tkerber | |
291 | 1 | tkerber | # Write atom positions in scaled or cartesian coordinates
|
292 | 1 | tkerber | if direct:
|
293 | 1 | tkerber | coord = atoms.get_scaled_positions() |
294 | 1 | tkerber | else:
|
295 | 1 | tkerber | coord = atoms.get_positions() |
296 | 1 | tkerber | |
297 | 1 | tkerber | if atoms.constraints:
|
298 | 1 | tkerber | sflags = np.zeros((len(atoms), 3), dtype=bool) |
299 | 1 | tkerber | for constr in atoms.constraints: |
300 | 1 | tkerber | if isinstance(constr, FixScaled): |
301 | 1 | tkerber | sflags[constr.a] = constr.mask |
302 | 1 | tkerber | elif isinstance(constr, FixAtoms): |
303 | 1 | tkerber | sflags[constr.index] = [True, True, True] |
304 | 1 | tkerber | |
305 | 1 | tkerber | if sort:
|
306 | 1 | tkerber | ind = np.argsort(atoms.get_chemical_symbols()) |
307 | 1 | tkerber | symbols = np.array(atoms.get_chemical_symbols())[ind] |
308 | 1 | tkerber | coord = coord[ind] |
309 | 1 | tkerber | if atoms.constraints:
|
310 | 1 | tkerber | sflags = sflags[ind] |
311 | 1 | tkerber | else:
|
312 | 1 | tkerber | symbols = atoms.get_chemical_symbols() |
313 | 1 | tkerber | |
314 | 1 | tkerber | # Create a list sc of (symbol, count) pairs
|
315 | 1 | tkerber | if symbol_count:
|
316 | 1 | tkerber | sc = symbol_count |
317 | 1 | tkerber | else:
|
318 | 1 | tkerber | sc = [] |
319 | 1 | tkerber | psym = symbols[0]
|
320 | 1 | tkerber | count = 0
|
321 | 1 | tkerber | for sym in symbols: |
322 | 1 | tkerber | if sym != psym:
|
323 | 1 | tkerber | sc.append((psym, count)) |
324 | 1 | tkerber | psym = sym |
325 | 1 | tkerber | count = 1
|
326 | 1 | tkerber | else:
|
327 | 1 | tkerber | count += 1
|
328 | 1 | tkerber | sc.append((psym, count)) |
329 | 1 | tkerber | |
330 | 1 | tkerber | # Create the label
|
331 | 1 | tkerber | if label == '': |
332 | 1 | tkerber | for sym, c in sc: |
333 | 1 | tkerber | label += '%2s ' % sym
|
334 | 1 | tkerber | f.write(label + '\n')
|
335 | 1 | tkerber | |
336 | 1 | tkerber | # Write unitcell in real coordinates and adapt to VASP convention
|
337 | 1 | tkerber | # for unit cell
|
338 | 1 | tkerber | # ase Atoms doesn't store the lattice constant separately, so always
|
339 | 1 | tkerber | # write 1.0.
|
340 | 1 | tkerber | f.write('%19.16f\n' % 1.0) |
341 | 1 | tkerber | for vec in atoms.get_cell(): |
342 | 1 | tkerber | f.write(' ')
|
343 | 1 | tkerber | for el in vec: |
344 | 1 | tkerber | f.write(' %21.16f' % el)
|
345 | 1 | tkerber | f.write('\n')
|
346 | 1 | tkerber | |
347 | 1 | tkerber | # Numbers of each atom
|
348 | 1 | tkerber | for sym, count in sc: |
349 | 1 | tkerber | f.write(' %3i' % count)
|
350 | 1 | tkerber | f.write('\n')
|
351 | 1 | tkerber | |
352 | 1 | tkerber | if atoms.constraints:
|
353 | 1 | tkerber | f.write('Selective dynamics\n')
|
354 | 1 | tkerber | |
355 | 1 | tkerber | if direct:
|
356 | 1 | tkerber | f.write('Direct\n')
|
357 | 1 | tkerber | else:
|
358 | 1 | tkerber | f.write('Cartesian\n')
|
359 | 1 | tkerber | |
360 | 1 | tkerber | for iatom, atom in enumerate(coord): |
361 | 1 | tkerber | for dcoord in atom: |
362 | 1 | tkerber | f.write(' %19.16f' % dcoord)
|
363 | 1 | tkerber | if atoms.constraints:
|
364 | 1 | tkerber | for flag in sflags[iatom]: |
365 | 1 | tkerber | if flag:
|
366 | 1 | tkerber | s = 'F'
|
367 | 1 | tkerber | else:
|
368 | 1 | tkerber | s = 'T'
|
369 | 1 | tkerber | f.write('%4s' % s)
|
370 | 1 | tkerber | f.write('\n')
|
371 | 1 | tkerber | |
372 | 1 | tkerber | if type(filename) == str: |
373 | 1 | tkerber | f.close() |