Source code for httk.atomistic.models.crystalpattern.fundamental

"""The standard-setting dummy-species fundamental-domain value."""

from collections.abc import Sequence
from functools import cached_property
from typing import TYPE_CHECKING, Any, ClassVar, Self

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.crystalpattern.anonymize import dummy_species, is_dummy_species
from httk.atomistic.models.crystalpattern.backend import CrystalPatternBackend
from httk.atomistic.models.formula.notation import anonymous_symbol
from httk.atomistic.models.sites.sites import Sites
from httk.atomistic.models.species.like import SpeciesLike
from httk.atomistic.models.species.species import Species
from httk.atomistic.models.species.view import SpeciesView
from httk.atomistic.models.structure.asu import FundamentalDomainStructure, WyckoffSite
from httk.atomistic.symmetry._periodicity_guard import require_full_periodicity
from httk.atomistic.symmetry.setting_transform import SettingTransform
from httk.atomistic.symmetry.spacegroup import Spacegroup

if TYPE_CHECKING:
    from httk.atomistic.models.protopattern.protopattern import Protopattern


[docs] class FundamentalDomainPattern(CrystalPatternBackend): """Store a standard-setting fundamental domain with dummy species labels. ``FundamentalDomainPattern`` is the fundamental-domain member of the ``CrystalPattern`` family, the anonymous-species, exact-geometry cell of the material-information matrix: ====================== ============== ============== Geometrical info Anonymous Assigned ====================== ============== ============== Wyckoff positions only Protopattern Protostructure Geometrical class Prototype Structuretype Exact geometry CrystalPattern Structure ====================== ============== ============== :param cell: The standard-setting cell geometry. :param spacegroup: The standard-setting space group. :param wyckoff_sites: The symmetry-distinct site definitions. :param species: The distinct dummy species definitions. :param coordinate_precision: The precision recorded for the reduced coordinates. """ _cell: Cell _spacegroup: Spacegroup _wyckoff_sites: tuple[WyckoffSite, ...] _species: tuple[Species, ...] _coordinate_precision: Any
[docs] kind: ClassVar[str] = "asu"
def __init__( self, cell: CellLike, spacegroup: Spacegroup | int, wyckoff_sites: Sequence[WyckoffSite], species: Sequence[SpeciesLike] | None = None, coordinate_precision: Any = None, ) -> None: self._cell = cell if isinstance(cell, Cell) else CellView(cell) require_full_periodicity(self._cell, "FundamentalDomainPattern") self._spacegroup = spacegroup if isinstance(spacegroup, Spacegroup) else Spacegroup.standard(spacegroup) if not self._spacegroup.is_standard_setting: raise ValueError( f"FundamentalDomainPattern records Wyckoff data in the IT standard setting, but was given " f"{self._spacegroup.setting}; pass Spacegroup.standard({self._spacegroup.it_number}) " "and express the difference as the transform instead" ) raw_sites = tuple(wyckoff_sites) labels = tuple(site.species for site in raw_sites) if species is None: norm_species = tuple(dummy_species(label) for label in dict.fromkeys(labels)) else: norm_species = tuple(value if isinstance(value, Species) else SpeciesView(value) for value in species) if len({value.name for value in norm_species}) != len(norm_species): raise ValueError("FundamentalDomainPattern species names must be unique") if any(not is_dummy_species(value) for value in norm_species): raise ValueError("FundamentalDomainPattern species must be dummy species") known = {value.name for value in norm_species} for site in raw_sites: if site.representative is not None: raise ValueError("FundamentalDomainPattern WyckoffSite representative is not supported") if site.moment is not None: raise ValueError("FundamentalDomainPattern WyckoffSite moment is not supported") if site.species not in known: raise ValueError( f"FundamentalDomainPattern WyckoffSite references unknown species name: {site.species!r}" ) position = self._spacegroup.wyckoff_position(site.wyckoff) if site.free_count != position.free_count: raise ValueError( f"Wyckoff position {position.multiplicity}{position.letter} of {self._spacegroup.setting} " f"takes {position.free_count} free parameter(s), but the site supplies {site.free_count}" ) expected = {anonymous_symbol(index) for index in range(len(norm_species))} if {value.name for value in norm_species} != expected: raise ValueError("FundamentalDomainPattern species labels must be consecutive anonymous symbols from 'A'") self._wyckoff_sites = tuple( sorted(raw_sites, key=lambda site: (site.species, site.wyckoff, site.free_params.to_fractions())) ) self._species = norm_species from httk.atomistic.models._vector_guards import to_precision self._coordinate_precision = to_precision(coordinate_precision) @property
[docs] def cell(self) -> Cell: """Return the standard-setting cell.""" return self._cell
@property
[docs] def spacegroup(self) -> Spacegroup: """Return the standard-setting space group.""" return self._spacegroup
@property
[docs] def wyckoff_sites(self) -> tuple[WyckoffSite, ...]: """Return the symmetry-distinct site definitions.""" return self._wyckoff_sites
@property
[docs] def species(self) -> tuple[Species, ...]: """Return the distinct dummy species.""" return self._species
@property
[docs] def coordinate_precision(self): """Return the reduced-coordinate precision.""" return self._coordinate_precision
@property
[docs] def basis_precision(self): """Return the cell-basis precision.""" return self._cell.precision
@cached_property def _domain(self) -> FundamentalDomainStructure: return FundamentalDomainStructure( self._cell, self._spacegroup, self._wyckoff_sites, self._species, transform=SettingTransform.identity(), coordinate_precision=self._coordinate_precision, ) @property
[docs] def sites(self) -> Sites: """Return the expanded standard-setting sites.""" return self._domain.sites
@property
[docs] def species_at_sites(self) -> tuple[str, ...]: """Return dummy species names in expanded site order.""" return self._domain.species_at_sites
@property
[docs] def periodicity(self) -> tuple[bool, bool, bool]: """Return the periodic directions.""" return self._cell.periodicity
@property
[docs] def nperiodic_dimensions(self) -> int: """Return the number of periodic directions.""" return self._cell.nperiodic_dimensions
@property
[docs] def nsites(self) -> int: """Return the number of expanded sites.""" return len(self.sites)
[docs] def cartesian_sites(self) -> Any: """Return the exact Cartesian expanded site positions.""" return self._domain.cartesian_sites()
[docs] def multiplicities(self) -> tuple[int, ...]: """Return the standard-setting multiplicity for each Wyckoff site.""" return self._domain.multiplicities()
@property
[docs] def nsites_conventional(self) -> int: """Return the number of sites in the conventional cell.""" return sum(self.multiplicities())
@property
[docs] def prototype(self) -> Self: """Return this prototype value.""" return self
@cached_property
[docs] def protopattern(self) -> "Protopattern": """Return the anonymous protopattern this fundamental domain folds to. The pattern keeps this domain's occupied Wyckoff letters and their dummy-species class partition; the exact geometry is discarded. The pattern's constructor re-canonicalizes the class labels by the pinned group-ordering rule. :return: The folded :class:`~httk.atomistic.models.protopattern.protopattern.Protopattern`. """ from httk.atomistic.models.protopattern.occupation import ProtopatternOccupation from httk.atomistic.models.protopattern.protopattern import Protopattern return Protopattern( self._spacegroup, [ProtopatternOccupation(site.wyckoff, site.species) for site in self._wyckoff_sites], )
def __eq__(self, other: object) -> bool: if not isinstance(other, FundamentalDomainPattern): return NotImplemented return ( self._cell == other._cell and self.basis_precision == other.basis_precision and self._spacegroup == other._spacegroup and self._wyckoff_sites == other._wyckoff_sites and self._species == other._species and self.coordinate_precision == other.coordinate_precision ) def __repr__(self) -> str: return f"FundamentalDomainPattern({self._spacegroup.setting!r}, wyckoff_sites={self._wyckoff_sites!r})"
[docs] class ASUPattern(FundamentalDomainPattern): """Assert that a fundamental-domain pattern is a true asymmetric unit. Mirror :class:`~httk.atomistic.models.structure.asu.ASUStructure`: mark a :class:`FundamentalDomainPattern` whose Wyckoff sites are known to span a real asymmetric unit rather than merely a fundamental domain. The family exposes no ``site_coordinate_span`` hook, so this marker adds no state and inherits the fundamental-domain equality and representation. """