laddu_core/reaction/
particle.rs1use std::fmt::Display;
2
3use serde::{Deserialize, Serialize};
4
5use crate::{data::EventLike, vectors::Vec4, LadduError, LadduResult};
6
7#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
9pub struct Particle {
10 label: String,
11 source: ParticleSource,
12}
13
14#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
16pub enum ParticleSource {
17 Stored {
19 p4_name: String,
21 },
22 Fixed {
24 p4: Vec4,
26 },
27 Missing,
29 Composite {
31 daughters: Box<[Particle; 2]>,
33 },
34}
35
36impl Particle {
37 pub fn stored(id: impl Into<String>) -> Self {
39 let id = id.into();
40 Self {
41 label: id.clone(),
42 source: ParticleSource::Stored { p4_name: id },
43 }
44 }
45
46 pub fn fixed(label: impl Into<String>, p4: Vec4) -> Self {
48 Self {
49 label: label.into(),
50 source: ParticleSource::Fixed { p4 },
51 }
52 }
53
54 pub fn missing(label: impl Into<String>) -> Self {
56 Self {
57 label: label.into(),
58 source: ParticleSource::Missing,
59 }
60 }
61
62 pub fn composite(
64 label: impl Into<String>,
65 daughters: (&Particle, &Particle),
66 ) -> LadduResult<Self> {
67 let daughters = [daughters.0.clone(), daughters.1.clone()];
68 if daughters.iter().any(Self::contains_missing) {
69 return Err(LadduError::Custom(
70 "missing particles cannot be used as composite daughters".to_string(),
71 ));
72 }
73 Ok(Self {
74 label: label.into(),
75 source: ParticleSource::Composite {
76 daughters: Box::new(daughters),
77 },
78 })
79 }
80
81 pub fn label(&self) -> &str {
83 &self.label
84 }
85
86 pub const fn source(&self) -> &ParticleSource {
88 &self.source
89 }
90
91 pub const fn is_missing(&self) -> bool {
93 matches!(self.source, ParticleSource::Missing)
94 }
95
96 pub(super) fn contains_missing(&self) -> bool {
97 self.is_missing() || self.daughters().iter().any(Self::contains_missing)
98 }
99
100 pub fn daughters(&self) -> &[Particle] {
102 match &self.source {
103 ParticleSource::Composite { daughters } => daughters.as_slice(),
104 _ => &[],
105 }
106 }
107
108 pub(super) fn contains_id(&self, particle: &str) -> bool {
109 if self.label() == particle {
110 return true;
111 }
112 self.daughters()
113 .iter()
114 .any(|daughter| daughter.contains_id(particle))
115 }
116
117 pub(super) fn parent_of_id(&self, child: &str) -> Option<&Particle> {
118 if self
119 .daughters()
120 .iter()
121 .any(|daughter| daughter.label() == child)
122 {
123 return Some(self);
124 }
125 self.daughters()
126 .iter()
127 .find_map(|daughter| daughter.parent_of_id(child))
128 }
129}
130
131impl Display for Particle {
132 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
133 write!(f, "{}", self.label)
134 }
135}
136
137pub(super) fn resolve_particle_direct(
138 event: &dyn EventLike,
139 particle: &Particle,
140) -> LadduResult<Option<Vec4>> {
141 match particle.source() {
142 ParticleSource::Stored { p4_name } => event
143 .p4(p4_name)
144 .ok_or_else(|| LadduError::Custom(format!("unknown p4 column '{p4_name}'")))
145 .map(Some),
146 ParticleSource::Fixed { p4 } => Ok(Some(*p4)),
147 ParticleSource::Missing => Ok(None),
148 ParticleSource::Composite { daughters } => daughters
149 .iter()
150 .map(|daughter| {
151 resolve_particle_direct(event, daughter)?.ok_or_else(|| {
152 LadduError::Custom(format!(
153 "missing daughter '{}' cannot be resolved inside composite '{}'",
154 daughter.label(),
155 particle.label()
156 ))
157 })
158 })
159 .try_fold(Vec4::new(0.0, 0.0, 0.0, 0.0), |acc, value| {
160 value.map(|value| acc + value)
161 })
162 .map(Some),
163 }
164}