Skip to main content

laddu_core/
quantum.rs

1//! Quantum-number helpers and discrete analysis enums.
2
3use std::{fmt::Display, str::FromStr};
4
5use serde::{Deserialize, Serialize};
6
7use crate::{quantum::types::Sign, LadduError};
8
9mod types;
10pub use types::{AngularMomentum, Charge, OrbitalAngularMomentum, Parity, Projection, Statistics};
11
12mod state;
13pub use state::{AllowedPartialWave, Isospin, PartialWave, ParticleProperties, SpinState};
14
15mod rules;
16pub use rules::{RuleSet, SelectionRules};
17
18/// Enumerate allowed projections for a spin.
19pub fn allowed_projections(spin: AngularMomentum) -> Vec<Projection> {
20    SpinState::allowed_projections(spin)
21        .into_iter()
22        .map(SpinState::projection)
23        .collect()
24}
25
26/// Standard reference frames for angular analyses.
27#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
28pub enum Frame {
29    /// The helicity frame, obtained by setting the $`z`$-axis equal to the boost direction from
30    /// the center-of-momentum to the rest frame of the resonance in question and the $`y`$-axis
31    /// perpendicular to the production plane.
32    Helicity,
33    /// The Gottfried-Jackson frame, obtained by setting the $`z`$-axis proportional to the beam's
34    /// direction in the rest frame of the resonance in question and the $`y`$-axis perpendicular
35    /// to the production plane.
36    GottfriedJackson,
37    /// The Adair frame.
38    Adair,
39}
40impl Display for Frame {
41    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
42        match self {
43            Frame::Helicity => write!(f, "Helicity"),
44            Frame::GottfriedJackson => write!(f, "Gottfried-Jackson"),
45            Frame::Adair => write!(f, "Adair"),
46        }
47    }
48}
49impl FromStr for Frame {
50    type Err = LadduError;
51
52    fn from_str(s: &str) -> Result<Self, Self::Err> {
53        match s.to_lowercase().as_str() {
54            "helicity" | "hx" | "hel" => Ok(Self::Helicity),
55            "gottfriedjackson" | "gottfried jackson" | "gj" | "gottfried-jackson" => {
56                Ok(Self::GottfriedJackson)
57            }
58            "adair" => Ok(Self::Adair),
59            _ => Err(LadduError::ParseError {
60                name: s.to_string(),
61                object: "Frame".to_string(),
62            }),
63        }
64    }
65}
66
67/// A simple enum describing a binary sign.
68#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
69pub enum Reflectivity {
70    /// A positive indicator.
71    Positive,
72    /// A negative indicator.
73    Negative,
74}
75impl Display for Reflectivity {
76    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
77        match self {
78            Reflectivity::Positive => write!(f, "+"),
79            Reflectivity::Negative => write!(f, "-"),
80        }
81    }
82}
83
84impl FromStr for Reflectivity {
85    type Err = LadduError;
86
87    fn from_str(s: &str) -> Result<Self, Self::Err> {
88        match parse_sign_value(s, "Reflectivity")? {
89            Sign::Positive => Ok(Self::Positive),
90            Sign::Negative => Ok(Self::Negative),
91        }
92    }
93}
94
95pub(crate) fn parse_sign_value(s: &str, object: &str) -> Result<Sign, LadduError> {
96    match s.to_lowercase().as_ref() {
97        "+" | "plus" | "pos" | "positive" => Ok(Sign::Positive),
98        "-" | "minus" | "neg" | "negative" => Ok(Sign::Negative),
99        _ => Err(LadduError::ParseError {
100            name: s.to_string(),
101            object: object.to_string(),
102        }),
103    }
104}
105
106/// An enum for Mandelstam variables
107#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
108pub enum Channel {
109    /// s-channel
110    S,
111    /// t-channel
112    T,
113    /// u-channel
114    U,
115}
116
117impl Display for Channel {
118    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
119        match self {
120            Channel::S => write!(f, "s"),
121            Channel::T => write!(f, "t"),
122            Channel::U => write!(f, "u"),
123        }
124    }
125}
126
127impl FromStr for Channel {
128    type Err = LadduError;
129
130    fn from_str(s: &str) -> Result<Self, Self::Err> {
131        match s.to_lowercase().as_ref() {
132            "s" => Ok(Self::S),
133            "t" => Ok(Self::T),
134            "u" => Ok(Self::U),
135            _ => Err(LadduError::ParseError {
136                name: s.to_string(),
137                object: "Channel".to_string(),
138            }),
139        }
140    }
141}
142
143#[cfg(test)]
144mod tests {
145    use super::*;
146
147    #[test]
148    fn spin_state_accepts_integer_and_half_integer_values() {
149        let spin_one = AngularMomentum::integer(1);
150        let spin_half = AngularMomentum::half_integer(1);
151        assert_eq!(
152            SpinState::new(spin_one, Projection::integer(0))
153                .unwrap()
154                .projection()
155                .value(),
156            0
157        );
158        assert_eq!(
159            SpinState::new(spin_half, Projection::half_integer(-1))
160                .unwrap()
161                .projection()
162                .value(),
163            -1
164        );
165    }
166
167    #[test]
168    fn spin_state_rejects_invalid_projection() {
169        let spin_one = AngularMomentum::integer(1);
170        assert!(SpinState::new(spin_one, Projection::half_integer(4)).is_err());
171        assert!(SpinState::new(spin_one, Projection::half_integer(1)).is_err());
172    }
173
174    #[test]
175    fn spin_state_enumerates_allowed_projections() {
176        let spin_zero = SpinState::allowed_projections(AngularMomentum::integer(0));
177        assert_eq!(
178            spin_zero
179                .iter()
180                .map(|state| state.projection().value())
181                .collect::<Vec<_>>(),
182            vec![0]
183        );
184
185        let spin_half = SpinState::allowed_projections(AngularMomentum::half_integer(1));
186        assert_eq!(
187            spin_half
188                .iter()
189                .map(|state| state.projection().value())
190                .collect::<Vec<_>>(),
191            vec![-1, 1]
192        );
193
194        let spin_one = SpinState::allowed_projections(AngularMomentum::integer(1));
195        assert_eq!(
196            spin_one
197                .iter()
198                .map(|state| state.projection().value())
199                .collect::<Vec<_>>(),
200            vec![-2, 0, 2]
201        );
202
203        let spin_three_halves = SpinState::allowed_projections(AngularMomentum::half_integer(3));
204        assert_eq!(
205            spin_three_halves
206                .iter()
207                .map(|state| state.projection().value())
208                .collect::<Vec<_>>(),
209            vec![-3, -1, 1, 3]
210        );
211    }
212
213    #[test]
214    fn allowed_projection_helper_returns_projection_values() {
215        assert_eq!(
216            allowed_projections(AngularMomentum::integer(1)),
217            vec![
218                Projection::integer(-1),
219                Projection::integer(0),
220                Projection::integer(1),
221            ]
222        );
223    }
224
225    #[test]
226    fn enum_displays() {
227        assert_eq!(format!("{}", Frame::Helicity), "Helicity");
228        assert_eq!(format!("{}", Frame::GottfriedJackson), "Gottfried-Jackson");
229        assert_eq!(format!("{}", Frame::Adair), "Adair");
230        assert_eq!(format!("{}", Reflectivity::Positive), "+");
231        assert_eq!(format!("{}", Reflectivity::Negative), "-");
232        assert_eq!(format!("{}", Channel::S), "s");
233        assert_eq!(format!("{}", Channel::T), "t");
234        assert_eq!(format!("{}", Channel::U), "u");
235    }
236
237    #[test]
238    fn enum_from_str() {
239        assert_eq!(Frame::from_str("Helicity").unwrap(), Frame::Helicity);
240        assert_eq!(Frame::from_str("HX").unwrap(), Frame::Helicity);
241        assert_eq!(Frame::from_str("HEL").unwrap(), Frame::Helicity);
242        assert_eq!(
243            Frame::from_str("GottfriedJackson").unwrap(),
244            Frame::GottfriedJackson
245        );
246        assert_eq!(Frame::from_str("GJ").unwrap(), Frame::GottfriedJackson);
247        assert_eq!(
248            Frame::from_str("Gottfried-Jackson").unwrap(),
249            Frame::GottfriedJackson
250        );
251        assert_eq!(
252            Frame::from_str("Gottfried Jackson").unwrap(),
253            Frame::GottfriedJackson
254        );
255        assert_eq!(Frame::from_str("Adair").unwrap(), Frame::Adair);
256        assert_eq!(Reflectivity::from_str("+").unwrap(), Reflectivity::Positive);
257        assert_eq!(
258            Reflectivity::from_str("pos").unwrap(),
259            Reflectivity::Positive
260        );
261        assert_eq!(
262            Reflectivity::from_str("plus").unwrap(),
263            Reflectivity::Positive
264        );
265        assert_eq!(
266            Reflectivity::from_str("Positive").unwrap(),
267            Reflectivity::Positive
268        );
269        assert_eq!(Reflectivity::from_str("-").unwrap(), Reflectivity::Negative);
270        assert_eq!(
271            Reflectivity::from_str("minus").unwrap(),
272            Reflectivity::Negative
273        );
274        assert_eq!(
275            Reflectivity::from_str("neg").unwrap(),
276            Reflectivity::Negative
277        );
278        assert_eq!(
279            Reflectivity::from_str("Negative").unwrap(),
280            Reflectivity::Negative
281        );
282        assert_eq!(Channel::from_str("S").unwrap(), Channel::S);
283        assert_eq!(Channel::from_str("s").unwrap(), Channel::S);
284        assert_eq!(Channel::from_str("T").unwrap(), Channel::T);
285        assert_eq!(Channel::from_str("t").unwrap(), Channel::T);
286        assert_eq!(Channel::from_str("U").unwrap(), Channel::U);
287        assert_eq!(Channel::from_str("u").unwrap(), Channel::U);
288    }
289}