1use std::{fmt::Display, num::ParseIntError, str::FromStr};
2
3use factorial::Factorial;
4use rustitude_core::{convert, prelude::*};
5use sphrs::Coordinates;
6use thiserror::Error;
7
8pub fn breakup_momentum<F: Field>(m0: F, m1: F, m2: F) -> F {
9 F::sqrt(F::abs(
10 m0.powi(4) + m1.powi(4) + m2.powi(4)
11 - convert!(2, F)
12 * (m0.powi(2) * m1.powi(2) + m0.powi(2) * m2.powi(2) + m1.powi(2) * m2.powi(2)),
13 )) / (convert!(2, F) * m0)
14}
15
16pub fn breakup_momentum_c<F: Field>(m0: F, m1: F, m2: F) -> Complex<F> {
19 rho(m0.powi(2), m1, m2) * m0 / convert!(2, F)
20}
21
22pub fn chi_plus<F: Field>(s: F, m1: F, m2: F) -> Complex<F> {
23 Complex::from(F::one() - ((m1 + m2) * (m1 + m2)) / s)
24}
25
26pub fn chi_minus<F: Field>(s: F, m1: F, m2: F) -> Complex<F> {
27 Complex::from(F::one() - ((m1 - m2) * (m1 - m2)) / s)
28}
29
30pub fn rho<F: Field>(s: F, m1: F, m2: F) -> Complex<F> {
31 Complex::sqrt(chi_plus(s, m1, m2) * chi_minus(s, m1, m2))
32}
33
34pub fn blatt_weisskopf<F: Field>(m0: F, m1: F, m2: F, l: usize) -> F {
35 let q = breakup_momentum(m0, m1, m2);
36 let z = q.powi(2) / convert!(0.1973, F).powi(2);
37 match l {
38 0 => F::one(),
39 1 => F::sqrt((convert!(2, F) * z) / (z + F::one())),
40 2 => F::sqrt(
41 (convert!(13.0, F) * z.powi(2)) / ((z - convert!(3, F)).powi(2) + convert!(9, F) * z),
42 ),
43 3 => F::sqrt(
44 (convert!(277.0, F) * z.powi(3))
45 / (z * (z - convert!(15.0, F)).powi(2)
46 + convert!(9, F) * (convert!(2, F) * z - convert!(5, F)).powi(2)),
47 ),
48 4 => F::sqrt(
49 (convert!(12746.0, F) * z.powi(4))
50 / (z.powi(2) - convert!(45.0, F) * z + convert!(105.0, F)).powi(2)
51 + convert!(25.0, F) * z * (convert!(2, F) * z - convert!(21.0, F)).powi(2),
52 ),
53 l => panic!("L = {l} is not yet implemented"),
54 }
55}
56
57pub fn blatt_weisskopf_c<F: Field>(m0: F, m1: F, m2: F, l: usize) -> Complex<F> {
63 let q = breakup_momentum_c(m0, m1, m2);
64 let z = q.powi(2) / convert!(0.1973, F).powi(2);
65 match l {
66 0 => Complex::from(F::one()),
67 1 => Complex::sqrt((Complex::from(convert!(2, F)) * z) / (z + F::one())),
68 2 => Complex::sqrt(
69 (z.powi(2) * convert!(13.0, F)) / ((z - convert!(3, F)).powi(2) + z * convert!(9, F)),
70 ),
71 3 => Complex::sqrt(
72 (z.powi(3) * convert!(277.0, F))
73 / (z * (z - convert!(15.0, F)).powi(2)
74 + (z * convert!(2, F) - convert!(5, F)).powi(2))
75 * convert!(9, F),
76 ),
77 4 => Complex::sqrt(
78 (z.powi(4) * convert!(12746.0, F))
79 / (z.powi(2) - z * convert!(45.0, F) + convert!(105.0, F)).powi(2)
80 + z * convert!(25.0, F) * (z * convert!(2, F) - convert!(21.0, F)).powi(2),
81 ),
82 l => panic!("L = {l} is not yet implemented"),
83 }
84}
85
86pub fn small_wigner_d_matrix<F: Field>(beta: F, j: usize, m: isize, n: isize) -> F {
87 let jpm = (j as i32 + m as i32) as u32;
88 let jmm = (j as i32 - m as i32) as u32;
89 let jpn = (j as i32 + n as i32) as u32;
90 let jmn = (j as i32 - n as i32) as u32;
91 let prefactor = F::sqrt(convert!(
92 jpm.factorial() * jmm.factorial() * jpn.factorial() * jmn.factorial(),
93 F
94 ));
95 let s_min = isize::max(0, n - m) as usize;
96 let s_max = isize::min(jpn as isize, jmm as isize) as usize;
97 let sum: F = (s_min..=s_max)
98 .map(|s| {
99 (F::powi(-F::one(), m as i32 - n as i32 + s as i32)
100 * (F::cos(beta / convert!(2, F))
101 .powi(2 * (j as i32) + n as i32 - m as i32 - 2 * (s as i32)))
102 * (F::sin(beta / convert!(2, F)).powi(m as i32 - n as i32 + 2 * s as i32)))
103 / convert!(
104 (jpm - s as u32).factorial()
105 * (s as u32).factorial()
106 * ((m - n + s as isize) as u32).factorial()
107 * (jmm - s as u32).factorial(),
108 F
109 )
110 })
111 .sum();
112 prefactor * sum
113}
114
115pub fn wigner_d_matrix<F: Field>(
116 alpha: F,
117 beta: F,
118 gamma: F,
119 j: usize,
120 m: isize,
121 n: isize,
122) -> Complex<F> {
123 Complex::cis(convert!(-m, F) * alpha)
124 * small_wigner_d_matrix(beta, j, m, n)
125 * Complex::cis(convert!(-n, F) * gamma)
126}
127
128#[derive(Clone, Copy, Default, PartialEq)]
129#[rustfmt::skip]
130pub enum Wave {
131 #[default]
132 S,
133 S0,
134 Pn1, P0, P1, P,
135 Dn2, Dn1, D0, D1, D2, D,
136 Fn3, Fn2, Fn1, F0, F1, F2, F3, F,
137}
138
139#[rustfmt::skip]
140impl Wave {
141 pub fn new(l: usize, m: isize) -> Self {
142 match l {
143 0 => Self::S0,
144 1 => match m {
145 -1 => Self::Pn1,
146 0 => Self::P0,
147 1 => Self::P1,
148 _ => panic!("|m = {m}| > (l = {l})")
149 }
150 2 => match m {
151 -2 => Self::Dn2,
152 -1 => Self::Dn1,
153 0 => Self::D0,
154 1 => Self::D1,
155 2 => Self::D2,
156 _ => panic!("|m = {m}| > (l = {l})")
157 }
158 3 => match m {
159 -3 => Self::Fn3,
160 -2 => Self::Fn2,
161 -1 => Self::Fn1,
162 0 => Self::F0,
163 1 => Self::F1,
164 2 => Self::F2,
165 3 => Self::F3,
166 _ => panic!("|m = {m}| > (l = {l})")
167 }
168 _ => panic!("(l = {l}) > 3 is not yet implemented!")
169 }
170 }
171 pub fn l(&self) -> i64 {
172 match self {
173 Self::S0 | Self::S => 0,
174 Self::Pn1 | Self::P0 | Self::P1 | Self::P => 1,
175 Self::Dn2 | Self::Dn1 | Self::D0 | Self::D1 | Self::D2 | Self::D => 2,
176 Self::Fn3 | Self::Fn2 | Self::Fn1 | Self::F0 | Self::F1 | Self::F2 | Self::F3 | Self::F => 3,
177 }
178 }
179 pub fn m(&self) -> i64 {
180 match self {
181 Self::S | Self::P | Self::D | Self::F => 0,
182 Self::S0 | Self::P0 | Self::D0 | Self::F0 => 0,
183 Self::Pn1 | Self::Dn1 | Self::Fn1 => -1,
184 Self::P1 | Self::D1 | Self::F1 => 1,
185 Self::Dn2 | Self::Fn2 => -2,
186 Self::D2 | Self::F2 => 2,
187 Self::Fn3 => -3,
188 Self::F3 => 3,
189 }
190 }
191}
192
193impl Display for Wave {
194 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
195 let l_string = match self.l() {
196 0 => "S",
197 1 => "P",
198 2 => "D",
199 3 => "F",
200 _ => unimplemented!(),
201 };
202 write!(f, "{} {:+}", l_string, self.m())
203 }
204}
205
206#[derive(Copy, Clone, PartialEq)]
207pub enum Frame {
208 Helicity,
209 GottfriedJackson,
210}
211
212#[derive(Debug, PartialEq, Eq, Error)]
213#[error("Unknown frame: {0}")]
214pub struct ParseFrameError(String);
215
216impl From<ParseFrameError> for RustitudeError {
217 fn from(value: ParseFrameError) -> Self {
218 RustitudeError::ParseError(value.to_string())
219 }
220}
221
222impl FromStr for Frame {
223 type Err = ParseFrameError;
224
225 fn from_str(s: &str) -> Result<Self, Self::Err> {
226 match s.to_lowercase().as_ref() {
227 "helicity" | "hx" => Ok(Frame::Helicity),
228 "gottfried-jackson" | "gj" => Ok(Frame::GottfriedJackson),
229 _ => Err(ParseFrameError(s.to_string())),
230 }
231 }
232}
233
234pub fn coordinates<F: Field + 'static>(
235 x: &Vector3<F>,
236 y: &Vector3<F>,
237 z: &Vector3<F>,
238 p: &Vector3<F>,
239) -> Coordinates<F> {
240 Coordinates::cartesian(p.dot(x), p.dot(y), p.dot(z))
241}
242
243impl Frame {
244 pub fn coordinates<F: Field>(
245 &self,
246 decay: Decay,
247 other_p4: &FourMomentum<F>,
248 event: &Event<F>,
249 ) -> (Vector3<F>, Vector3<F>, Vector3<F>, Coordinates<F>) {
250 let resonance_p4 = decay.resonance_p4(event);
251 let beam_res_vec = event.beam_p4.boost_along(&resonance_p4).momentum();
252 let recoil_res_vec = event.recoil_p4.boost_along(&resonance_p4).momentum();
253 let other_res_vec = other_p4.boost_along(&resonance_p4).momentum();
254 let (x, y, z) = match self {
255 Frame::Helicity => {
256 let z = -recoil_res_vec.unit();
257 let y = beam_res_vec.cross(&z).unit();
258 let x = y.cross(&z);
259 (x, y, z)
260 }
261 Frame::GottfriedJackson => {
262 let z = beam_res_vec.unit();
263 let y = event.beam_p4.momentum().cross(&(-recoil_res_vec)).unit();
264 let x = y.cross(&z);
265 (x, y, z)
266 }
267 };
268 (x, y, z, coordinates(&x, &y, &z, &other_res_vec))
269 }
270 pub fn coordinates_from_boosted_vec<F: Field>(
271 &self,
272 decay: Decay,
273 other_res_vec: &Vector3<F>,
274 event: &Event<F>,
275 ) -> (Vector3<F>, Vector3<F>, Vector3<F>, Coordinates<F>) {
276 let resonance_p4 = decay.resonance_p4(event);
277 let beam_res_vec = event.beam_p4.boost_along(&resonance_p4).momentum();
278 let recoil_res_vec = event.recoil_p4.boost_along(&resonance_p4).momentum();
279 let (x, y, z) = match self {
280 Frame::Helicity => {
281 let z = -recoil_res_vec.unit();
282 let y = beam_res_vec.cross(&z).unit();
283 let x = y.cross(&z);
284 (x, y, z)
285 }
286 Frame::GottfriedJackson => {
287 let z = beam_res_vec.unit();
288 let y = event.beam_p4.momentum().cross(&(-recoil_res_vec)).unit();
289 let x = y.cross(&z);
290 (x, y, z)
291 }
292 };
293 (x, y, z, coordinates(&x, &y, &z, other_res_vec))
294 }
295}
296
297#[derive(Copy, Clone, PartialEq)]
298pub enum Sign {
299 Positive = 1,
300 Negative = -1,
301}
302
303#[derive(Debug, PartialEq, Eq, Error)]
304#[error("Unknown sign: {0}")]
305pub struct ParseSignError(String);
306
307impl From<ParseSignError> for RustitudeError {
308 fn from(value: ParseSignError) -> Self {
309 RustitudeError::ParseError(value.to_string())
310 }
311}
312
313impl FromStr for Sign {
314 type Err = ParseSignError;
315
316 fn from_str(s: &str) -> Result<Self, Self::Err> {
317 match s.to_lowercase().as_ref() {
318 "positive" | "pos" | "p" | "+" | "plus" | "+1" => Ok(Sign::Positive),
319 "negative" | "neg" | "n" | "-" | "minus" | "m" | "-1" => Ok(Sign::Negative),
320 _ => Err(ParseSignError(s.to_string())),
321 }
322 }
323}
324
325impl Display for Sign {
326 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
327 match self {
328 Sign::Positive => write!(f, "+"),
329 Sign::Negative => write!(f, "-"),
330 }
331 }
332}
333
334#[derive(Clone, Copy)]
335pub enum Decay {
336 TwoBodyDecay([usize; 2]),
337 ThreeBodyDecay([usize; 3]),
338}
339#[derive(Debug, PartialEq, Eq, Error)]
340pub enum ParseDecayError {
341 #[error("Invalid format: {0}")]
342 InvalidFormat(String),
343 #[error("Invalid number: {0}")]
344 InvalidNumber(#[from] ParseIntError),
345 #[error("Invalid number of final states: {0}")]
346 InvalidLength(usize),
347}
348
349impl From<ParseDecayError> for RustitudeError {
350 fn from(value: ParseDecayError) -> Self {
351 RustitudeError::ParseError(value.to_string())
352 }
353}
354
355impl FromStr for Decay {
356 type Err = ParseDecayError;
357
358 fn from_str(s: &str) -> Result<Self, Self::Err> {
359 let cleaned = s.replace(|c: char| c.is_whitespace() || c == '[' || c == ']', "");
360 let parts: Vec<&str> = cleaned.split(',').collect();
361 match parts.len() {
362 2 => {
363 let values = [
364 parts[0].parse().map_err(ParseDecayError::InvalidNumber)?,
365 parts[1].parse().map_err(ParseDecayError::InvalidNumber)?,
366 ];
367 Ok(Decay::TwoBodyDecay(values))
368 }
369 3 => {
370 let values = [
371 parts[0].parse().map_err(ParseDecayError::InvalidNumber)?,
372 parts[1].parse().map_err(ParseDecayError::InvalidNumber)?,
373 parts[2].parse().map_err(ParseDecayError::InvalidNumber)?,
374 ];
375 Ok(Decay::ThreeBodyDecay(values))
376 }
377 _ => Err(ParseDecayError::InvalidLength(parts.len())),
378 }
379 }
380}
381
382impl Default for Decay {
383 fn default() -> Self {
384 Self::TwoBodyDecay([0, 1])
385 }
386}
387
388impl Decay {
389 pub fn resonance_p4<F: Field>(&self, event: &Event<F>) -> FourMomentum<F> {
390 match self {
391 Decay::TwoBodyDecay(inds) => inds.iter().map(|&i| event.daughter_p4s[i]).sum(),
392 Decay::ThreeBodyDecay(inds) => inds.iter().map(|&i| event.daughter_p4s[i]).sum(),
393 }
394 }
395 pub fn daughter_p4<'a, F: Field>(
396 &self,
397 index: usize,
398 event: &'a Event<F>,
399 ) -> &'a FourMomentum<F> {
400 match self {
401 Decay::TwoBodyDecay(inds) => &event.daughter_p4s[inds[index]],
402 Decay::ThreeBodyDecay(inds) => &event.daughter_p4s[inds[index]],
403 }
404 }
405 pub fn primary_p4<'a, F: Field>(&self, event: &'a Event<F>) -> &'a FourMomentum<F> {
406 match self {
407 Decay::TwoBodyDecay(inds) => &event.daughter_p4s[inds[0]],
408 Decay::ThreeBodyDecay(inds) => &event.daughter_p4s[inds[0]],
409 }
410 }
411 pub fn secondary_p4<'a, F: Field>(&self, event: &'a Event<F>) -> &'a FourMomentum<F> {
412 match self {
413 Decay::TwoBodyDecay(inds) => &event.daughter_p4s[inds[1]],
414 Decay::ThreeBodyDecay(inds) => &event.daughter_p4s[inds[1]],
415 }
416 }
417 pub fn tertiary_p4<'a, F: Field>(&self, event: &'a Event<F>) -> &'a FourMomentum<F> {
418 match self {
419 Decay::TwoBodyDecay(_) => panic!(),
420 Decay::ThreeBodyDecay(inds) => &event.daughter_p4s[inds[2]],
421 }
422 }
423 pub fn coordinates<F: Field>(
424 &self,
425 frame: Frame,
426 index: usize,
427 event: &Event<F>,
428 ) -> (Vector3<F>, Vector3<F>, Vector3<F>, Coordinates<F>) {
429 frame.coordinates(*self, self.daughter_p4(index, event), event)
430 }
431}