Source code for stko._internal.molecular.periodic.unitcell

import logging
from pathlib import Path
from typing import Self

import numpy as np
from stk import PeriodicInfo

from stko._internal.molecular.periodic.utilities import get_from_parameters

logger = logging.getLogger(__name__)


[docs] class UnitCell(PeriodicInfo): """Unit cell information for periodic systems. We are aware that this naming choice may not be appropriate (because not all inputs will be unit cells, strictly). However, for backwards compatability, we have not changed this naming. """ @classmethod def _update_periodic_info( cls, vector_1: np.ndarray, vector_2: np.ndarray, vector_3: np.ndarray, ) -> Self: """Return clone of :class:`.UnitCell` with new parameters.""" clone = cls.__new__(cls) UnitCell.__init__( self=clone, vector_1=vector_1, vector_2=vector_2, vector_3=vector_3, ) return clone
[docs] def with_cell_from_vectors( self, vector_1: np.ndarray, vector_2: np.ndarray, vector_3: np.ndarray, ) -> Self: """Update cell. Parameters: vector_1: First cell lattice vector of shape (3, ) in Angstrom. vector_2: Second cell lattice vector of shape (3, ) in Angstrom. vector_3: Third cell lattice vector of shape (3, ) in Angstrom. Returns: Clone with updated cell parameters. """ return self._update_periodic_info( vector_1=vector_1, vector_2=vector_2, vector_3=vector_3, )
[docs] def with_cell_from_turbomole(self, filename: Path | str) -> Self: # noqa: PLR0912, C901 """Update cell from structure in Turbomole coord file. Returns: Clone with updated cell parameters. """ filename = Path(filename) bohr_to_ang = 0.5291772105638411 with filename.open() as f: content = f.readlines() periodicity: bool | int = False lattice_vectors = None lattice_units = None cell_parameters = None cell_units = None for line_number, line in enumerate(content): if "$periodic" in line: periodicity = int(line.rstrip().split()[1]) if "$cell" in line: if "angs" in line: cell_units = "angstrom" elif "bohr" in line: cell_units = "bohr" else: msg = "cell not in Angstroms." raise ValueError(msg) cell_parameters = [ float(j) for j in content[line_number + 1].rstrip().split() ] if "$lattice" in line: if "angs" in line: lattice_units = "angstrom" elif "bohr" in line: lattice_units = "bohr" else: msg = "lattice not in Angstroms." raise ValueError(msg) lattice_vectors = ( np.array( [ float(j) for j in content[line_number + 1].rstrip().split() ] ), np.array( [ float(j) for j in content[line_number + 2].rstrip().split() ] ), np.array( [ float(j) for j in content[line_number + 3].rstrip().split() ] ), ) # Check that cell is only defined once. chk2 = lattice_vectors is not None and cell_parameters is not None if periodicity and chk2: msg = "The cell is defined twice in the file." raise RuntimeError(msg) if lattice_vectors is not None: vector_1 = ( lattice_vectors[0] * bohr_to_ang if lattice_units == "bohr" else lattice_vectors[0] ) vector_2 = ( lattice_vectors[1] * bohr_to_ang if lattice_units == "bohr" else lattice_vectors[0] ) vector_3 = ( lattice_vectors[2] * bohr_to_ang if lattice_units == "bohr" else lattice_vectors[0] ) elif cell_parameters is not None: vector_1, vector_2, vector_3 = get_from_parameters( a=( cell_parameters[0] * bohr_to_ang if cell_units == "bohr" else cell_parameters[0] ), b=( cell_parameters[1] * bohr_to_ang if cell_units == "bohr" else cell_parameters[1] ), c=( cell_parameters[2] * bohr_to_ang if cell_units == "bohr" else cell_parameters[2] ), alpha=cell_parameters[3], beta=cell_parameters[4], gamma=cell_parameters[5], ) else: msg = "The cell is not defined in the file." raise RuntimeError(msg) # Update the cell. return self._update_periodic_info(vector_1, vector_2, vector_3)
[docs] def with_cell_from_cif(self, filename: Path | str) -> Self: """Update cell from structure in CIF. Returns: Clone with updated cell parameters. """ filename = Path(filename) cell_info: dict[str, float] = {} targets = { "_cell_length_a": "a", "_cell_length_b": "b", "_cell_length_c": "c", "_cell_angle_alpha": "alpha", "_cell_angle_beta": "beta", "_cell_angle_gamma": "gamma", } with filename.open() as f: lines = f.readlines() for targ, target_string in targets.items(): for line in lines: # Avoid running through the rest. if target_string in cell_info: break splits = line.rstrip().split(" ") if splits[0] == targ: cell_info[targets[targ]] = float(splits[-1]) vector_1, vector_2, vector_3 = get_from_parameters( a=cell_info["a"], b=cell_info["b"], c=cell_info["c"], alpha=cell_info["alpha"], beta=cell_info["beta"], gamma=cell_info["gamma"], ) # Update the cell. return self._update_periodic_info(vector_1, vector_2, vector_3)