stripper_xml/
normalization.rs

1use serde::{Deserialize, Serialize, Serializer};
2
3use crate::ParseErr;
4
5#[derive(Deserialize, Serialize)]
6#[derive(Clone, Debug, Default, PartialEq, PartialOrd)]
7pub struct Normalization {
8    #[serde(rename = "@name")]
9    pub name: String,
10    //#[serde(rename = "$field")]
11    #[serde(rename = "XSection")]
12    pub xsection: XSection,
13    #[serde(rename = "Contribution")]
14    pub contribution: Contribution,
15    #[serde(rename = "NumberOfRejectedEvents")]
16    pub number_of_rejected_events: String,
17}
18
19// // TODO: this would be the proper way to do this, but it doesn't work
20// #[derive(Deserialize, Serialize)]
21// #[derive(Clone, Debug, PartialEq, PartialOrd)]
22// #[serde(untagged)]
23// pub enum XSection {
24//     Neg(XSectionNeg),
25//     Pos(XSectionPos),
26// }
27
28// #[derive(Deserialize, Serialize)]
29// #[derive(Clone, Debug, PartialEq, PartialOrd)]
30// #[serde(rename_all = "PascalCase")]
31// pub struct XSectionNeg {
32//     #[serde(rename = "XSNeg")]
33//     pub xs_neg: XSScale,
34//     pub max_weight_neg: f64,
35//     pub total_events_neg: u64,
36//     pub accepted_events_neg: u64,
37//     pub factor_neg: String,
38// }
39
40// #[derive(Deserialize, Serialize)]
41// #[derive(Clone, Debug, PartialEq, PartialOrd)]
42// #[serde(rename_all = "PascalCase")]
43// pub struct XSectionPos {
44//     #[serde(rename = "XSPos")]
45//     pub xs_pos: XSScale,
46//     pub max_weight_pos: f64,
47//     pub total_events_pos: u64,
48//     pub accepted_events_pos: u64,
49//     pub factor_pos: String,
50// }
51
52#[derive(Deserialize, Serialize)]
53#[derive(Clone, Debug, Default, PartialEq, PartialOrd)]
54#[serde(rename_all = "PascalCase")]
55pub struct XSection {
56    // TODO: the `alias` is an evil hack and breaks serialization
57    #[serde(rename = "XSPos", alias = "XSNeg")]
58    pub xs_pos: XSScale,
59    #[serde(alias = "MaxWeightNeg")]
60    pub max_weight_pos: f64,
61    #[serde(alias = "TotalEventsNeg")]
62    pub total_events_pos: u64,
63    #[serde(alias = "AcceptedEventsNeg")]
64    pub accepted_events_pos: u64,
65    #[serde(alias = "FactorNeg")]
66    pub factor_pos: String,
67}
68
69#[derive(Deserialize, Serialize)]
70#[derive(Clone, Debug, Default, PartialEq, PartialOrd)]
71pub struct Contribution {
72    #[serde(rename = "@name")]
73    pub name: String,
74    pub xsection: XSScale,
75    pub rw: Reweight,
76}
77
78#[derive(Deserialize, Serialize)]
79#[derive(Clone, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Hash)]
80pub struct Reweight {
81    pub rwentry: Vec<String>,
82}
83
84#[derive(Clone, Debug, Default, PartialEq, PartialOrd)]
85pub struct XSScale (pub [f64; 2]);
86
87impl<'de> Deserialize<'de> for XSScale {
88    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
89    where
90        D: serde::Deserializer<'de> {
91        let xs_scale_str = String::deserialize(deserializer)?;
92        let mut entries = xs_scale_str.split(',');
93        let mut xs_scale = [0.; 2];
94        for q in &mut xs_scale {
95            let Some(p) = entries.next() else {
96                return Err(serde::de::Error::custom(
97                    ParseErr::NumEntries(xs_scale_str, 2)
98                ));
99            };
100            *q = p.parse().map_err(serde::de::Error::custom)?;
101        }
102        if entries.next().is_some() {
103            return Err(serde::de::Error::custom(
104                ParseErr::NumEntries(xs_scale_str, 2)
105            ));
106        }
107        Ok(Self(xs_scale))
108    }
109}
110
111impl Serialize for XSScale {
112    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
113    where
114        S: Serializer,
115    {
116        let p = self.0;
117        serializer.serialize_str(
118            &format!("{},{}", p[0], p[1])
119        )
120    }
121}
122
123#[cfg(test)]
124mod tests {
125    use super::*;
126
127    #[test]
128    fn deser_xsection_neg() {
129        pub const REF_XS: &str = r#"<XSection>
130 <XSNeg> 687.103,0.978277 </XSNeg>
131 <MaxWeightNeg> 796475 </MaxWeightNeg>
132 <TotalEventsNeg> 488338734 </TotalEventsNeg>
133 <AcceptedEventsNeg> 493310 </AcceptedEventsNeg>
134 <FactorNeg> 803.98,1.14468 </FactorNeg>
135</XSection>"#;
136        let _xs: XSection = quick_xml::de::from_str(REF_XS).unwrap();
137    }
138
139    #[test]
140    fn deser_xsection_pos() {
141        pub const REF_XS: &str = r#"<XSection>
142 <XSPos> 687.103,0.978277 </XSPos>
143 <MaxWeightPos> 796475 </MaxWeightPos>
144 <TotalEventsPos> 488338734 </TotalEventsPos>
145 <AcceptedEventsPos> 493310 </AcceptedEventsPos>
146 <FactorPos> 803.98,1.14468 </FactorPos>
147</XSection>"#;
148        let _xs: XSection = quick_xml::de::from_str(REF_XS).unwrap();
149    }
150
151    #[test]
152    fn deser_normalization() {
153        pub const REF_NORM: &str = r#"<?xml version="1.0" encoding="UTF-8"?>
154<Normalization name="Cm">
155<!--
156File generated with STRIPPER v0.1 for online data base
157-->
158<XSection>
159 <XSNeg> 687.103,0.978277 </XSNeg>
160 <MaxWeightNeg> 796475 </MaxWeightNeg>
161 <TotalEventsNeg> 488338734 </TotalEventsNeg>
162 <AcceptedEventsNeg> 493310 </AcceptedEventsNeg>
163 <FactorNeg> 803.98,1.14468 </FactorNeg>
164</XSection>
165<Contribution name="Cm">
166  <xsection> 687.103,0.978277</xsection>
167  <rw>
168    <rwentry> x1 </rwentry>
169    <rwentry> x2 </rwentry>
170    <rwentry> log(muR**2) </rwentry>
171    <rwentry> log(muF**2) </rwentry>
172  </rw>
173</Contribution>
174<NumberOfRejectedEvents> 0 , 100</NumberOfRejectedEvents>
175</Normalization>
176"#;
177        let norm: Normalization = quick_xml::de::from_str(REF_NORM).unwrap();
178        assert_eq!(norm.name, "Cm");
179        assert_eq!(norm.contribution.name, "Cm");
180        assert_eq!(norm.contribution.xsection.0, [687.103,0.978277]);
181        assert_eq!(
182            norm.contribution.rw.rwentry,
183            ["x1", "x2", "log(muR**2)", "log(muF**2)"]
184        );
185        let XSection {
186            xs_pos,
187            max_weight_pos,
188            total_events_pos,
189            accepted_events_pos,
190            factor_pos
191        } = norm.xsection;
192        assert_eq!(xs_pos, XSScale([687.103, 0.978277]));
193        assert_eq!(max_weight_pos, 796475.);
194        assert_eq!(accepted_events_pos, 493310);
195        assert_eq!(total_events_pos, 488338734);
196        assert_eq!(factor_pos, "803.98,1.14468");
197    }
198}