Source code for httk.atomistic.models.sites.view

"""
A view presenting any sites backend as a Sites object (the class representation).
"""

import fractions
from functools import cached_property
from typing import Any, Self

from httk.core import FracVector, unwrap

from httk.atomistic.models._vector_guards import to_fracvector, to_precision
from httk.atomistic.models.sites.backend import SitesBackend
from httk.atomistic.models.sites.like import SitesLike
from httk.atomistic.models.sites.sites import Sites
from httk.atomistic.models.sites.view_base import SitesViewBase


[docs] class SitesView(SitesViewBase, Sites): r""" A view presenting an underlying sites backend as a ``Sites`` object. This view is a genuine ``Sites``, so it can be passed anywhere a Sites is accepted. Its state is built lazily on first access from the backend. :param obj: The sites-like object to present. :param \**hints: Backend-selection hints. """ _backend: SitesBackend def __new__(cls, obj: SitesLike, **hints: Any) -> Self: if isinstance(obj, cls): return obj backend = cls._prepare_backend(obj, hints) instance = super().__new__(cls) instance._backend = backend return instance def __init__(self, obj: SitesLike, **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_reduced_coords(self) -> None: coords = to_fracvector(self._backend.reduced_coords) if coords.dim != () and not (len(coords.dim) == 2 and coords.dim[1] == 3): raise ValueError("Sites reduced_coords must be an Nx3 vector-like") object.__setattr__(self, "_reduced_coords", coords) def _fill_precision(self) -> None: object.__setattr__(self, "_precision", to_precision(self._backend.precision)) @cached_property def _reduced_coords(self) -> FracVector: # type: ignore[override] # pyright: ignore[reportIncompatibleVariableOverride] self._fill_reduced_coords() return self.__dict__["_reduced_coords"] @cached_property def _precision(self) -> fractions.Fraction | None: # type: ignore[override] # pyright: ignore[reportIncompatibleVariableOverride] self._fill_precision() return self.__dict__["_precision"]
[docs] def unwrap(self) -> Any: """Return the raw object behind the backend. :return: The unwrapped source object. """ return unwrap(self._backend)
def __reduce__(self) -> tuple[type[Self], tuple[SitesBackend]]: """Rebuild the view from its backend during pickling. :return: The view constructor and its backend argument. """ return type(self), (self._backend,)
[docs] def unview(self) -> Sites: """Return this presentation as standalone exact sites. :return: The exact sites representation. """ # The folded design makes a genuine Sites backend exactly the presented value: reuse it. backend = self._backend if type(backend) is Sites: return backend return Sites(self._reduced_coords, self._precision)