Source code for httk.atomistic.models.bareprototype.bareprototype
"""The immutable anonymous Wyckoff-only prototype value."""
from collections.abc import Sequence
from typing import ClassVar
from httk.atomistic.models.bareprototype.backend import BarePrototypeBackend
from httk.atomistic.models.formula.notation import anonymous_symbol
from httk.atomistic.models.prototype.notation import canonical_label_map
from httk.atomistic.models.prototype.occupation import PrototypeOccupation
from httk.atomistic.symmetry.spacegroup import Spacegroup
[docs]
class BarePrototype(BarePrototypeBackend):
"""Store only standard-setting Wyckoff occupations, without geometrical refinement.
:param spacegroup: The standard-setting space group or its IT number.
:param occupations: Occupied Wyckoff positions and their anonymous labels.
"""
kind: ClassVar[str] = "bare_prototype"
def __init__(
self, spacegroup: Spacegroup | int, occupations: Sequence[PrototypeOccupation | tuple[str, str]]
) -> None:
self._spacegroup = spacegroup if isinstance(spacegroup, Spacegroup) else Spacegroup.standard(spacegroup)
if not self._spacegroup.is_standard_setting:
raise ValueError("BarePrototype records Wyckoff data in the IT standard setting")
raw = tuple(
value if isinstance(value, PrototypeOccupation) else PrototypeOccupation(*value) for value in occupations
)
if not raw:
raise ValueError("BarePrototype occupations must be non-empty")
letters_by_label: dict[str, list[str]] = {}
for value in raw:
try:
self._spacegroup.wyckoff_position(value.wyckoff)
except KeyError as exc:
raise ValueError(str(exc)) from exc
letters_by_label.setdefault(value.label, []).append(value.wyckoff)
expected = {anonymous_symbol(index) for index in range(len(letters_by_label))}
if set(letters_by_label) != expected:
raise ValueError("BarePrototype class labels must be consecutive anonymous symbols from 'A'")
relabel = canonical_label_map({label: tuple(sorted(letters)) for label, letters in letters_by_label.items()})
self._occupations = tuple(
sorted(
(PrototypeOccupation(value.wyckoff, relabel[value.label]) for value in raw),
key=lambda value: (value.label, value.wyckoff),
)
)
@property
def spacegroup(self) -> Spacegroup:
"""Return the standard-setting space group."""
return self._spacegroup
@property
def occupations(self) -> tuple[PrototypeOccupation, ...]:
"""Return canonical anonymous Wyckoff occupations."""
return self._occupations
def __eq__(self, other: object) -> bool:
if not isinstance(other, BarePrototype):
return NotImplemented
return (self.spacegroup, self.occupations) == (other.spacegroup, other.occupations)
def __hash__(self) -> int:
return hash((self.spacegroup, self.occupations))
def __repr__(self) -> str:
pairs = ", ".join(f"{value.wyckoff}:{value.label}" for value in self.occupations)
return f"BarePrototype({self.spacegroup.setting!r}, {pairs})"