Source code for atomistics.calculators.lammps.shared

from collections.abc import Iterable
from typing import Optional


[docs] def get_box_relax_command( pressure: float | Iterable[float | None], vmax: Optional[float] ) -> str: """ Build a LAMMPS ``fix box/relax`` command string for the given pressure specification. When ``pressure`` is a scalar the command uses isotropic relaxation (``iso``). When ``pressure`` is an iterable of length 3, the x/y/z components are set individually; components that are ``None`` are omitted. An iterable of length 6 additionally sets the xy/xz/yz off-diagonal components. Args: pressure (float | Iterable[float | None]): Target pressure in bar. A scalar applies isotropic pressure; an iterable of 3 or 6 elements applies anisotropic pressure component-wise (``None`` entries are skipped). vmax (float | None): Maximum fractional volume change per timestep (LAMMPS ``vmax`` keyword). No ``vmax`` clause is added when ``None``. Returns: str: A complete LAMMPS ``fix`` command string. Raises: ValueError: If ``pressure`` is an iterable whose length is not 3 or 6. TypeError: If ``vmax`` is not a ``float``. """ if not isinstance(pressure, Iterable): box_relax = f"fix ensemble all box/relax iso {pressure}" else: pressure_lst = list(pressure) if len(pressure_lst) == 3: pressure_str = " ".join( f"{tag} {value}" for tag, value in zip(["x", "y", "z"], pressure_lst) if value is not None ) box_relax = f"fix ensemble all box/relax {pressure_str}" elif len(pressure_lst) == 6: pressure_str = " ".join( f"{tag} {value}" for tag, value in zip(["x", "y", "z", "xy", "xz", "yz"], pressure_lst) if value is not None ) box_relax = f"fix ensemble all box/relax {pressure_str}" else: raise ValueError( "pressure must be a float or an iterable of length 3 or 6." ) if vmax is not None: if isinstance(vmax, float): return box_relax + f" vmax {vmax}" else: raise TypeError("vmax must be a float.") else: return box_relax