Source code for dpdata.vasp.poscar

#!/usr/bin/python3
from __future__ import annotations

import numpy as np


def _to_system_data_lower(lines, cartesian=True, selective_dynamics=False):
    def move_flag_mapper(flag):
        if flag == "T":
            return True
        elif flag == "F":
            return False
        else:
            raise RuntimeError(f"Invalid move flag: {flag}")

    """Treat as cartesian poscar."""
    system = {}
    system["atom_names"] = [str(ii) for ii in lines[5].split()]
    system["atom_numbs"] = [int(ii) for ii in lines[6].split()]
    scale = float(lines[1])
    cell = []
    move_flags = []
    for ii in range(2, 5):
        boxv = [float(jj) for jj in lines[ii].split()]
        boxv = np.array(boxv) * scale
        cell.append(boxv)
    system["cells"] = [np.array(cell)]
    natoms = sum(system["atom_numbs"])
    coord = []
    for ii in range(8, 8 + natoms):
        tmp = lines[ii].split()
        tmpv = [float(jj) for jj in tmp[:3]]
        if cartesian:
            tmpv = np.array(tmpv) * scale
        else:
            tmpv = np.matmul(np.array(tmpv), system["cells"][0])
        coord.append(tmpv)
        if selective_dynamics:
            if len(tmp) == 6:
                move_flags.append(list(map(move_flag_mapper, tmp[3:])))
            else:
                raise RuntimeError(
                    f"Invalid move flags, should be 6 columns, got {tmp}"
                )

    system["coords"] = [np.array(coord)]
    system["orig"] = np.zeros(3)
    atom_types = []
    for idx, ii in enumerate(system["atom_numbs"]):
        for jj in range(ii):
            atom_types.append(idx)
    system["atom_types"] = np.array(atom_types, dtype=int)
    system["cells"] = np.array(system["cells"])
    system["coords"] = np.array(system["coords"])
    if move_flags:
        move_flags = np.array(move_flags, dtype=bool)
        move_flags = move_flags.reshape((1, natoms, 3))
        system["move"] = np.array(move_flags, dtype=bool)
    return system


[docs] def to_system_data(lines): # remove the line that has 'selective dynamics' selective_dynamics = False if lines[7][0] == "S" or lines[7][0] == "s": selective_dynamics = True lines.pop(7) is_cartesian = lines[7][0] in ["C", "c", "K", "k"] if not is_cartesian: if lines[7][0] not in ["d", "D"]: raise RuntimeError( "seem not to be a valid POSCAR of vasp 5.x, may be a POSCAR of vasp 4.x?" ) return _to_system_data_lower(lines, is_cartesian, selective_dynamics)
[docs] def from_system_data(system, f_idx=0, skip_zeros=True): ret = "" for ii, name in zip(system["atom_numbs"], system["atom_names"]): if ii == 0: continue ret += "%s%d " % (name, ii) # noqa: UP031 ret += "\n" ret += "1.0\n" for ii in system["cells"][f_idx]: for jj in ii: ret += f"{jj:.16e} " ret += "\n" for idx, ii in enumerate(system["atom_names"]): if system["atom_numbs"][idx] == 0: continue ret += f"{ii} " ret += "\n" for ii in system["atom_numbs"]: if ii == 0: continue ret += "%d " % ii # noqa: UP031 ret += "\n" move = system.get("move", None) if move is not None and len(move) > 0: ret += "Selective Dynamics\n" # should use Cartesian for VESTA software ret += "Cartesian\n" atype = system["atom_types"] posis = system["coords"][f_idx] # atype_idx = [[idx,tt] for idx,tt in enumerate(atype)] # sort_idx = np.argsort(atype, kind = 'mergesort') sort_idx = np.lexsort((np.arange(len(atype)), atype)) atype = atype[sort_idx] posis = posis[sort_idx] if move is not None and len(move) > 0: move = move[f_idx][sort_idx] if isinstance(move, np.ndarray): move = move.tolist() posi_list = [] for idx in range(len(posis)): ii_posi = posis[idx] line = f"{ii_posi[0]:15.10f} {ii_posi[1]:15.10f} {ii_posi[2]:15.10f}" if move is not None and len(move) > 0: move_flags = move[idx] if not isinstance(move_flags, list) or len(move_flags) != 3: raise RuntimeError( f"Invalid move flags: {move_flags}, should be a list of 3 bools" ) line += " " + " ".join("T" if flag else "F" for flag in move_flags) posi_list.append(line) posi_list.append("") ret += "\n".join(posi_list) return ret