1use particle_id::ParticleID;
2use serde::{Deserialize, Serialize, Serializer};
3use serde_repr::*;
4use strum::EnumString;
5use thiserror::Error;
6
7#[derive(Deserialize, Serialize)]
8#[derive(Clone, Debug, Default, PartialEq, PartialOrd)]
9pub struct Eventrecord {
10 #[serde(rename = "@nevents")]
11 pub nevents: u64,
12 #[serde(rename = "@nsubevents")]
13 pub nsubevents: u64,
14 #[serde(rename = "@nreweights")]
15 pub nreweights: u64,
16 #[serde(rename = "@as")]
17 pub alpha_s_power: u64,
18 #[serde(rename = "@name")]
19 pub name: String,
20 #[serde(rename = "e")]
21 pub events: Vec<Event>,
22}
23
24#[derive(Deserialize, Serialize)]
25#[derive(Clone, Debug, Default, PartialEq, PartialOrd)]
26#[serde(rename = "e")]
27pub struct Event {
28 #[serde(rename = "se")]
29 pub subevents: Vec<SubEvent>,
30}
31
32#[derive(Deserialize, Serialize)]
33#[derive(Clone, Debug, Default, PartialEq, PartialOrd)]
34#[serde(rename = "se")]
35pub struct SubEvent {
36 #[serde(rename = "@w")]
37 pub weight: f64,
38 #[serde(rename = "@muR")]
39 pub mu_r: f64,
40 #[serde(rename = "@muF")]
41 pub mu_f: f64,
42 #[serde(rename = "p")]
43 pub particles: Vec<Particle>,
44 #[serde(rename = "rw")]
45 pub reweight: Vec<Reweight>
46}
47
48#[derive(Deserialize, Serialize)]
49#[derive(Clone, Debug, PartialEq, PartialOrd)]
50#[serde(rename = "p")]
51pub struct Particle {
52 #[serde(rename = "@id")]
53 pub id: Id,
54 #[serde(rename = "$text")]
55 pub momentum: Momentum,
56}
57
58#[derive(Deserialize, Serialize)]
59#[derive(Clone, Debug, Default, PartialEq, PartialOrd)]
60#[serde(rename = "rw")]
61pub struct Reweight {
62 #[serde(rename = "@ch")]
63 pub channel: u32,
64 #[serde(rename = "$text")]
65 pub reweights: Reweights,
66}
67
68#[derive(Clone, Debug, Default, PartialEq, PartialOrd)]
69pub struct Reweights {
70 pub x1: f64,
71 pub x2: f64,
72 pub log_coeff: Vec<f64>,
73}
74
75#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
76pub struct Id {
77 pub status: Status,
78 pub pdg_id: ParticleID,
79}
80
81#[derive(Deserialize_repr, Serialize_repr)]
82#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
83#[derive(EnumString)]
84#[repr(u8)]
85pub enum Status {
86 #[strum(serialize = "0")]
87 Outgoing = 0,
88 #[strum(serialize = "1")]
89 Incoming = 1,
90}
91
92#[derive(Clone, Debug, Default, PartialEq, PartialOrd)]
93pub struct Momentum (pub [f64; 4]);
94
95impl<'de> Deserialize<'de> for Momentum {
96 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
97 where
98 D: serde::Deserializer<'de> {
99 let momentum_str = String::deserialize(deserializer)?;
100 let mut entries = momentum_str.split(',');
101 let mut momentum = [0.; 4];
102 for q in &mut momentum {
103 let Some(p) = entries.next() else {
104 return Err(serde::de::Error::custom(
105 ParseErr::NumEntries(momentum_str, 4)
106 ));
107 };
108 *q = p.parse().map_err(serde::de::Error::custom)?;
109 }
110 if entries.next().is_some() {
111 return Err(serde::de::Error::custom(
112 ParseErr::NumEntries(momentum_str, 4)
113 ));
114 }
115 Ok(Self(momentum))
116 }
117}
118impl Serialize for Momentum {
119 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
120 where
121 S: Serializer,
122 {
123 let p = self.0;
124 serializer.serialize_str(
125 &format!("{},{},{},{}", p[0], p[1], p[2], p[3])
126 )
127 }
128}
129
130impl<'de> Deserialize<'de> for Reweights {
131 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
132 where
133 D: serde::Deserializer<'de> {
134 use ParseErr::NumEntries;
135 use serde::de::Error;
136 let reweights_str = String::deserialize(deserializer)?;
137 let mut reweights = reweights_str.split(',');
138 let Some(x1) = reweights.next() else {
139 return Err(Error::custom(
140 NumEntries(reweights_str, 2)
141 ));
142 };
143 let x1 = x1.parse().map_err(Error::custom)?;
144 let Some(x2) = reweights.next() else {
145 return Err(Error::custom(
146 NumEntries(reweights_str, 2)
147 ));
148 };
149 let x2 = x2.parse().map_err(Error::custom)?;
150 let mut log_coeff = Vec::new();
151 for val in reweights {
152 log_coeff.push(val.parse().map_err(Error::custom)?);
153 }
154 Ok(Self {
155 x1,
156 x2,
157 log_coeff,
158 })
159 }
160}
161
162impl Serialize for Reweights {
163 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
164 where
165 S: Serializer,
166 {
167 let mut res = format!("{},{}", self.x1, self.x2);
168 for log_coeff in &self.log_coeff {
169 res.push(',');
170 res += &log_coeff.to_string();
171 }
172 serializer.serialize_str(&res)
173 }
174}
175
176impl<'de> Deserialize<'de> for Id {
177 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
178 where
179 D: serde::Deserializer<'de> {
180 let id_str = String::deserialize(deserializer)?;
181 let mut entries = id_str.split(',');
182 let Some(status) = entries.next() else {
183 return Err(serde::de::Error::custom(
184 ParseErr::NumEntries(id_str, 2)
185 ));
186 };
187 let status = status.parse().map_err(
188 serde::de::Error::custom
189 )?;
190
191 let Some(pdg_id) = entries.next() else {
192 return Err(serde::de::Error::custom(
193 ParseErr::NumEntries(id_str, 2)
194 ));
195 };
196 let pdg_id: i32 = pdg_id.parse().map_err(
197 serde::de::Error::custom
198 )?;
199 let pdg_id = ParticleID::new(pdg_id);
200
201 if entries.next().is_some() {
202 return Err(serde::de::Error::custom(
203 ParseErr::NumEntries(id_str, 2)
204 ));
205 }
206
207 Ok(Self{status, pdg_id})
208 }
209}
210impl Serialize for Id {
211 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
212 where
213 S: Serializer,
214 {
215 serializer.serialize_str(
216 &format!("{},{}", self.status as u8, self.pdg_id.id())
217 )
218 }
219}
220
221pub trait WriteXML {
225 type Error;
226
227 fn write<W: std::io::Write>(&self, writer: &mut W) -> Result<(), Self::Error>;
228}
229
230impl WriteXML for Eventrecord {
231 type Error = std::io::Error;
232
233 fn write<W: std::io::Write>(&self, writer: &mut W) -> Result<(), Self::Error> {
234 writeln!(
235 writer,
236 "<Eventrecord nevents=\"{}\" nsubevents=\"{}\" nreweights=\"{}\" as=\"{}\" name=\"{}\">",
237 self.nevents, self.nsubevents, self.nreweights, self.alpha_s_power, self.name
238 )?;
239 writeln!(
240 writer,
241 "<!--\nRecord generated with {} {}\n-->",
242 env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION")
243 )?;
244 for event in &self.events {
245 event.write(writer)?;
246 }
247 writer.write_all(b"</Eventrecord>\n")
248 }
249}
250
251impl WriteXML for Event {
252 type Error = std::io::Error;
253
254 fn write<W: std::io::Write>(&self, writer: &mut W) -> Result<(), Self::Error> {
255 writer.write_all(b"<e>\n")?;
256 for subevent in &self.subevents {
257 subevent.write(writer)?;
258 }
259 writer.write_all(b"</e>\n")
260 }
261}
262
263impl WriteXML for SubEvent {
264 type Error = std::io::Error;
265
266 fn write<W: std::io::Write>(&self, writer: &mut W) -> Result<(), Self::Error> {
267 use std::fmt::Write;
268
269 writeln!(
270 writer,
271 "<se w=\"{}\" muR=\"{}\" muF=\"{}\">",
272 self.weight, self.mu_r, self.mu_f
273 )?;
274 let mut out = String::new();
275 for p in &self.particles {
276 let ser = quick_xml::se::Serializer::new(&mut out);
277 p.serialize(ser).unwrap();
278 out.write_char('\n').unwrap();
279 }
280 for rw in &self.reweight {
281 let ser = quick_xml::se::Serializer::new(&mut out);
282 rw.serialize(ser).unwrap();
283 out.write_char('\n').unwrap();
284 }
285 writer.write_all(out.as_bytes())?;
286 writer.write_all(b"</se>\n")
287 }
288}
289
290
291#[derive(Debug, Error)]
292pub enum ParseErr {
293 #[error("'{0}' is not a comma-separated list with {1} float values")]
294 NumEntries(String, usize)
295}
296
297#[cfg(test)]
298mod tests {
299 use super::*;
300
301 #[test]
302 fn deser_subevent() {
303 let txt = r#"<se w="-0.0002369763508" muR="91.16253934" muF="91.16253934">
304<p id="1,21"> 5780.608219,0,0,5780.608219 </p>
305<p id="1,21"> 334.3891359,0,0,-334.3891359 </p>
306<p id="0,6"> 357.9061187,-58.25473457,9.621341818,-307.9843429 </p>
307<p id="0,-6"> 5757.091237,58.25473457,-9.621341818,5754.203426 </p>
308<rw ch="12"> 0.8893243414,0.05144448245,-0.0002369763508 </rw>
309</se>"#;
310 use Status::*;
311 use particle_id::sm_elementary_particles::*;
312 let ref_event = SubEvent {
313 weight: -0.0002369763508,
314 mu_r: 91.16253934,
315 mu_f: 91.16253934,
316 particles: vec![
317 Particle{
318 id: Id{
319 status: Incoming,
320 pdg_id: gluon,
321 },
322 momentum: Momentum([5780.608219,0.,0.,5780.608219])
323 },
324 Particle{
325 id: Id{
326 status: Incoming,
327 pdg_id: gluon,
328 },
329 momentum: Momentum([334.3891359,0.,0.,-334.3891359])
330 },
331 Particle{
332 id: Id{
333 status: Outgoing,
334 pdg_id: top,
335 },
336 momentum: Momentum([357.9061187,-58.25473457,9.621341818,-307.9843429])
337 },
338 Particle{
339 id: Id{
340 status: Outgoing,
341 pdg_id: anti_top,
342 },
343 momentum: Momentum([5757.091237,58.25473457,-9.621341818,5754.203426])
344 }
345 ],
346 reweight: vec![ Reweight {
347 channel: 12,
348 reweights: Reweights{
349 x1: 0.8893243414,
350 x2: 0.05144448245,
351 log_coeff: vec![-0.0002369763508]
352 }
353 }],
354 };
355 let event: SubEvent = quick_xml::de::from_str(txt).unwrap();
356 assert_eq!(event, ref_event);
357 }
358
359 const REF_RECORD: &str = r#"<?xml version="1.0" encoding="UTF-8"?>
360<Eventrecord nevents="2286" nsubevents="2286" nreweights="2286" as="2" name="Bm">
361<!--
362File generated with STRIPPER v0.1 for online data base
363-->
364<e>
365<se w="-0.0002369763508" muR="91.16253934" muF="91.16253934">
366<p id="1,21"> 5780.608219,0,0,5780.608219 </p>
367<p id="1,21"> 334.3891359,0,0,-334.3891359 </p>
368<p id="0,6"> 357.9061187,-58.25473457,9.621341818,-307.9843429 </p>
369<p id="0,-6"> 5757.091237,58.25473457,-9.621341818,5754.203426 </p>
370<rw ch="12"> 0.8893243414,0.05144448245,-0.0002369763508 </rw>
371</se>
372</e>
373<e>
374<se w="-0.0004385904665" muR="517.0997809" muF="517.0997809">
375<p id="1,1"> 327.0442813,0,0,327.0442813 </p>
376<p id="1,-1"> 4344.52245,0,0,-4344.52245 </p>
377<p id="0,6"> 3334.580936,-386.757619,943.5205498,-3170.151619 </p>
378<p id="0,-6"> 1336.985795,386.757619,-943.5205498,-847.3265495 </p>
379<rw ch="1"> 0.05031450481,0.6683880692,-0.0004385904665 </rw>
380</se>
381</e>
382<e>
383<se w="-2.098171554e-05" muR="1140.717994" muF="1140.717994">
384<p id="1,1"> 1610.067985,0,0,1610.067985 </p>
385<p id="1,-1"> 4034.720799,0,0,-4034.720799 </p>
386<p id="0,6"> 3362.889308,-2194.656814,598.8951393,-2470.642493 </p>
387<p id="0,-6"> 2281.899476,2194.656814,-598.8951393,45.98967904 </p>
388<rw ch="1"> 0.2477027669,0.6207262768,-2.098171554e-05 </rw>
389</se>
390</e>
391<e>
392<se w="-4.834595231e-05" muR="635.8532498" muF="635.8532498">
393<p id="1,1"> 1299.986308,0,0,1299.986308 </p>
394<p id="1,-1"> 3291.996472,0,0,-3291.996472 </p>
395<p id="0,6"> 1510.408466,-1129.902831,557.4950784,814.9210479 </p>
396<p id="0,-6"> 3081.574313,1129.902831,-557.4950784,-2806.931212 </p>
397<rw ch="1"> 0.1999978935,0.5064609956,-4.834595231e-05 </rw>
398</se>
399</e>
400</Eventrecord>
401"#;
402 #[test]
403 fn deser_file() {
404
405 let record: Eventrecord = quick_xml::de::from_str(REF_RECORD).unwrap();
406 assert_eq!(record.nevents, 2286);
407 assert_eq!(record.nsubevents, 2286);
408 assert_eq!(record.nreweights, 2286);
409 assert_eq!(record.alpha_s_power, 2);
410 assert_eq!(record.name, "Bm");
411 assert_eq!(record.events.len(), 4);
412 }
413
414 #[test]
415 fn ser_file() {
416
417 let record: Eventrecord = quick_xml::de::from_str(REF_RECORD).unwrap();
418 let mut tmp = br#"<?xml version="1.0" encoding="UTF-8"?>
419"#.to_vec();
420 record.write(&mut tmp).unwrap();
421 let record_2: Eventrecord = quick_xml::de::from_reader(tmp.as_slice()).unwrap();
422 assert_eq!(record, record_2);
423 }
424}