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)