Source code for supercell_core.input_parsers

from typing import List, Optional

from .errors import *
from .physics import Unit
from .lattice import lattice, Lattice
from .heterostructure import Heterostructure


[docs]def read_POSCAR(filename: str, **kwargs) -> Lattice: """ Reads VASP input file "POSCAR" Parameters ---------- filename : str kwargs Passed on to `parse_POSCAR` Returns ------- Lattice object representing the same lattice as the VASP input files Raises ------ IOError If something is wrong with I/O on the file ParseError If supplied file is not a valid POSCAR, or if the arguments supplied cannot be reasonably paired with the input file in a way that makes a proper Lattice """ with open(filename, 'r') as f: # I don't think there is any reasonable POSCAR with > 1000 atoms # so s will be at most a few tens of thousands of kB, so just read() it s = f.read() return parse_POSCAR(s, **kwargs)
# Helper functions for parser to make the code read better
[docs]def eat_line(s: List[str]) -> List[str]: # Takes a list of lines, returns list without the first one return s[1:]
[docs]def get_line(s: List[str]) -> str: # Returns first line in list return s[0].strip()
[docs]def iter_magmom(magmom: str): # TODO: Type Hint for generator # Yields values of spin from magmom string s = [x.strip() for x in magmom.splitlines()][0].split() try: for el in s: y = el.split('*') if len(y) == 2: z_spin = int(y[1]) count = int(y[0]) for i in range(count): yield (0, 0, z_spin) elif len(y) == 1: yield (0, 0, int(y[0])) else: raise ParseError("Ambiguous spin description using '*'") except Exception: raise ParseError("Invalid MAGMOM string supplied")
[docs]def parse_POSCAR(poscar: str, atomic_species: List[str] = None, magmom: Optional[str] = None, normalise_positions: Optional[bool] = False) -> Lattice: """ Reads lattice data from a string, treating it as VASP POSCAR file Format documentation: https://cms.mpi.univie.ac.at/wiki/index.php/POSCAR Parameters ---------- poscar : str Contents of the POSCAR file atomic_species : List[str] Contains symbols of chemical elements in the same order as in POTCAR One symbol per atomic species Required if the POSCAR file does not specify the atomic species magmom : str, optional Contents of the MAGMOM line from INCAR file. Default: all spins set to zero normalise_positions : bool, optional If True, atomic positions are moved to be within the elementary cell (preserving location of atoms in the whole crystal) Default: False Returns ------- Lattice object representing the same lattice as the VASP would-be input Raises ------ IOError If something is wrong with I/O on the file ParseError If supplied file is not a valid POSCAR, or if the arguments supplied cannot be reasonably paired with the input file in a way that makes a proper Lattice """ # Build the lattice the usual way res = lattice() try: s = poscar.splitlines() # 1: system name, irrelevant for us s = eat_line(s) # 2: scale factor scale = float(get_line(s)) s = eat_line(s) # 3, 4, 5: lattice vectors in angstroms vecs = [] for i in range(3): vecs.append([scale * float(x.strip()) for x in get_line(s).split()]) if len(vecs[-1]) != 3: raise ParseError("Vector length different than 3") s = eat_line(s) res.set_vectors(*vecs) # 6 (optional): atomic species names line = get_line(s).split() # Heuristic for species names – failure to parse as int try: int(line[0]) except: if atomic_species is None: atomic_species = line line = get_line(s).split() s = eat_line(s) # 7: atomic species counts if len(line) != len(atomic_species): raise ParseError("Number of atomic species doesn't match ({} != {})".format( len(get_line(s).split()), len(atomic_species) )) as_counts = [int(x.strip()) for x in get_line(s).split()] s = eat_line(s) # 8: possibly Selective Dynamics, then remember to ignore Ts and Fs # at the ends of positions selective_dynamics = False if get_line(s)[0] in "Ss": s = eat_line(s) selective_dynamics = True # 9: Cartesian or Direct unit = Unit.Crystal if get_line(s)[0] in "CcKk": unit = Unit.Angstrom s = eat_line(s) # 10+: atomic positions if magmom: spins = iter_magmom(magmom) else: # default spin: 0 spins = iter([(0, 0, 0)] * sum(as_counts)) def letter_to_sd_bool(letter: str): if letter == 'T': return True elif letter == 'F': return False raise ParseError("Bad selective dynamics flag") for specie, count in zip(atomic_species, as_counts): for i in range(count): splitted = get_line(s).split() vec = [float(x) for x in splitted[0:3]] if selective_dynamics: if len(splitted) != 6: raise ParseError("Vector length different than 3, " + "or bad number of selective dynamics " + "flags") sd = tuple(map(letter_to_sd_bool, [x.strip() for x in splitted[3:6]])) else: if len(splitted) != 3: raise ParseError("Vector length different than 3") res.add_atom(specie, vec, next(spins), unit=unit, normalise_positions=normalise_positions) s = eat_line(s) except Exception as e: raise ParseError(str(e)) return res
[docs]def read_supercell_in(filename: str) -> Heterostructure: pass