root / ase / io / vasp.py @ 14
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() |