Source code for httk.atomistic.cell_class_view
"""
A view presenting any cell backend as a Cell (the class representation).
"""
import fractions
from functools import cached_property
from typing import Any, Self
from httk.core import SurdScalar, SurdVector, unwrap
from ._vector_guards import to_periodicity, to_precision, to_surdscalar, to_surdvector
from .cell import Cell
from .cell_backend import CellBackend
from .cell_like import CellLike
from .cell_view import CellView
[docs]
class CellClassView(CellView, Cell):
"""
A view presenting an underlying cell backend as a ``Cell``.
This view is a genuine ``Cell``, so it can be passed anywhere a Cell is accepted.
Its state is built lazily on first access from the backend.
"""
_backend: CellBackend
def __new__(cls, obj: CellLike, **hints: Any) -> Self:
if isinstance(obj, cls):
return obj
backend = cls._prepare_backend(obj, hints)
instance = super().__new__(cls)
instance._backend = backend
# Derived-value caches are checked by Cell's ordinary properties, so initialize them
# eagerly without materializing any backend presentation state.
instance._basis_cache = None
instance._metric_cache = None
instance._lengths_cache = None
instance._angles_cache = None
instance._volume_cache = None
return instance
def __init__(self, obj: CellLike, **hints: Any) -> None:
pass
# Validate then assign: failed fills leave no partial presentation state, and fills must not
# read shadowed attributes or they recurse.
def _fill_unscaled_basis(self) -> None:
unscaled = to_surdvector(self._backend.unscaled_basis)
if unscaled.dim != (3, 3):
raise ValueError("Cell basis must be a 3x3 vector-like")
if unscaled.det().sign() == 0:
raise ValueError(
"Cell basis must be non-degenerate (its three vectors must span three "
"dimensions); a zero or linearly dependent row cannot be a cell, and for a "
"non-periodic direction it should be a unit vector rather than a zero one"
)
self.__dict__["_unscaled_basis"] = unscaled
def _fill_scale(self) -> None:
scale = to_surdscalar(self._backend.scale)
if scale.sign() <= 0:
raise ValueError("Cell scale must be strictly positive")
self.__dict__["_scale"] = scale
def _fill_precision(self) -> None:
self.__dict__["_precision"] = to_precision(self._backend.precision)
def _fill_periodicity(self) -> None:
self.__dict__["_periodicity"] = to_periodicity(self._backend.periodicity)
@cached_property
def _unscaled_basis(self) -> SurdVector: # type: ignore[override] # pyright: ignore[reportIncompatibleVariableOverride]
self._fill_unscaled_basis()
return self.__dict__["_unscaled_basis"]
@cached_property
def _scale(self) -> SurdScalar: # type: ignore[override] # pyright: ignore[reportIncompatibleVariableOverride]
self._fill_scale()
return self.__dict__["_scale"]
@cached_property
def _precision(self) -> fractions.Fraction | None: # type: ignore[override] # pyright: ignore[reportIncompatibleVariableOverride]
self._fill_precision()
return self.__dict__["_precision"]
@cached_property
def _periodicity(self) -> tuple[bool, bool, bool]: # type: ignore[override] # pyright: ignore[reportIncompatibleVariableOverride]
self._fill_periodicity()
return self.__dict__["_periodicity"]
[docs]
def unwrap(self) -> Any:
return unwrap(self._backend)