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#[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 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 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#[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 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 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#[derive(Clone, Debug, Serialize, Deserialize)]
200pub struct Angles {
201 pub costheta: CosTheta,
203 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 pub fn costheta_variable(&self) -> Box<dyn Variable> {
216 Box::new(self.costheta.clone())
217 }
218
219 pub fn phi_variable(&self) -> Box<dyn Variable> {
221 Box::new(self.phi.clone())
222 }
223
224 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 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}