"""A collection of data containers."""
from collections import abc
from typing import (
Dict,
ItemsView,
Iterator,
KeysView,
Optional,
Union,
ValuesView,
)
from typing import NamedTuple
[docs]class Parity:
"""Safe, immutable data container for parity."""
def __init__(self, value: Union[float, int, str]) -> None:
value = float(value)
if value not in [-1.0, +1.0]:
raise ValueError("Parity can only be +1 or -1")
self.__value: int = int(value)
[docs] def __eq__(self, other: object) -> bool:
if isinstance(other, Parity):
return self.__value == other.value
return self.__value == other
def __int__(self) -> int:
return self.value
def __repr__(self) -> str:
return (
f'<{self.__class__.__name__} {"+1" if self.__value > 0 else "-1"}>'
)
@property
def value(self) -> int:
return self.__value
[docs]class Spin:
"""Safe, immutable data container for (iso)spin."""
def __init__(self, magnitude: float, projection: float) -> None:
if abs(projection) > magnitude:
raise ValueError(
"Spin projection cannot be larger than its magnitude"
)
self.__magnitude = float(magnitude)
self.__projection = float(projection)
[docs] def __eq__(self, other: object) -> bool:
if isinstance(other, Spin):
return self.__magnitude == other.magnitude
return self.__magnitude == other
def __float__(self) -> float:
return self.__magnitude
def __repr__(self) -> str:
return f"<{self.__class__.__name__} {self.__magnitude, self.__projection}>"
@property
def magnitude(self) -> float:
return self.__magnitude
@property
def projection(self) -> float:
return self.__projection
[docs]class MeasuredValue(NamedTuple):
"""Value with (optional) uncertainty, as reported by a measurement."""
value: float
uncertainty: Optional[float] = None
[docs] def __eq__(self, other: object) -> bool:
if isinstance(other, MeasuredValue):
return self.value == other.value
return self.value == other
def __float__(self) -> float:
return self.value
def __repr__(self) -> str:
if self.uncertainty is None:
return str(self.value)
return f"{self.value} ± {self.uncertainty}"
[docs]class Particle(NamedTuple):
"""Immutable data container for particle info."""
name: str
pid: int
charge: float
spin: float
mass: MeasuredValue
strangeness: int = 0
charmness: int = 0
bottomness: int = 0
topness: int = 0
baryon_number: int = 0
electron_number: int = 0
muon_number: int = 0
tau_number: int = 0
width: Optional[MeasuredValue] = None
isospin: Optional[Spin] = None
parity: Optional[Parity] = None
c_parity: Optional[Parity] = None
g_parity: Optional[Parity] = None
[docs]class ParticleCollection(abc.Mapping):
"""Safe, `dict`-like collection of `.Particle` instances."""
def __init__(self) -> None:
self.__particles: Dict[str, Particle] = dict()
def __getitem__(self, particle_name: str) -> Particle:
return self.__particles[particle_name]
def __contains__(self, particle_name: object) -> bool:
return particle_name in self.__particles
def __iter__(self) -> Iterator[str]:
return self.__particles.__iter__()
def __len__(self) -> int:
return len(self.__particles)
def __repr__(self) -> str:
return str(self.__particles)
[docs] def add(self, particle: Particle) -> None:
self.__particles[particle.name] = particle
[docs] def items(self) -> ItemsView[str, Particle]:
return self.__particles.items()
[docs] def keys(self) -> KeysView[str]:
return self.__particles.keys()
[docs] def values(self) -> ValuesView[Particle]:
return self.__particles.values()