Source code for httk.atomistic.models.formula.composition_view

"""Lazy composition presentation view."""

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

from httk.core import unwrap

from httk.atomistic.models.formula.backend import ChemicalFormulaBackend
from httk.atomistic.models.formula.composition import Composition
from httk.atomistic.models.formula.diagnostics import CompositionDiagnostic
from httk.atomistic.models.formula.view_base import ChemicalFormulaViewBase

if TYPE_CHECKING:
    from httk.atomistic.models.formula.like import ChemicalFormulaLike


[docs] class CompositionView(ChemicalFormulaViewBase, Composition): r"""Present any chemical-formula backend as a lazy composition. :param obj: The chemical-formula-like object to present. :param \*\*hints: Backend-selection hints. """ _backend: ChemicalFormulaBackend def __new__(cls, obj: "ChemicalFormulaLike", **hints: Any) -> Self: if isinstance(obj, cls): return obj backend = cls._prepare_backend(obj, hints) if backend.is_anonymous: raise ValueError("a composition view cannot invent elements for anonymous labels") instance = super().__new__(cls) instance._backend = backend return instance def __init__(self, obj: "ChemicalFormulaLike", **hints: Any) -> None: pass def _fill(self) -> None: backend = self._backend object.__setattr__(self, "amounts", backend.amounts) object.__setattr__(self, "uncertainties", backend.uncertainties) object.__setattr__(self, "complete", backend.complete) object.__setattr__(self, "exact", backend.exact) object.__setattr__(self, "normalized", backend.normalized) object.__setattr__(self, "normalization_status", backend.normalization_status) object.__setattr__(self, "diagnostics", backend.diagnostics) @cached_property
[docs] def amounts(self) -> tuple[tuple[str, Fraction], ...]: # pyright: ignore[reportIncompatibleVariableOverride] """Return the lazily materialized elemental amounts.""" if "amounts" not in self.__dict__: self._fill() return self.__dict__["amounts"]
@cached_property
[docs] def uncertainties(self) -> tuple[tuple[str, Fraction | None], ...]: # pyright: ignore[reportIncompatibleVariableOverride] """Return the lazily materialized amount precisions.""" if "amounts" not in self.__dict__: self._fill() return self.__dict__["uncertainties"]
@cached_property
[docs] def complete(self) -> bool: # pyright: ignore[reportIncompatibleVariableOverride] """Return whether the presented composition is complete.""" if "amounts" not in self.__dict__: self._fill() return self.__dict__["complete"]
@cached_property
[docs] def exact(self) -> bool: # pyright: ignore[reportIncompatibleVariableOverride] """Return whether the presented amounts are exact.""" if "amounts" not in self.__dict__: self._fill() return self.__dict__["exact"]
@cached_property
[docs] def normalized(self) -> bool: # pyright: ignore[reportIncompatibleVariableOverride] """Return whether the presented composition is normalized.""" if "amounts" not in self.__dict__: self._fill() return self.__dict__["normalized"]
@cached_property
[docs] def normalization_status(self) -> str: # pyright: ignore[reportIncompatibleVariableOverride] """Return the presented composition's normalization status.""" if "amounts" not in self.__dict__: self._fill() return self.__dict__["normalization_status"]
@cached_property
[docs] def diagnostics(self) -> tuple[CompositionDiagnostic, ...]: # pyright: ignore[reportIncompatibleVariableOverride] """Return diagnostics associated with the presented composition.""" if "amounts" not in self.__dict__: self._fill() return self.__dict__["diagnostics"]
def _ensure_materialized(self) -> None: _ = self.amounts def __reduce__(self) -> tuple[type[Self], tuple[ChemicalFormulaBackend]]: """Rebuild the view from its backend during pickling. :return: The view constructor and its backend argument. """ return type(self), (self._backend,)
[docs] def unwrap(self) -> Any: """Return the raw object behind the backend. :return: The unwrapped source object. """ return unwrap(self._backend)
[docs] def unview(self) -> Composition: """Return the presented composition as a standalone value. :return: The materialized composition value. """ backend = self._backend if type(backend) is Composition: return backend return Composition( self.amounts, self.uncertainties, self.complete, self.exact, self.normalized, self.normalization_status, self.diagnostics, )