laddu_core/reaction/
two_to_two.rs1use serde::{Deserialize, Serialize};
2
3use super::{graph::ParticleGraph, particle::resolve_particle_direct, Particle};
4use crate::{
5 data::EventLike,
6 vectors::{Vec3, Vec4},
7 LadduError, LadduResult,
8};
9
10#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
12pub struct TwoToTwoReaction {
13 p1: String,
14 p2: String,
15 p3: String,
16 p4: String,
17 missing_index: Option<usize>,
18}
19
20impl TwoToTwoReaction {
21 pub fn new(p1: &Particle, p2: &Particle, p3: &Particle, p4: &Particle) -> LadduResult<Self> {
23 let particles = [p1, p2, p3, p4];
24 let missing = particles
25 .iter()
26 .enumerate()
27 .filter_map(|(index, particle)| particle.is_missing().then_some(index))
28 .collect::<Vec<_>>();
29 if missing.len() > 1 {
30 return Err(LadduError::Custom(
31 "two-to-two reaction can contain at most one missing particle".to_string(),
32 ));
33 }
34 Ok(Self {
35 p1: p1.label().to_string(),
36 p2: p2.label().to_string(),
37 p3: p3.label().to_string(),
38 p4: p4.label().to_string(),
39 missing_index: missing.first().copied(),
40 })
41 }
42
43 pub fn p1(&self) -> &str {
45 &self.p1
46 }
47
48 pub fn p2(&self) -> &str {
50 &self.p2
51 }
52
53 pub fn p3(&self) -> &str {
55 &self.p3
56 }
57
58 pub fn p4(&self) -> &str {
60 &self.p4
61 }
62
63 pub const fn missing_index(&self) -> Option<usize> {
65 self.missing_index
66 }
67
68 pub fn resolve(
70 &self,
71 graph: &ParticleGraph,
72 event: &dyn EventLike,
73 ) -> LadduResult<ResolvedTwoToTwo> {
74 let mut momenta = [
75 resolve_particle_direct(event, graph.particle(&self.p1)?)?,
76 resolve_particle_direct(event, graph.particle(&self.p2)?)?,
77 resolve_particle_direct(event, graph.particle(&self.p3)?)?,
78 resolve_particle_direct(event, graph.particle(&self.p4)?)?,
79 ];
80 if let Some(index) = self.missing_index {
81 let missing = match index {
82 0 => momenta[2].unwrap() + momenta[3].unwrap() - momenta[1].unwrap(),
83 1 => momenta[2].unwrap() + momenta[3].unwrap() - momenta[0].unwrap(),
84 2 => momenta[0].unwrap() + momenta[1].unwrap() - momenta[3].unwrap(),
85 3 => momenta[0].unwrap() + momenta[1].unwrap() - momenta[2].unwrap(),
86 _ => unreachable!("validated two-to-two slot index"),
87 };
88 momenta[index] = Some(missing);
89 }
90 Ok(ResolvedTwoToTwo {
91 p1: momenta[0].unwrap(),
92 p2: momenta[1].unwrap(),
93 p3: momenta[2].unwrap(),
94 p4: momenta[3].unwrap(),
95 })
96 }
97
98 pub(super) fn missing_particle(&self) -> Option<&str> {
99 self.missing_index.map(|index| self.slot_unchecked(index))
100 }
101
102 fn slot_unchecked(&self, index: usize) -> &str {
103 match index {
104 0 => &self.p1,
105 1 => &self.p2,
106 2 => &self.p3,
107 3 => &self.p4,
108 _ => unreachable!("validated two-to-two slot index"),
109 }
110 }
111
112 pub fn role(&self, role: &str) -> LadduResult<&str> {
114 match role {
115 "p1" => Ok(&self.p1),
116 "p2" => Ok(&self.p2),
117 "p3" => Ok(&self.p3),
118 "p4" => Ok(&self.p4),
119 _ => Err(LadduError::Custom(format!(
120 "unknown two-to-two reaction role '{role}'"
121 ))),
122 }
123 }
124}
125
126#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
128pub struct ResolvedTwoToTwo {
129 pub(super) p1: Vec4,
130 pub(super) p2: Vec4,
131 pub(super) p3: Vec4,
132 pub(super) p4: Vec4,
133}
134
135impl ResolvedTwoToTwo {
136 pub const fn p1(self) -> Vec4 {
138 self.p1
139 }
140
141 pub const fn p2(self) -> Vec4 {
143 self.p2
144 }
145
146 pub const fn p3(self) -> Vec4 {
148 self.p3
149 }
150
151 pub const fn p4(self) -> Vec4 {
153 self.p4
154 }
155
156 pub fn com_boost(self) -> Vec3 {
158 -(self.p1 + self.p2).beta()
159 }
160
161 pub fn s(self) -> f64 {
163 (self.p1 + self.p2).m2()
164 }
165
166 pub fn t(self) -> f64 {
168 (self.p1 - self.p3).m2()
169 }
170
171 pub fn u(self) -> f64 {
173 (self.p1 - self.p4).m2()
174 }
175}