Source code for httk.atomistic.models.moments.crystalaxis_view
"""Crystal-axis class view for site moments."""
import fractions
from functools import cached_property
from typing import Any, Self
from httk.core import SurdVector
from httk.atomistic.models._vector_guards import to_precision, to_surdvector
from httk.atomistic.models.cell.cell import Cell
from httk.atomistic.models.cell.like import CellLike
from httk.atomistic.models.cell.view import CellView
from httk.atomistic.models.moments.backend import SiteMomentsBackend
from httk.atomistic.models.moments.cartesian import CartesianSiteMoments
from httk.atomistic.models.moments.crystalaxis import (
CrystalAxisSiteMoments,
_cartesian_to_crystalaxis,
)
from httk.atomistic.models.moments.like import SiteMomentsLike
from httk.atomistic.models.moments.view_base import SiteMomentsViewBase
[docs]
class CrystalAxisSiteMomentsView(SiteMomentsViewBase, CrystalAxisSiteMoments):
"""A lazy crystal-axis site-moments presentation with an eagerly checked frame hint."""
_backend: SiteMomentsBackend
_cell_hint: Cell | None
def __new__(cls, obj: SiteMomentsLike, *, cell: CellLike | None = None, **hints: Any) -> Self:
if isinstance(obj, cls):
return obj
backend = cls._prepare_backend(obj, hints)
cell_hint = None if cell is None else (cell if isinstance(cell, Cell) else CellView(cell))
if isinstance(backend, CrystalAxisSiteMoments):
if cell_hint is not None and cell_hint != backend.cell:
raise ValueError("CrystalAxisSiteMomentsView cell hint conflicts with the backend cell")
elif isinstance(backend, CartesianSiteMoments) and cell_hint is None:
raise ValueError("CrystalAxisSiteMomentsView requires a cell hint for Cartesian moments")
instance = super().__new__(cls)
instance._backend = backend
instance._cell_hint = cell_hint
return instance
def __init__(self, obj: SiteMomentsLike, *, cell: CellLike | None = None, **hints: Any) -> None:
pass
def _fill_cell(self) -> None:
if self._cell_hint is not None:
value = self._cell_hint
elif isinstance(self._backend, CrystalAxisSiteMoments):
value = self._backend.cell
else:
raise ValueError("CrystalAxisSiteMomentsView needs a cell to define the crystal-axis frame")
object.__setattr__(self, "_cell", value)
def _fill_crystalaxis_moments(self) -> None:
if isinstance(self._backend, CrystalAxisSiteMoments):
moments = to_surdvector(self._backend.crystalaxis_moments)
else:
# Access the canonical value before the cell: collinear backends must fail at fill
# because a scalar moment has no axis to invert into.
cartesian = to_surdvector(self._backend.cartesian_moments)
moments = _cartesian_to_crystalaxis(cartesian, self.cell)
if len(moments.dim) != 2 or moments.dim[1] != 3:
raise ValueError("CrystalAxisSiteMoments moments must be an Nx3 vector-like")
object.__setattr__(self, "_crystalaxis_moments", moments)
def _fill_precision(self) -> None:
object.__setattr__(self, "_precision", to_precision(self._backend.precision))
@cached_property
def _cell(self) -> Cell: # type: ignore[override] # pyright: ignore[reportIncompatibleVariableOverride]
self._fill_cell()
return self.__dict__["_cell"]
@cached_property
def _crystalaxis_moments(self) -> SurdVector: # type: ignore[override] # pyright: ignore[reportIncompatibleVariableOverride]
self._fill_crystalaxis_moments()
return self.__dict__["_crystalaxis_moments"]
@cached_property
def _precision(self) -> fractions.Fraction | None: # type: ignore[override] # pyright: ignore[reportIncompatibleVariableOverride]
self._fill_precision()
return self.__dict__["_precision"]
[docs]
def unview(self) -> CrystalAxisSiteMoments:
# A genuine CrystalAxisSiteMoments backend is exactly the presented value (a conflicting
# cell hint is rejected at construction): reuse it.
backend = self._backend
if type(backend) is CrystalAxisSiteMoments:
return backend
return CrystalAxisSiteMoments(self._crystalaxis_moments, self.cell, self._precision)