Skip to main content

laddu_core/variables/
angles.rs

1use std::fmt::Display;
2
3use serde::{Deserialize, Serialize};
4
5use super::Variable;
6use crate::{
7    data::{DatasetMetadata, EventLike},
8    quantum::Frame,
9    reaction::Reaction,
10    LadduResult,
11};
12
13#[derive(Clone, Debug, Serialize, Deserialize)]
14enum AngleSource {
15    Decay {
16        reaction: Box<Reaction>,
17        parent: String,
18        daughter: String,
19        frame: Frame,
20    },
21    Production {
22        reaction: Box<Reaction>,
23        produced: String,
24    },
25}
26
27impl AngleSource {
28    fn bind(&mut self, metadata: &DatasetMetadata) -> LadduResult<()> {
29        let _ = metadata;
30        Ok(())
31    }
32
33    fn costheta(&self, event: &dyn EventLike) -> f64 {
34        match self {
35            Self::Decay {
36                reaction,
37                parent,
38                daughter,
39                frame,
40            } => reaction
41                .angles_value(event, parent, daughter, *frame)
42                .unwrap_or_else(|err| panic!("failed to evaluate reaction costheta: {err}"))
43                .costheta(),
44            Self::Production { reaction, produced } => reaction
45                .production_angles_value(event, produced)
46                .unwrap_or_else(|err| panic!("failed to evaluate production costheta: {err}"))
47                .costheta(),
48        }
49    }
50
51    fn phi(&self, event: &dyn EventLike) -> f64 {
52        match self {
53            Self::Decay {
54                reaction,
55                parent,
56                daughter,
57                frame,
58            } => reaction
59                .angles_value(event, parent, daughter, *frame)
60                .unwrap_or_else(|err| panic!("failed to evaluate reaction phi: {err}"))
61                .phi(),
62            Self::Production { reaction, produced } => reaction
63                .production_angles_value(event, produced)
64                .unwrap_or_else(|err| panic!("failed to evaluate production phi: {err}"))
65                .phi(),
66        }
67    }
68
69    fn label(&self, kind: &str) -> String {
70        match self {
71            Self::Decay {
72                parent,
73                daughter,
74                frame,
75                ..
76            } => format!("{kind}(parent={parent}, daughter={daughter}, frame={frame})"),
77            Self::Production { produced, .. } => format!("{kind}(produced={produced})"),
78        }
79    }
80
81    fn angles_label(&self) -> String {
82        match self {
83            Self::Decay {
84                parent,
85                daughter,
86                frame,
87                ..
88            } => format!("Angles(parent={parent}, daughter={daughter}, frame={frame})"),
89            Self::Production { produced, .. } => format!("Angles(produced={produced})"),
90        }
91    }
92}
93
94/// A struct for obtaining the cosine of the polar angle of a decay product in a given frame of its parent resonance.
95#[derive(Clone, Debug, Serialize, Deserialize)]
96pub struct CosTheta {
97    source: AngleSource,
98}
99
100impl Display for CosTheta {
101    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
102        write!(f, "{}", self.source.label("CosTheta"))
103    }
104}
105
106impl CosTheta {
107    /// Construct an angle for a reaction daughter in the specified parent frame.
108    pub fn from_reaction(
109        reaction: Reaction,
110        parent: impl Into<String>,
111        daughter: impl Into<String>,
112        frame: Frame,
113    ) -> Self {
114        Self {
115            source: AngleSource::Decay {
116                reaction: Box::new(reaction),
117                parent: parent.into(),
118                daughter: daughter.into(),
119                frame,
120            },
121        }
122    }
123
124    /// Construct an angle for the produced system.
125    pub fn from_production(reaction: Reaction, produced: impl Into<String>) -> Self {
126        Self {
127            source: AngleSource::Production {
128                reaction: Box::new(reaction),
129                produced: produced.into(),
130            },
131        }
132    }
133}
134
135#[typetag::serde]
136impl Variable for CosTheta {
137    fn bind(&mut self, metadata: &DatasetMetadata) -> LadduResult<()> {
138        self.source.bind(metadata)
139    }
140
141    fn value(&self, event: &dyn EventLike) -> f64 {
142        self.source.costheta(event)
143    }
144}
145
146/// A struct for obtaining the azimuthal angle of a decay product in a given frame of its parent resonance.
147#[derive(Clone, Debug, Serialize, Deserialize)]
148pub struct Phi {
149    source: AngleSource,
150}
151
152impl Display for Phi {
153    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
154        write!(f, "{}", self.source.label("Phi"))
155    }
156}
157
158impl Phi {
159    /// Construct an angle for a reaction daughter in the specified parent frame.
160    pub fn from_reaction(
161        reaction: Reaction,
162        parent: impl Into<String>,
163        daughter: impl Into<String>,
164        frame: Frame,
165    ) -> Self {
166        Self {
167            source: AngleSource::Decay {
168                reaction: Box::new(reaction),
169                parent: parent.into(),
170                daughter: daughter.into(),
171                frame,
172            },
173        }
174    }
175
176    /// Construct an angle for the produced system.
177    pub fn from_production(reaction: Reaction, produced: impl Into<String>) -> Self {
178        Self {
179            source: AngleSource::Production {
180                reaction: Box::new(reaction),
181                produced: produced.into(),
182            },
183        }
184    }
185}
186
187#[typetag::serde]
188impl Variable for Phi {
189    fn bind(&mut self, metadata: &DatasetMetadata) -> LadduResult<()> {
190        self.source.bind(metadata)
191    }
192
193    fn value(&self, event: &dyn EventLike) -> f64 {
194        self.source.phi(event)
195    }
196}
197
198/// A struct for obtaining both spherical angles at the same time.
199#[derive(Clone, Debug, Serialize, Deserialize)]
200pub struct Angles {
201    /// See [`CosTheta`].
202    pub costheta: CosTheta,
203    /// See [`Phi`].
204    pub phi: Phi,
205}
206
207impl Display for Angles {
208    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
209        write!(f, "{}", self.costheta.source.angles_label())
210    }
211}
212
213impl Angles {
214    /// Return the variable used for `cos(theta)`.
215    pub fn costheta_variable(&self) -> Box<dyn Variable> {
216        Box::new(self.costheta.clone())
217    }
218
219    /// Return the variable used for `phi`.
220    pub fn phi_variable(&self) -> Box<dyn Variable> {
221        Box::new(self.phi.clone())
222    }
223
224    /// Construct reaction-derived angle variables for a daughter in its parent frame.
225    pub fn from_reaction(
226        reaction: Reaction,
227        parent: impl Into<String>,
228        daughter: impl Into<String>,
229        frame: Frame,
230    ) -> Self {
231        let parent = parent.into();
232        let daughter = daughter.into();
233        let costheta =
234            CosTheta::from_reaction(reaction.clone(), parent.clone(), daughter.clone(), frame);
235        let phi = Phi::from_reaction(reaction, parent, daughter, frame);
236        Self { costheta, phi }
237    }
238
239    /// Construct reaction-derived production angle variables.
240    pub fn from_production(reaction: Reaction, produced: impl Into<String>) -> Self {
241        let produced = produced.into();
242        let costheta = CosTheta::from_production(reaction.clone(), produced.clone());
243        let phi = Phi::from_production(reaction, produced);
244        Self { costheta, phi }
245    }
246}