use std::fmt::{Debug, Display};
use laddu_amplitudes::DecayAmplitudeExt;
use laddu_core::{
data::{Dataset, DatasetMetadata, EventLike, OwnedEvent},
reaction::{Decay, Particle, Reaction},
traits::Variable,
variables::{
Angles, CosTheta, IntoP4Selection, Mandelstam, Mass, P4Selection, Phi, PolAngle,
PolMagnitude, Polarization, VariableExpression,
},
LadduResult,
};
use numpy::PyArray1;
use pyo3::{exceptions::PyValueError, prelude::*, types::PyTuple};
use serde::{Deserialize, Serialize};
use crate::{
amplitudes::{py_tags, PyExpression},
data::{PyDataset, PyEvent},
quantum::angular_momentum::{
parse_angular_momentum, parse_orbital_angular_momentum, parse_projection,
},
vectors::PyVec4,
};
#[derive(FromPyObject, Clone, Serialize, Deserialize)]
pub enum PyVariable {
#[pyo3(transparent)]
Mass(PyMass),
#[pyo3(transparent)]
CosTheta(PyCosTheta),
#[pyo3(transparent)]
Phi(PyPhi),
#[pyo3(transparent)]
PolAngle(PyPolAngle),
#[pyo3(transparent)]
PolMagnitude(PyPolMagnitude),
#[pyo3(transparent)]
Mandelstam(PyMandelstam),
}
impl Debug for PyVariable {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Mass(v) => write!(f, "{:?}", v.0),
Self::CosTheta(v) => write!(f, "{:?}", v.0),
Self::Phi(v) => write!(f, "{:?}", v.0),
Self::PolAngle(v) => write!(f, "{:?}", v.0),
Self::PolMagnitude(v) => write!(f, "{:?}", v.0),
Self::Mandelstam(v) => write!(f, "{:?}", v.0),
}
}
}
impl Display for PyVariable {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Mass(v) => write!(f, "{}", v.0),
Self::CosTheta(v) => write!(f, "{}", v.0),
Self::Phi(v) => write!(f, "{}", v.0),
Self::PolAngle(v) => write!(f, "{}", v.0),
Self::PolMagnitude(v) => write!(f, "{}", v.0),
Self::Mandelstam(v) => write!(f, "{}", v.0),
}
}
}
impl PyVariable {
pub(crate) fn bind_in_place(&mut self, metadata: &DatasetMetadata) -> PyResult<()> {
match self {
Self::Mass(mass) => mass.0.bind(metadata).map_err(PyErr::from),
Self::CosTheta(cos_theta) => cos_theta.0.bind(metadata).map_err(PyErr::from),
Self::Phi(phi) => phi.0.bind(metadata).map_err(PyErr::from),
Self::PolAngle(pol_angle) => pol_angle.0.bind(metadata).map_err(PyErr::from),
Self::PolMagnitude(pol_magnitude) => {
pol_magnitude.0.bind(metadata).map_err(PyErr::from)
}
Self::Mandelstam(mandelstam) => mandelstam.0.bind(metadata).map_err(PyErr::from),
}
}
pub(crate) fn bound(&self, metadata: &DatasetMetadata) -> PyResult<Self> {
let mut cloned = self.clone();
cloned.bind_in_place(metadata)?;
Ok(cloned)
}
pub(crate) fn evaluate_event(&self, event: &OwnedEvent) -> PyResult<f64> {
Ok(self.value(event))
}
}
#[pyclass(name = "VariableExpression", module = "laddu")]
pub struct PyVariableExpression(pub VariableExpression);
#[pymethods]
impl PyVariableExpression {
fn __and__(&self, rhs: &PyVariableExpression) -> PyVariableExpression {
PyVariableExpression(self.0.clone() & rhs.0.clone())
}
fn __or__(&self, rhs: &PyVariableExpression) -> PyVariableExpression {
PyVariableExpression(self.0.clone() | rhs.0.clone())
}
fn __invert__(&self) -> PyVariableExpression {
PyVariableExpression(!self.0.clone())
}
fn __str__(&self) -> String {
format!("{}", self.0)
}
}
#[derive(Clone, FromPyObject)]
pub enum PyP4SelectionInput {
#[pyo3(transparent)]
Name(String),
#[pyo3(transparent)]
Names(Vec<String>),
}
impl PyP4SelectionInput {
fn into_selection(self) -> P4Selection {
match self {
PyP4SelectionInput::Name(name) => name.into_selection(),
PyP4SelectionInput::Names(names) => names.into_selection(),
}
}
}
#[pyclass(name = "Particle", module = "laddu", from_py_object)]
#[derive(Clone, Serialize, Deserialize)]
pub struct PyParticle(pub Particle);
#[pymethods]
impl PyParticle {
#[staticmethod]
fn stored(id: &str) -> Self {
Self(Particle::stored(id))
}
#[staticmethod]
fn fixed(label: &str, p4: &PyVec4) -> Self {
Self(Particle::fixed(label, p4.0))
}
#[staticmethod]
fn missing(label: &str) -> Self {
Self(Particle::missing(label))
}
#[staticmethod]
fn composite(label: &str, daughters: &Bound<'_, PyTuple>) -> PyResult<Self> {
if daughters.len() != 2 {
return Err(PyValueError::new_err(
"composite particles require exactly two ordered daughters",
));
}
let daughter_1 = daughters.get_item(0)?.extract::<PyParticle>()?;
let daughter_2 = daughters.get_item(1)?.extract::<PyParticle>()?;
Ok(Self(Particle::composite(
label,
(&daughter_1.0, &daughter_2.0),
)?))
}
#[getter]
fn label(&self) -> String {
self.0.label().to_string()
}
fn __repr__(&self) -> String {
format!("{:?}", self.0)
}
fn __str__(&self) -> String {
self.0.to_string()
}
}
#[pyclass(name = "Reaction", module = "laddu", from_py_object)]
#[derive(Clone, Serialize, Deserialize)]
pub struct PyReaction(pub Reaction);
#[pymethods]
impl PyReaction {
#[staticmethod]
fn two_to_two(
p1: &PyParticle,
p2: &PyParticle,
p3: &PyParticle,
p4: &PyParticle,
) -> PyResult<Self> {
Ok(Self(Reaction::two_to_two(&p1.0, &p2.0, &p3.0, &p4.0)?))
}
fn mass(&self, particle: &str) -> PyMass {
PyMass(self.0.mass(particle))
}
fn decay(&self, parent: &str) -> PyResult<PyDecay> {
Ok(PyDecay(self.0.decay(parent)?))
}
fn mandelstam(&self, channel: &str) -> PyResult<PyMandelstam> {
Ok(PyMandelstam(self.0.mandelstam(channel.parse()?)?))
}
fn pol_angle(&self, angle_aux: String) -> PyPolAngle {
PyPolAngle(self.0.pol_angle(angle_aux))
}
fn polarization(&self, pol_magnitude: String, pol_angle: String) -> PyResult<PyPolarization> {
if pol_magnitude == pol_angle {
return Err(PyValueError::new_err(
"`pol_magnitude` and `pol_angle` must reference distinct auxiliary columns",
));
}
Ok(PyPolarization(
self.0.polarization(pol_magnitude, pol_angle),
))
}
fn __repr__(&self) -> String {
format!("{:?}", self.0)
}
fn __str__(&self) -> String {
format!("{:?}", self.0)
}
}
#[pyclass(name = "Decay", module = "laddu", from_py_object)]
#[derive(Clone, Serialize, Deserialize)]
pub struct PyDecay(pub Decay);
#[pymethods]
impl PyDecay {
#[getter]
fn reaction(&self) -> PyReaction {
PyReaction(self.0.reaction().clone())
}
#[getter]
fn parent(&self) -> String {
self.0.parent().to_string()
}
#[getter]
fn daughter_1(&self) -> String {
self.0.daughter_1().to_string()
}
#[getter]
fn daughter_2(&self) -> String {
self.0.daughter_2().to_string()
}
fn daughters(&self) -> Vec<String> {
self.0.daughters().into_iter().map(str::to_string).collect()
}
fn mass(&self) -> PyMass {
PyMass(self.0.mass())
}
fn parent_mass(&self) -> PyMass {
PyMass(self.0.parent_mass())
}
fn daughter_1_mass(&self) -> PyMass {
PyMass(self.0.daughter_1_mass())
}
fn daughter_2_mass(&self) -> PyMass {
PyMass(self.0.daughter_2_mass())
}
fn daughter_mass(&self, daughter: &str) -> PyResult<PyMass> {
Ok(PyMass(self.0.daughter_mass(daughter)?))
}
#[pyo3(signature=(daughter, frame="Helicity"))]
fn costheta(&self, daughter: &str, frame: &str) -> PyResult<PyCosTheta> {
Ok(PyCosTheta(self.0.costheta(daughter, frame.parse()?)?))
}
#[pyo3(signature=(daughter, frame="Helicity"))]
fn phi(&self, daughter: &str, frame: &str) -> PyResult<PyPhi> {
Ok(PyPhi(self.0.phi(daughter, frame.parse()?)?))
}
#[pyo3(signature=(daughter, frame="Helicity"))]
fn angles(&self, daughter: &str, frame: &str) -> PyResult<PyAngles> {
Ok(PyAngles(self.0.angles(daughter, frame.parse()?)?))
}
#[pyo3(signature=(*tags, spin, projection, daughter, lambda_1, lambda_2, frame="Helicity"))]
#[allow(clippy::too_many_arguments)]
fn helicity_factor(
&self,
tags: &Bound<'_, PyTuple>,
spin: &Bound<'_, PyAny>,
projection: &Bound<'_, PyAny>,
daughter: &str,
lambda_1: &Bound<'_, PyAny>,
lambda_2: &Bound<'_, PyAny>,
frame: &str,
) -> PyResult<PyExpression> {
Ok(PyExpression(self.0.helicity_factor(
py_tags(tags)?,
parse_angular_momentum(spin)?,
parse_projection(projection)?,
daughter,
parse_projection(lambda_1)?,
parse_projection(lambda_2)?,
frame.parse()?,
)?))
}
#[pyo3(signature=(*tags, spin, projection, orbital_l, coupled_spin, daughter, daughter_1_spin, daughter_2_spin, lambda_1, lambda_2, frame="Helicity"))]
#[allow(clippy::too_many_arguments)]
fn canonical_factor(
&self,
tags: &Bound<'_, PyTuple>,
spin: &Bound<'_, PyAny>,
projection: &Bound<'_, PyAny>,
orbital_l: &Bound<'_, PyAny>,
coupled_spin: &Bound<'_, PyAny>,
daughter: &str,
daughter_1_spin: &Bound<'_, PyAny>,
daughter_2_spin: &Bound<'_, PyAny>,
lambda_1: &Bound<'_, PyAny>,
lambda_2: &Bound<'_, PyAny>,
frame: &str,
) -> PyResult<PyExpression> {
Ok(PyExpression(self.0.canonical_factor(
py_tags(tags)?,
parse_angular_momentum(spin)?,
parse_projection(projection)?,
parse_orbital_angular_momentum(orbital_l)?,
parse_angular_momentum(coupled_spin)?,
daughter,
parse_angular_momentum(daughter_1_spin)?,
parse_angular_momentum(daughter_2_spin)?,
parse_projection(lambda_1)?,
parse_projection(lambda_2)?,
frame.parse()?,
)?))
}
fn __repr__(&self) -> String {
format!("{:?}", self.0)
}
fn __str__(&self) -> String {
format!("{:?}", self.0)
}
}
#[pyclass(name = "Mass", module = "laddu", from_py_object)]
#[derive(Clone, Serialize, Deserialize)]
pub struct PyMass(pub Mass);
#[pymethods]
impl PyMass {
#[new]
fn new(constituents: PyP4SelectionInput) -> Self {
Self(Mass::new(constituents.into_selection()))
}
fn value(&self, event: &PyEvent) -> PyResult<f64> {
let metadata = event
.metadata_opt()
.ok_or_else(|| PyValueError::new_err(
"This event is not associated with metadata; supply `p4_names`/`aux_names` when constructing it or evaluate via a Dataset.",
))?;
let mut variable = self.0.clone();
variable.bind(metadata).map_err(PyErr::from)?;
Ok(variable.value(&event.event))
}
fn value_on<'py>(
&self,
py: Python<'py>,
dataset: &PyDataset,
) -> PyResult<Bound<'py, PyArray1<f64>>> {
let values = self.0.value_on(&dataset.0).map_err(PyErr::from)?;
Ok(PyArray1::from_vec(py, values))
}
fn __eq__(&self, value: f64) -> PyVariableExpression {
PyVariableExpression(self.0.eq(value))
}
fn __lt__(&self, value: f64) -> PyVariableExpression {
PyVariableExpression(self.0.lt(value))
}
fn __gt__(&self, value: f64) -> PyVariableExpression {
PyVariableExpression(self.0.gt(value))
}
fn __le__(&self, value: f64) -> PyVariableExpression {
PyVariableExpression(self.0.le(value))
}
fn __ge__(&self, value: f64) -> PyVariableExpression {
PyVariableExpression(self.0.ge(value))
}
fn __repr__(&self) -> String {
format!("{:?}", self.0)
}
fn __str__(&self) -> String {
format!("{}", self.0)
}
}
#[pyclass(name = "CosTheta", module = "laddu", from_py_object)]
#[derive(Clone, Serialize, Deserialize)]
pub struct PyCosTheta(pub CosTheta);
#[pymethods]
impl PyCosTheta {
fn value(&self, event: &PyEvent) -> PyResult<f64> {
let metadata = event
.metadata_opt()
.ok_or_else(|| PyValueError::new_err(
"This event is not associated with metadata; supply `p4_names`/`aux_names` when constructing it or evaluate via a Dataset.",
))?;
let mut variable = self.0.clone();
variable.bind(metadata).map_err(PyErr::from)?;
Ok(variable.value(&event.event))
}
fn value_on<'py>(
&self,
py: Python<'py>,
dataset: &PyDataset,
) -> PyResult<Bound<'py, PyArray1<f64>>> {
let values = self.0.value_on(&dataset.0).map_err(PyErr::from)?;
Ok(PyArray1::from_vec(py, values))
}
fn __eq__(&self, value: f64) -> PyVariableExpression {
PyVariableExpression(self.0.eq(value))
}
fn __lt__(&self, value: f64) -> PyVariableExpression {
PyVariableExpression(self.0.lt(value))
}
fn __gt__(&self, value: f64) -> PyVariableExpression {
PyVariableExpression(self.0.gt(value))
}
fn __le__(&self, value: f64) -> PyVariableExpression {
PyVariableExpression(self.0.le(value))
}
fn __ge__(&self, value: f64) -> PyVariableExpression {
PyVariableExpression(self.0.ge(value))
}
fn __repr__(&self) -> String {
format!("{:?}", self.0)
}
fn __str__(&self) -> String {
format!("{}", self.0)
}
}
#[pyclass(name = "Phi", module = "laddu", from_py_object)]
#[derive(Clone, Serialize, Deserialize)]
pub struct PyPhi(pub Phi);
#[pymethods]
impl PyPhi {
fn value(&self, event: &PyEvent) -> PyResult<f64> {
let metadata = event
.metadata_opt()
.ok_or_else(|| PyValueError::new_err(
"This event is not associated with metadata; supply `p4_names`/`aux_names` when constructing it or evaluate via a Dataset.",
))?;
let mut variable = self.0.clone();
variable.bind(metadata).map_err(PyErr::from)?;
Ok(variable.value(&event.event))
}
fn value_on<'py>(
&self,
py: Python<'py>,
dataset: &PyDataset,
) -> PyResult<Bound<'py, PyArray1<f64>>> {
let values = self.0.value_on(&dataset.0).map_err(PyErr::from)?;
Ok(PyArray1::from_vec(py, values))
}
fn __eq__(&self, value: f64) -> PyVariableExpression {
PyVariableExpression(self.0.eq(value))
}
fn __lt__(&self, value: f64) -> PyVariableExpression {
PyVariableExpression(self.0.lt(value))
}
fn __gt__(&self, value: f64) -> PyVariableExpression {
PyVariableExpression(self.0.gt(value))
}
fn __le__(&self, value: f64) -> PyVariableExpression {
PyVariableExpression(self.0.le(value))
}
fn __ge__(&self, value: f64) -> PyVariableExpression {
PyVariableExpression(self.0.ge(value))
}
fn __repr__(&self) -> String {
format!("{:?}", self.0)
}
fn __str__(&self) -> String {
format!("{}", self.0)
}
}
#[pyclass(name = "Angles", module = "laddu", skip_from_py_object)]
#[derive(Clone)]
pub struct PyAngles(pub Angles);
#[pymethods]
impl PyAngles {
#[getter]
fn costheta(&self) -> PyCosTheta {
PyCosTheta(self.0.costheta.clone())
}
#[getter]
fn phi(&self) -> PyPhi {
PyPhi(self.0.phi.clone())
}
fn __repr__(&self) -> String {
format!("{:?}", self.0)
}
fn __str__(&self) -> String {
format!("{}", self.0)
}
}
#[pyclass(name = "PolAngle", module = "laddu", from_py_object)]
#[derive(Clone, Serialize, Deserialize)]
pub struct PyPolAngle(pub PolAngle);
#[pymethods]
impl PyPolAngle {
#[new]
fn new(reaction: PyReaction, pol_angle: String) -> Self {
Self(PolAngle::new(reaction.0.clone(), pol_angle))
}
fn value(&self, event: &PyEvent) -> PyResult<f64> {
let metadata = event
.metadata_opt()
.ok_or_else(|| PyValueError::new_err(
"This event is not associated with metadata; supply `p4_names`/`aux_names` when constructing it or evaluate via a Dataset.",
))?;
let mut variable = self.0.clone();
variable.bind(metadata).map_err(PyErr::from)?;
Ok(variable.value(&event.event))
}
fn value_on<'py>(
&self,
py: Python<'py>,
dataset: &PyDataset,
) -> PyResult<Bound<'py, PyArray1<f64>>> {
let values = self.0.value_on(&dataset.0).map_err(PyErr::from)?;
Ok(PyArray1::from_vec(py, values))
}
fn __eq__(&self, value: f64) -> PyVariableExpression {
PyVariableExpression(self.0.eq(value))
}
fn __lt__(&self, value: f64) -> PyVariableExpression {
PyVariableExpression(self.0.lt(value))
}
fn __gt__(&self, value: f64) -> PyVariableExpression {
PyVariableExpression(self.0.gt(value))
}
fn __le__(&self, value: f64) -> PyVariableExpression {
PyVariableExpression(self.0.le(value))
}
fn __ge__(&self, value: f64) -> PyVariableExpression {
PyVariableExpression(self.0.ge(value))
}
fn __repr__(&self) -> String {
format!("{:?}", self.0)
}
fn __str__(&self) -> String {
format!("{}", self.0)
}
}
#[pyclass(name = "PolMagnitude", module = "laddu", from_py_object)]
#[derive(Clone, Serialize, Deserialize)]
pub struct PyPolMagnitude(pub PolMagnitude);
#[pymethods]
impl PyPolMagnitude {
#[new]
fn new(pol_magnitude: String) -> Self {
Self(PolMagnitude::new(pol_magnitude))
}
fn value(&self, event: &PyEvent) -> PyResult<f64> {
let metadata = event
.metadata_opt()
.ok_or_else(|| PyValueError::new_err(
"This event is not associated with metadata; supply `p4_names`/`aux_names` when constructing it or evaluate via a Dataset.",
))?;
let mut variable = self.0.clone();
variable.bind(metadata).map_err(PyErr::from)?;
Ok(variable.value(&event.event))
}
fn value_on<'py>(
&self,
py: Python<'py>,
dataset: &PyDataset,
) -> PyResult<Bound<'py, PyArray1<f64>>> {
let values = self.0.value_on(&dataset.0).map_err(PyErr::from)?;
Ok(PyArray1::from_vec(py, values))
}
fn __eq__(&self, value: f64) -> PyVariableExpression {
PyVariableExpression(self.0.eq(value))
}
fn __lt__(&self, value: f64) -> PyVariableExpression {
PyVariableExpression(self.0.lt(value))
}
fn __gt__(&self, value: f64) -> PyVariableExpression {
PyVariableExpression(self.0.gt(value))
}
fn __le__(&self, value: f64) -> PyVariableExpression {
PyVariableExpression(self.0.le(value))
}
fn __ge__(&self, value: f64) -> PyVariableExpression {
PyVariableExpression(self.0.ge(value))
}
fn __repr__(&self) -> String {
format!("{:?}", self.0)
}
fn __str__(&self) -> String {
format!("{}", self.0)
}
}
#[pyclass(name = "Polarization", module = "laddu", skip_from_py_object)]
#[derive(Clone)]
pub struct PyPolarization(pub Polarization);
#[pymethods]
impl PyPolarization {
#[new]
#[pyo3(signature=(reaction, *, pol_magnitude, pol_angle))]
fn new(reaction: PyReaction, pol_magnitude: String, pol_angle: String) -> PyResult<Self> {
if pol_magnitude == pol_angle {
return Err(PyValueError::new_err(
"`pol_magnitude` and `pol_angle` must reference distinct auxiliary columns",
));
}
let polarization = Polarization::new(reaction.0.clone(), pol_magnitude, pol_angle);
Ok(PyPolarization(polarization))
}
#[getter]
fn pol_magnitude(&self) -> PyPolMagnitude {
PyPolMagnitude(self.0.pol_magnitude.clone())
}
#[getter]
fn pol_angle(&self) -> PyPolAngle {
PyPolAngle(self.0.pol_angle.clone())
}
fn __repr__(&self) -> String {
format!("{:?}", self.0)
}
fn __str__(&self) -> String {
format!("{}", self.0)
}
}
#[pyclass(name = "Mandelstam", module = "laddu", from_py_object)]
#[derive(Clone, Serialize, Deserialize)]
pub struct PyMandelstam(pub Mandelstam);
#[pymethods]
impl PyMandelstam {
#[new]
fn new(reaction: PyReaction, channel: &str) -> PyResult<Self> {
Ok(Self(reaction.0.mandelstam(channel.parse()?)?))
}
fn value(&self, event: &PyEvent) -> PyResult<f64> {
let metadata = event
.metadata_opt()
.ok_or_else(|| PyValueError::new_err(
"This event is not associated with metadata; supply `p4_names`/`aux_names` when constructing it or evaluate via a Dataset.",
))?;
let mut variable = self.0.clone();
variable.bind(metadata).map_err(PyErr::from)?;
Ok(variable.value(&event.event))
}
fn value_on<'py>(
&self,
py: Python<'py>,
dataset: &PyDataset,
) -> PyResult<Bound<'py, PyArray1<f64>>> {
let values = self.0.value_on(&dataset.0).map_err(PyErr::from)?;
Ok(PyArray1::from_vec(py, values))
}
fn __eq__(&self, value: f64) -> PyVariableExpression {
PyVariableExpression(self.0.eq(value))
}
fn __lt__(&self, value: f64) -> PyVariableExpression {
PyVariableExpression(self.0.lt(value))
}
fn __gt__(&self, value: f64) -> PyVariableExpression {
PyVariableExpression(self.0.gt(value))
}
fn __le__(&self, value: f64) -> PyVariableExpression {
PyVariableExpression(self.0.le(value))
}
fn __ge__(&self, value: f64) -> PyVariableExpression {
PyVariableExpression(self.0.ge(value))
}
fn __repr__(&self) -> String {
format!("{:?}", self.0)
}
fn __str__(&self) -> String {
format!("{}", self.0)
}
}
#[typetag::serde]
impl Variable for PyVariable {
fn bind(&mut self, metadata: &DatasetMetadata) -> LadduResult<()> {
match self {
PyVariable::Mass(mass) => mass.0.bind(metadata),
PyVariable::CosTheta(cos_theta) => cos_theta.0.bind(metadata),
PyVariable::Phi(phi) => phi.0.bind(metadata),
PyVariable::PolAngle(pol_angle) => pol_angle.0.bind(metadata),
PyVariable::PolMagnitude(pol_magnitude) => pol_magnitude.0.bind(metadata),
PyVariable::Mandelstam(mandelstam) => mandelstam.0.bind(metadata),
}
}
fn value_on(&self, dataset: &Dataset) -> LadduResult<Vec<f64>> {
match self {
PyVariable::Mass(mass) => mass.0.value_on(dataset),
PyVariable::CosTheta(cos_theta) => cos_theta.0.value_on(dataset),
PyVariable::Phi(phi) => phi.0.value_on(dataset),
PyVariable::PolAngle(pol_angle) => pol_angle.0.value_on(dataset),
PyVariable::PolMagnitude(pol_magnitude) => pol_magnitude.0.value_on(dataset),
PyVariable::Mandelstam(mandelstam) => mandelstam.0.value_on(dataset),
}
}
fn value(&self, event: &dyn EventLike) -> f64 {
match self {
PyVariable::Mass(mass) => mass.0.value(event),
PyVariable::CosTheta(cos_theta) => cos_theta.0.value(event),
PyVariable::Phi(phi) => phi.0.value(event),
PyVariable::PolAngle(pol_angle) => pol_angle.0.value(event),
PyVariable::PolMagnitude(pol_magnitude) => pol_magnitude.0.value(event),
PyVariable::Mandelstam(mandelstam) => mandelstam.0.value(event),
}
}
}