def _proc_geom_buffs( geom_buff: Tuple[str, Sequence[Basis], Species, Sequence[Tau]], geom_init: Tuple[str, float, Basis, Species, Tau], target_coord: str, relax_dims: _RelaxDims ) -> Tuple[Sequence[Basis], Species, Sequence[Tau]]: from itertools import starmap from pwproc.geometry import convert_coords # Unpack geometry ctype_i, alat, basis_i, species_i, pos_i = geom_init ctype, basis_steps, species, pos = geom_buff assert (species_i == species) # Convert coordinates if needed pos_i = convert_coords(alat, basis_i, pos_i, ctype_i, target_coord) basis_steps = (basis_i, ) * len(pos) if len(basis_steps) == 0 else tuple( basis_steps) pos = tuple( starmap( lambda basis, tau: convert_coords(alat, basis, tau, ctype, target_coord), zip(basis_steps, pos))) # Unpack relax dimensions n_steps, relax_kind, relax_done, zmag_relax = relax_dims # The geometry is unchanged in a magnetization check, just eliminate the last if zmag_relax: pos = pos[:-1] basis_steps = basis_steps[:-1] # Check length of buffer if relax_done: # The final duplicate SCF in vc-relax does not register on the step count # The converged geometry is printed again after SCF at the end of relax # However the first geometry is captured in the init_geom buffer if len(pos) != n_steps: raise ParserError("Unexpected length for geometry") # Remove final duplicate geometry if relax_kind == 'relax': pos = pos[:-1] basis_steps = basis_steps[:-1] else: # First geometry is not counted here if len(pos) == n_steps - 1: pass elif len(pos) == n_steps: # Geometry written for a step that did not finish pos = pos[:-1] basis_steps = basis_steps[:-1] else: raise ParserError("Unexpected length for geometry") return (basis_i, ) + basis_steps, species, (pos_i, ) + pos
def parse_scf(path, coord_type='crystal'): # type: (str, str) -> GeometryData """Parse pw.x output file. :param path: path to pw.x output :param coord_type: coordinate type of output :returns: GeometryData """ from pwproc.geometry import convert_coords, GeometryData from pwproc.parsers import get_save_file, get_init_basis, get_init_coord # Run parsers on output to get geometry prefix = get_save_file(path) alat, basis = get_init_basis(path) ctype, species, pos = get_init_coord(path) # Parse file for energy energy = get_scf_energy(path) # Convert coordinates if needed pos = convert_coords(alat, basis, pos, ctype, coord_type) return GeometryData(prefix, basis, species, pos, energy=energy, coord_type=coord_type)
def convert_coords(self, new_coords): # type: (str) -> None from pwproc.geometry import convert_coords self.tau = tuple( convert_coords(1.0, b, t, self.coord_type, new_coords) for b, t in zip(self.basis, self.tau)) self._coord_type = new_coords
def read_poscar(lines, out_type='angstrom'): # type: (Iterable[str], str) -> Tuple[str, float, Basis, Species, Tau] from itertools import chain, repeat from pwproc.util import parse_vector from pwproc.geometry import convert_coords # Read data from input lines = iter(lines) name = next(lines).strip() alat = float(next(lines)) basis = tuple(next(lines) for _ in range(3)) s_name = next(lines) s_num = next(lines) # Read atomic positions coord_line = next(lines).strip().lower() if coord_line == 'direct': in_type = 'crystal' elif coord_line == 'cartesian': in_type = 'angstrom' else: raise ValueError('Poscar error {}'.format(coord_line)) pos = [line for line in lines] # parse the basis basis = alat * Basis(np.array(tuple(map(parse_vector, basis)))) # Parse the species label species_pairs = tuple(zip(s_name.split(), map(int, s_num.split()))) species = tuple(chain(*(repeat(s, n) for s, n in species_pairs))) species = Species(species) # Parse positions pos = alat * Tau(np.array(tuple(map(parse_vector, pos)))) # Convert the input coordinates pos = convert_coords(alat, basis, pos, in_type, out_type) return name, alat, basis, species, pos
def convert_coords(self, new_coord): # type: (str) -> None from pwproc.geometry import convert_coords self.tau = convert_coords(1.0, self.basis, self.tau, self.coord_type, new_coord) self._coord_type = new_coord