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

"""Lazy protopattern erasure and presentation view."""

from typing import Any, Self

from httk.core import MISSING, unwrap

from httk.atomistic.models.protopattern.backend import ProtopatternBackend
from httk.atomistic.models.protopattern.derived import DerivedProtopattern
from httk.atomistic.models.protopattern.protopattern import Protopattern
from httk.atomistic.models.protopattern.view_base import ProtopatternViewBase


[docs] class ProtopatternView(ProtopatternViewBase, Protopattern): r"""Present a lazy standard-setting protopattern view. Sources may be an existing protopattern, a protostructure (real species erased to anonymous classes), or a fundamental-domain/structure source recognized and discretized. Erasure is deferred until the first field access. :param obj: The protopattern-like or structure-like source. :param \*\*hints: Backend-selection hints. """ _backend: ProtopatternBackend _resolved_protopattern: Protopattern | None _DEFERRED_FIELDS = frozenset({"_spacegroup", "_occupations"}) def __new__(cls, obj: Any = MISSING, **hints: Any) -> Self: if obj is MISSING: # pickle/copy rebuild an empty instance; __setstate__ restores it return super().__new__(cls) if isinstance(obj, cls): if hints: raise ValueError("ProtopatternView rewrapping does not accept hints") return obj backend = cls._prepare_backend(obj, hints) if not isinstance(backend, ProtopatternBackend): raise TypeError(f"Cannot recognize {type(backend).__name__} as a protopattern source") instance = super().__new__(cls) instance._backend = backend instance._resolved_protopattern = None return instance def __init__(self, obj: Any, **hints: Any) -> None: pass def __getattribute__(self, name: str) -> Any: if name in type(self)._DEFERRED_FIELDS: namespace = object.__getattribute__(self, "__dict__") if name not in namespace: object.__getattribute__(self, "_effective_protopattern")() return object.__getattribute__(self, name) def _effective_protopattern(self) -> Protopattern: cached = object.__getattribute__(self, "_resolved_protopattern") if cached is not None: return cached backend = object.__getattribute__(self, "_backend") if type(backend) is Protopattern: resolved = backend elif isinstance(backend, DerivedProtopattern): resolved = backend.resolve() else: resolved = Protopattern(backend.spacegroup, backend.occupations) state = dict(resolved.__dict__) state["_resolved_protopattern"] = resolved object.__getattribute__(self, "__dict__").update(state) return resolved
[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) -> Protopattern: """Return the erased protopattern as a standalone value. :return: The protopattern value. """ return self._effective_protopattern()
def __getstate__(self) -> dict[str, Any]: state: dict[str, Any] = {"backend": self._backend} if self._resolved_protopattern is not None: state["resolved"] = self._resolved_protopattern return state def __setstate__(self, state: dict[str, Any]) -> None: self._backend = state["backend"] self._resolved_protopattern = None resolved = state.get("resolved") if resolved is not None: state_copy = dict(resolved.__dict__) state_copy["_resolved_protopattern"] = resolved object.__getattribute__(self, "__dict__").update(state_copy)