Source code for polarimetry.decay

"""Data structures that describe a three-body decay."""
from __future__ import annotations

import sys
from typing import Dict

import sympy as sp
from attrs import field, frozen
from attrs.validators import instance_of

from polarimetry._attrs import assert_spin_value, to_ls, to_rational

if sys.version_info < (3, 8):
    from typing_extensions import Literal
else:
    from typing import Literal


[docs]@frozen class Particle: name: str latex: str spin: sp.Rational = field(converter=to_rational, validator=assert_spin_value) parity: Literal[-1, 1] mass: float width: float
[docs]@frozen class IsobarNode: parent: Particle child1: Particle | IsobarNode child2: Particle | IsobarNode interaction: LSCoupling | None = field(default=None, converter=to_ls) @property def children(self) -> tuple[Particle, Particle]: return self.child1, self.child2
[docs]@frozen class ThreeBodyDecay: states: OuterStates chains: tuple[ThreeBodyDecayChain, ...] def __attrs_post_init__(self) -> None: expected_initial_state = self.initial_state expected_final_state = set(self.final_state.values()) for i, chain in enumerate(self.chains): if chain.parent != expected_initial_state: raise ValueError( f"Chain {i} has initial state {chain.parent.name}, but should have" f" {expected_initial_state.name}" ) final_state = {chain.spectator, *chain.decay_products} if final_state != expected_final_state: to_str = lambda s: ", ".join(p.name for p in s) raise ValueError( f"Chain {i} has final state {to_str(final_state)}, but should have" f" {to_str(expected_final_state)}" ) @property def initial_state(self) -> Particle: return self.states[0] @property def final_state(self) -> dict[Literal[1, 2, 3], Particle]: return {k: v for k, v in self.states.items() if k != 0}
[docs] def find_chain(self, resonance_name: str) -> ThreeBodyDecayChain: for chain in self.chains: if chain.resonance.name == resonance_name: return chain raise KeyError(f"No decay chain found for resonance {resonance_name}")
[docs] def get_subsystem(self, subsystem_id: Literal[1, 2, 3]) -> ThreeBodyDecay: child1_id, child2_id = get_decay_product_ids(subsystem_id) child1 = self.final_state[child1_id] child2 = self.final_state[child2_id] filtered_chains = [ chain for chain in self.chains if chain.decay_products in {(child1, child2), (child2, child1)} ] return ThreeBodyDecay(self.states, filtered_chains)
[docs]def get_decay_product_ids(spectator_id: Literal[1, 2, 3]) -> tuple[int, int]: if spectator_id == 1: return 2, 3 if spectator_id == 2: return 3, 1 if spectator_id == 3: return 1, 2 raise ValueError(f"Spectator ID has to be one of 1, 2, 3, not {spectator_id}")
OuterStates = Dict[Literal[0, 1, 2, 3], Particle] """Mapping of the initial and final state IDs to their `.Particle` definition."""
[docs]@frozen class ThreeBodyDecayChain: decay: IsobarNode = field(validator=instance_of(IsobarNode)) def __attrs_post_init__(self) -> None: if not isinstance(self.decay.child1, IsobarNode): raise TypeError(f"Child 1 has of type {IsobarNode.__name__} (the decay)") if not isinstance(self.decay.child1.child1, Particle): raise TypeError(f"Child 1 of child 1 has of type {Particle.__name__}") if not isinstance(self.decay.child1.child1, Particle): raise TypeError(f"Child 1 of child 1 has of type {Particle.__name__}") if not isinstance(self.decay.child2, Particle): raise TypeError(f"Child 2 has of type {Particle.__name__} (spectator)") if self.incoming_ls is None: raise ValueError(f"LS-coupling for production node required") if self.outgoing_ls is None: raise ValueError(f"LS-coupling for decay node required") @property def parent(self) -> Particle: return self.decay.parent @property def resonance(self) -> Particle: return self.decay.child1.parent @property def decay_products(self) -> tuple[Particle, Particle]: return ( self.decay.child1.child1, self.decay.child1.child2, ) @property def spectator(self) -> Particle: return self.decay.child2 @property def incoming_ls(self) -> LSCoupling: return self.decay.interaction @property def outgoing_ls(self) -> LSCoupling: return self.decay.child1.interaction
[docs]@frozen class LSCoupling: L: int S: sp.Rational = field(converter=to_rational, validator=assert_spin_value)