Source code for httk.atomistic.models.formula.anonymous_view
"""Eager anonymous-formula presentation view."""
from typing import TYPE_CHECKING, Any, Self, cast
from httk.core import unwrap
from httk.atomistic.models.formula.anonymous import AnonymousFormula
from httk.atomistic.models.formula.backend import ChemicalFormulaBackend
from httk.atomistic.models.formula.notation import (
anonymous_symbol,
parse_anonymous_formula,
reduced_coefficients,
render_anonymous,
)
from httk.atomistic.models.formula.view_base import ChemicalFormulaViewBase
if TYPE_CHECKING:
from httk.atomistic.models.formula.like import ChemicalFormulaLike
[docs]
class AnonymousFormulaView(ChemicalFormulaViewBase, AnonymousFormula):
r"""Present a complete composition as an eager canonical anonymous formula.
:param obj: The chemical-formula-like object to present.
:param \*\*hints: Backend-selection hints.
"""
_backend: ChemicalFormulaBackend
def __new__(cls, obj: "ChemicalFormulaLike", **hints: Any) -> Self:
if isinstance(obj, cls):
return obj
backend = cls._prepare_backend(obj, hints)
if backend.is_anonymous:
if type(backend) is AnonymousFormula:
text = str(backend)
coefficients = backend._coefficients
else:
amounts = backend.amounts
if not amounts:
raise ValueError("an empty composition cannot yield an anonymous formula")
reduced = cast(
tuple[int, ...],
reduced_coefficients(tuple(value for _, value in amounts)),
)
coefficients = tuple((label, count) for (label, _), count in zip(amounts, reduced))
text = "".join(label + (str(count) if count != 1 else "") for label, count in coefficients)
coefficients = parse_anonymous_formula(text)
else:
if not backend.complete:
raise ValueError(
'composition is incomplete — species with unknown chemical symbol "X" cannot yield a formula'
)
amounts = backend.amounts
if not amounts:
raise ValueError("an empty composition cannot yield a formula")
reduced = cast(tuple[int, ...], reduced_coefficients(tuple(value for _, value in amounts)))
reduced_by_element = dict(zip((element for element, _ in amounts), reduced))
ordered = sorted(amounts, key=lambda item: (-item[1], item[0]))
coefficients = tuple(
(anonymous_symbol(index), reduced_by_element[element]) for index, (element, _) in enumerate(ordered)
)
text = render_anonymous(tuple(count for _, count in coefficients))
coefficients = parse_anonymous_formula(text)
instance = str.__new__(cls, text)
instance._coefficients = coefficients
instance._backend = backend
return instance
def __init__(self, obj: "ChemicalFormulaLike", **hints: Any) -> None:
pass
@property
[docs]
def amounts(self):
"""Return the presented amounts using anonymous labels."""
backend = self._backend
if backend.is_anonymous:
return backend.amounts
ordered = sorted(backend.amounts, key=lambda item: (-item[1], item[0]))
return tuple((anonymous_symbol(index), amount) for index, (_, amount) in enumerate(ordered))
@property
[docs]
def uncertainties(self):
"""Return the presented amount precisions using anonymous labels."""
backend = self._backend
if backend.is_anonymous:
return backend.uncertainties
uncertainties = dict(backend.uncertainties)
ordered = sorted(backend.amounts, key=lambda item: (-item[1], item[0]))
return tuple((anonymous_symbol(index), uncertainties[element]) for index, (element, _) in enumerate(ordered))
@property
[docs]
def complete(self):
"""Return whether the presented composition is complete."""
return self._backend.complete
@property
[docs]
def exact(self):
"""Return whether the presented amounts are exact."""
return self._backend.exact
@property
[docs]
def normalized(self):
"""Return whether the presented composition is normalized."""
return self._backend.normalized
@property
[docs]
def normalization_status(self):
"""Return the presented composition's normalization status."""
return self._backend.normalization_status
@property
[docs]
def diagnostics(self):
"""Return diagnostics associated with the presented composition."""
return self._backend.diagnostics
@property
[docs]
def unview(self) -> AnonymousFormula:
"""Return the presented formula as a standalone value.
:return: The canonical anonymous formula value.
"""
backend = self._backend
if type(backend) is AnonymousFormula:
return backend
return AnonymousFormula(str(self))
[docs]
def unwrap(self) -> Any:
"""Return the raw object behind the backend.
:return: The unwrapped source object.
"""
return unwrap(self._backend)