Skip to main content

laddu_core/data/
event.rs

1use std::{fmt::Display, ops::Deref, sync::Arc};
2
3use indexmap::IndexMap;
4use serde::{Deserialize, Serialize};
5
6#[cfg(feature = "mpi")]
7use super::dataset::MpiDatasetLayout;
8use super::{Dataset, DatasetMetadata};
9use crate::{
10    variables::Variable,
11    vectors::{Vec3, Vec4},
12};
13
14/// An event that can be used to test the implementation of an
15/// [`Amplitude`](crate::amplitude::Amplitude). This particular event contains the reaction
16/// $`\gamma p \to K_S^0 K_S^0 p`$ with a polarized photon beam.
17pub fn test_event() -> EventData {
18    let pol_magnitude = 0.38562805;
19    let pol_angle = 0.05708078;
20    EventData {
21        p4s: vec![
22            Vec3::new(0.0, 0.0, 8.747).with_mass(0.0),
23            Vec3::new(0.119, 0.374, 0.222).with_mass(1.007),
24            Vec3::new(-0.112, 0.293, 3.081).with_mass(0.498),
25            Vec3::new(-0.007, -0.667, 5.446).with_mass(0.498),
26        ],
27        aux: vec![pol_magnitude, pol_angle],
28        weight: 0.48,
29    }
30}
31
32/// Raw event data in a [`Dataset`] containing all particle and auxiliary information.
33///
34/// An [`EventData`] instance owns the list of four-momenta (`p4s`), auxiliary scalars (`aux`),
35/// and weight recorded for a particular collision event. Use [`Event`] when you need a
36/// metadata-aware view with name-based helpers.
37#[derive(Debug, Clone, Default, Serialize, Deserialize)]
38pub struct EventData {
39    /// A list of four-momenta for each particle.
40    pub p4s: Vec<Vec4>,
41    /// A list of auxiliary scalar values associated with the event.
42    pub aux: Vec<f64>,
43    /// The weight given to the event.
44    pub weight: f64,
45}
46
47impl Display for EventData {
48    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
49        writeln!(f, "Event:")?;
50        writeln!(f, "  p4s:")?;
51        for p4 in &self.p4s {
52            writeln!(f, "    {}", p4.to_p4_string())?;
53        }
54        writeln!(f, "  aux:")?;
55        for (idx, value) in self.aux.iter().enumerate() {
56            writeln!(f, "    aux[{idx}]: {value}")?;
57        }
58        writeln!(f, "  weight:")?;
59        writeln!(f, "    {}", self.weight)?;
60        Ok(())
61    }
62}
63
64impl EventData {
65    /// Return a four-momentum from the sum of four-momenta at the given indices in the [`EventData`].
66    pub fn get_p4_sum<T: AsRef<[usize]>>(&self, indices: T) -> Vec4 {
67        indices.as_ref().iter().map(|i| self.p4s[*i]).sum::<Vec4>()
68    }
69
70    /// Boost all the four-momenta in the [`EventData`] to the rest frame of the given set of
71    /// four-momenta by indices.
72    pub fn boost_to_rest_frame_of<T: AsRef<[usize]>>(&self, indices: T) -> Self {
73        let frame = self.get_p4_sum(indices);
74        EventData {
75            p4s: self
76                .p4s
77                .iter()
78                .map(|p4| p4.boost(&(-frame.beta())))
79                .collect(),
80            aux: self.aux.clone(),
81            weight: self.weight,
82        }
83    }
84}
85
86#[derive(Debug, Clone, Default)]
87/// Columnar storage for one named four-momentum across all events.
88pub struct ColumnarP4Column {
89    pub(crate) px: Vec<f64>,
90    pub(crate) py: Vec<f64>,
91    pub(crate) pz: Vec<f64>,
92    pub(crate) e: Vec<f64>,
93}
94
95impl ColumnarP4Column {
96    /// Create an empty four-momentum column with capacity for `capacity` events.
97    pub fn with_capacity(capacity: usize) -> Self {
98        Self {
99            px: Vec::with_capacity(capacity),
100            py: Vec::with_capacity(capacity),
101            pz: Vec::with_capacity(capacity),
102            e: Vec::with_capacity(capacity),
103        }
104    }
105
106    /// Append one four-momentum to the column.
107    pub fn push(&mut self, p4: Vec4) {
108        self.px.push(p4.x);
109        self.py.push(p4.y);
110        self.pz.push(p4.z);
111        self.e.push(p4.t);
112    }
113
114    /// Return the number of four-momenta stored in this column.
115    pub fn len(&self) -> usize {
116        self.px.len()
117    }
118
119    /// Return true if this column stores no four-momenta.
120    pub fn is_empty(&self) -> bool {
121        self.px.is_empty()
122    }
123
124    /// Return the four-momentum at `event_index`.
125    pub fn get(&self, event_index: usize) -> Vec4 {
126        Vec4::new(
127            self.px[event_index],
128            self.py[event_index],
129            self.pz[event_index],
130            self.e[event_index],
131        )
132    }
133}
134
135/// Columnar dataset storage used by [`Dataset`].
136#[derive(Debug, Default)]
137pub struct DatasetStorage {
138    pub(crate) metadata: Arc<DatasetMetadata>,
139    pub(crate) p4: Vec<ColumnarP4Column>,
140    pub(crate) aux: Vec<Vec<f64>>,
141    pub(crate) weights: Vec<f64>,
142}
143
144impl Clone for DatasetStorage {
145    fn clone(&self) -> Self {
146        Self {
147            metadata: self.metadata.clone(),
148            p4: self.p4.clone(),
149            aux: self.aux.clone(),
150            weights: self.weights.clone(),
151        }
152    }
153}
154
155impl DatasetStorage {
156    /// Create columnar dataset storage from metadata, four-momentum columns, auxiliary columns, and weights.
157    pub fn new(
158        metadata: DatasetMetadata,
159        p4: Vec<ColumnarP4Column>,
160        aux: Vec<Vec<f64>>,
161        weights: Vec<f64>,
162    ) -> Self {
163        Self {
164            metadata: Arc::new(metadata),
165            p4,
166            aux,
167            weights,
168        }
169    }
170
171    /// Create empty columnar storage with the given metadata and event capacity.
172    pub(crate) fn empty_with_capacity(metadata: Arc<DatasetMetadata>, capacity: usize) -> Self {
173        Self {
174            p4: (0..metadata.p4_names().len())
175                .map(|_| ColumnarP4Column::with_capacity(capacity))
176                .collect(),
177            aux: (0..metadata.aux_names().len())
178                .map(|_| Vec::with_capacity(capacity))
179                .collect(),
180            weights: Vec::with_capacity(capacity),
181            metadata,
182        }
183    }
184
185    /// Append one ordered event row.
186    pub(crate) fn push_event_data(&mut self, event: &EventData) {
187        for (column, p4) in self.p4.iter_mut().zip(&event.p4s) {
188            column.push(*p4);
189        }
190        for (column, value) in self.aux.iter_mut().zip(&event.aux) {
191            column.push(*value);
192        }
193        self.weights.push(event.weight);
194    }
195
196    pub(crate) fn set_metadata(&mut self, metadata: Arc<DatasetMetadata>) {
197        self.metadata = metadata;
198    }
199
200    pub(crate) fn push_p4_column(&mut self, values: Vec<Vec4>) {
201        let mut column = ColumnarP4Column::with_capacity(values.len());
202        for value in values {
203            column.push(value);
204        }
205        self.p4.push(column);
206    }
207
208    pub(crate) fn push_aux_column(&mut self, values: Vec<f64>) {
209        self.aux.push(values);
210    }
211
212    /// Convert this columnar dataset back to a row-event dataset.
213    pub fn to_dataset(&self) -> Dataset {
214        let events = (0..self.n_events())
215            .map(|event_index| Arc::new(self.event_data(event_index)))
216            .collect::<Vec<_>>();
217        #[cfg(not(feature = "mpi"))]
218        let dataset = Dataset::new_local(events, self.metadata.clone());
219        #[cfg(feature = "mpi")]
220        let mut dataset = Dataset::new_local(events, self.metadata.clone());
221        #[cfg(feature = "mpi")]
222        {
223            if let Some(world) = crate::mpi::get_world() {
224                dataset.mpi_layout = Some(MpiDatasetLayout::Canonical);
225                dataset.set_cached_global_event_count_from_world(&world);
226                dataset.set_cached_global_weighted_sum_from_world(&world);
227            }
228        }
229        dataset
230    }
231
232    /// Access metadata.
233    pub(crate) fn metadata(&self) -> &DatasetMetadata {
234        &self.metadata
235    }
236
237    /// Number of local events.
238    pub(crate) fn n_events(&self) -> usize {
239        self.weights.len()
240    }
241
242    /// Retrieve a p4 value by row and p4 index.
243    pub(crate) fn p4(&self, event_index: usize, p4_index: usize) -> Vec4 {
244        self.p4[p4_index].get(event_index)
245    }
246
247    /// Retrieve an aux value by row and aux index.
248    pub(crate) fn aux(&self, event_index: usize, aux_index: usize) -> f64 {
249        self.aux[aux_index][event_index]
250    }
251
252    /// Retrieve event weight by row index.
253    pub(crate) fn weight(&self, event_index: usize) -> f64 {
254        self.weights[event_index]
255    }
256
257    pub(crate) fn event_data(&self, event_index: usize) -> EventData {
258        let mut p4s = Vec::with_capacity(self.p4.len());
259        for p4_index in 0..self.p4.len() {
260            p4s.push(self.p4(event_index, p4_index));
261        }
262        let mut aux = Vec::with_capacity(self.aux.len());
263        for aux_index in 0..self.aux.len() {
264            aux.push(self.aux(event_index, aux_index));
265        }
266        EventData {
267            p4s,
268            aux,
269            weight: self.weight(event_index),
270        }
271    }
272
273    fn row_view(&self, event_index: usize) -> ColumnarEventView<'_> {
274        ColumnarEventView {
275            storage: self,
276            event_index,
277        }
278    }
279
280    #[allow(dead_code)]
281    pub(crate) fn for_each_event_local<F>(&self, mut op: F)
282    where
283        F: FnMut(usize, Event<'_>),
284    {
285        for event_index in 0..self.n_events() {
286            let row = self.row_view(event_index);
287            let view = Event {
288                row: EventRow::Columnar(row),
289                metadata: &self.metadata,
290            };
291            op(event_index, view);
292        }
293    }
294
295    pub(crate) fn event_view(&self, event_index: usize) -> Event<'_> {
296        let row = self.row_view(event_index);
297        Event {
298            row: EventRow::Columnar(row),
299            metadata: self.metadata(),
300        }
301    }
302}
303
304#[derive(Debug)]
305struct ColumnarEventView<'a> {
306    storage: &'a DatasetStorage,
307    event_index: usize,
308}
309
310#[allow(dead_code)]
311impl ColumnarEventView<'_> {
312    fn p4(&self, p4_index: usize) -> Vec4 {
313        self.storage.p4(self.event_index, p4_index)
314    }
315
316    fn aux(&self, aux_index: usize) -> f64 {
317        self.storage.aux(self.event_index, aux_index)
318    }
319
320    fn weight(&self) -> f64 {
321        self.storage.weight(self.event_index)
322    }
323}
324
325#[derive(Debug)]
326enum EventRow<'a> {
327    Columnar(ColumnarEventView<'a>),
328    Owned(&'a EventData),
329}
330
331impl EventRow<'_> {
332    fn p4(&self, p4_index: usize) -> Vec4 {
333        match self {
334            Self::Columnar(row) => row.p4(p4_index),
335            Self::Owned(event) => event.p4s[p4_index],
336        }
337    }
338
339    fn aux(&self, aux_index: usize) -> f64 {
340        match self {
341            Self::Columnar(row) => row.aux(aux_index),
342            Self::Owned(event) => event.aux[aux_index],
343        }
344    }
345
346    fn weight(&self) -> f64 {
347        match self {
348            Self::Columnar(row) => row.weight(),
349            Self::Owned(event) => event.weight,
350        }
351    }
352
353    fn n_p4(&self) -> usize {
354        match self {
355            Self::Columnar(row) => row.storage.p4.len(),
356            Self::Owned(event) => event.p4s.len(),
357        }
358    }
359
360    fn n_aux(&self) -> usize {
361        match self {
362            Self::Columnar(row) => row.storage.aux.len(),
363            Self::Owned(event) => event.aux.len(),
364        }
365    }
366}
367
368/// Borrowed, metadata-aware event access for variable and amplitude evaluation.
369#[derive(Debug)]
370pub struct Event<'a> {
371    row: EventRow<'a>,
372    metadata: &'a DatasetMetadata,
373}
374
375/// Shared event access interface implemented by borrowed and owned event rows.
376pub trait EventLike {
377    /// Retrieve a four-momentum by positional index.
378    fn p4_at(&self, p4_index: usize) -> Vec4;
379
380    /// Retrieve an auxiliary scalar by positional index.
381    fn aux_at(&self, aux_index: usize) -> f64;
382
383    /// Number of four-momenta in this event.
384    fn n_p4(&self) -> usize;
385
386    /// Number of auxiliary values in this event.
387    fn n_aux(&self) -> usize;
388
389    /// Retrieve event weight.
390    fn weight(&self) -> f64;
391
392    /// Retrieve the dataset metadata attached to this event.
393    fn metadata(&self) -> &DatasetMetadata;
394
395    /// Retrieve a four-momentum by metadata name.
396    fn p4(&self, name: &str) -> Option<Vec4> {
397        let selection = self.metadata().p4_selection(name)?;
398        Some(
399            selection
400                .indices()
401                .iter()
402                .map(|index| self.p4_at(*index))
403                .sum(),
404        )
405    }
406
407    /// Retrieve an auxiliary scalar by metadata name.
408    fn aux(&self, name: &str) -> Option<f64> {
409        let index = self.metadata().aux_index(name)?;
410        Some(self.aux_at(index))
411    }
412
413    /// Retrieve the sum of multiple four-momenta selected by name.
414    fn get_p4_sum<N>(&self, names: N) -> Option<Vec4>
415    where
416        Self: Sized,
417        N: IntoIterator,
418        N::Item: AsRef<str>,
419    {
420        names
421            .into_iter()
422            .map(|name| self.p4(name.as_ref()))
423            .collect::<Option<Vec<_>>>()
424            .map(|momenta| momenta.into_iter().sum())
425    }
426}
427
428impl EventLike for Event<'_> {
429    /// Retrieve a four-momentum by positional index.
430    fn p4_at(&self, p4_index: usize) -> Vec4 {
431        self.row.p4(p4_index)
432    }
433
434    /// Retrieve an auxiliary scalar by positional index.
435    fn aux_at(&self, aux_index: usize) -> f64 {
436        self.row.aux(aux_index)
437    }
438
439    /// Number of four-momenta in this event.
440    fn n_p4(&self) -> usize {
441        self.row.n_p4()
442    }
443
444    /// Number of auxiliary values in this event.
445    fn n_aux(&self) -> usize {
446        self.row.n_aux()
447    }
448
449    /// Retrieve event weight.
450    fn weight(&self) -> f64 {
451        self.row.weight()
452    }
453
454    fn metadata(&self) -> &DatasetMetadata {
455        self.metadata
456    }
457}
458
459impl Event<'_> {
460    /// Retrieve a four-momentum by positional index.
461    pub fn p4_at(&self, p4_index: usize) -> Vec4 {
462        EventLike::p4_at(self, p4_index)
463    }
464
465    /// Retrieve an auxiliary scalar by positional index.
466    pub fn aux_at(&self, aux_index: usize) -> f64 {
467        EventLike::aux_at(self, aux_index)
468    }
469
470    /// Number of four-momenta in this event.
471    pub fn n_p4(&self) -> usize {
472        EventLike::n_p4(self)
473    }
474
475    /// Number of auxiliary values in this event.
476    pub fn n_aux(&self) -> usize {
477        EventLike::n_aux(self)
478    }
479
480    /// Retrieve a four-momentum by metadata name.
481    pub fn p4(&self, name: &str) -> Option<Vec4> {
482        EventLike::p4(self, name)
483    }
484
485    /// Retrieve an auxiliary scalar by metadata name.
486    pub fn aux(&self, name: &str) -> Option<f64> {
487        EventLike::aux(self, name)
488    }
489
490    /// Retrieve event weight.
491    pub fn weight(&self) -> f64 {
492        EventLike::weight(self)
493    }
494
495    /// Copy this borrowed event into owned raw event data.
496    pub fn to_event_data(&self) -> EventData {
497        EventData {
498            p4s: (0..self.n_p4()).map(|index| self.p4_at(index)).collect(),
499            aux: (0..self.n_aux()).map(|index| self.aux_at(index)).collect(),
500            weight: self.weight(),
501        }
502    }
503
504    /// Retrieve the sum of multiple four-momenta selected by name.
505    pub fn get_p4_sum<N>(&self, names: N) -> Option<Vec4>
506    where
507        N: IntoIterator,
508        N::Item: AsRef<str>,
509    {
510        EventLike::get_p4_sum(self, names)
511    }
512
513    /// Evaluate a [`Variable`] against this event.
514    pub fn evaluate<V: Variable>(&self, variable: &V) -> f64 {
515        variable.value(self)
516    }
517}
518
519impl Display for Event<'_> {
520    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
521        writeln!(f, "Event:")?;
522        writeln!(f, "  p4s:")?;
523        for index in 0..self.n_p4() {
524            let label = self
525                .metadata
526                .p4_names()
527                .get(index)
528                .map_or_else(|| format!("p4[{index}]"), Clone::clone);
529            writeln!(f, "    {label}: {}", self.p4_at(index).to_p4_string())?;
530        }
531        writeln!(f, "  aux:")?;
532        for index in 0..self.n_aux() {
533            let label = self
534                .metadata
535                .aux_names()
536                .get(index)
537                .map_or_else(|| format!("aux[{index}]"), Clone::clone);
538            writeln!(f, "    {label}: {}", self.aux_at(index))?;
539        }
540        writeln!(f, "  weight:")?;
541        writeln!(f, "    {}", self.weight())?;
542        Ok(())
543    }
544}
545
546/// Owned metadata-aware event data.
547///
548/// This is useful for detached events, MPI fetches, and Python-owned event objects. Use
549/// [`OwnedEvent::as_event`] to evaluate variables and amplitudes through the standard borrowed
550/// [`Event`] interface.
551#[derive(Clone, Debug)]
552pub struct OwnedEvent {
553    event: Arc<EventData>,
554    metadata: Arc<DatasetMetadata>,
555}
556
557impl OwnedEvent {
558    /// Create a new metadata-aware event from raw data and dataset metadata.
559    pub fn new(event: Arc<EventData>, metadata: Arc<DatasetMetadata>) -> Self {
560        Self { event, metadata }
561    }
562
563    /// Borrow the raw [`EventData`].
564    pub fn data(&self) -> &EventData {
565        &self.event
566    }
567
568    /// Obtain a clone of the underlying [`EventData`] handle.
569    pub fn data_arc(&self) -> Arc<EventData> {
570        self.event.clone()
571    }
572
573    /// Borrow this owned row as an [`Event`] suitable for variable and amplitude evaluation.
574    pub fn as_event(&self) -> Event<'_> {
575        Event {
576            row: EventRow::Owned(&self.event),
577            metadata: &self.metadata,
578        }
579    }
580
581    /// Return the four-momenta stored in this event keyed by their registered names.
582    pub fn p4s(&self) -> IndexMap<&str, Vec4> {
583        let mut map = IndexMap::with_capacity(self.metadata.p4_names.len());
584        for (idx, name) in self.metadata.p4_names.iter().enumerate() {
585            if let Some(p4) = self.event.p4s.get(idx) {
586                map.insert(name.as_str(), *p4);
587            }
588        }
589        map
590    }
591
592    /// Return the auxiliary scalars stored in this event keyed by their registered names.
593    pub fn aux(&self) -> IndexMap<&str, f64> {
594        let mut map = IndexMap::with_capacity(self.metadata.aux_names.len());
595        for (idx, name) in self.metadata.aux_names.iter().enumerate() {
596            if let Some(value) = self.event.aux.get(idx) {
597                map.insert(name.as_str(), *value);
598            }
599        }
600        map
601    }
602
603    /// Return the event weight.
604    pub fn weight(&self) -> f64 {
605        self.event.weight
606    }
607
608    /// Retrieve the dataset metadata attached to this event.
609    pub fn metadata(&self) -> &DatasetMetadata {
610        &self.metadata
611    }
612
613    /// Clone the metadata handle associated with this event.
614    pub fn metadata_arc(&self) -> Arc<DatasetMetadata> {
615        self.metadata.clone()
616    }
617
618    /// Retrieve a four-momentum (or aliased sum) by name.
619    pub fn p4(&self, name: &str) -> Option<Vec4> {
620        EventLike::p4(self, name)
621    }
622
623    fn resolve_p4_indices<N>(&self, names: N) -> Vec<usize>
624    where
625        N: IntoIterator,
626        N::Item: AsRef<str>,
627    {
628        let mut indices = Vec::new();
629        for name in names {
630            let name_ref = name.as_ref();
631            if let Some(selection) = self.metadata.p4_selection(name_ref) {
632                indices.extend_from_slice(selection.indices());
633            } else {
634                panic!("Unknown particle name '{name}'", name = name_ref);
635            }
636        }
637        indices
638    }
639
640    /// Return a four-momentum formed by summing four-momenta with the specified names.
641    pub fn get_p4_sum<N>(&self, names: N) -> Vec4
642    where
643        N: IntoIterator,
644        N::Item: AsRef<str>,
645    {
646        let indices = self.resolve_p4_indices(names);
647        self.event.get_p4_sum(&indices)
648    }
649
650    /// Boost all four-momenta into the rest frame defined by the specified particle names.
651    pub fn boost_to_rest_frame_of<N>(&self, names: N) -> EventData
652    where
653        N: IntoIterator,
654        N::Item: AsRef<str>,
655    {
656        let indices = self.resolve_p4_indices(names);
657        self.event.boost_to_rest_frame_of(&indices)
658    }
659}
660
661impl EventLike for OwnedEvent {
662    fn p4_at(&self, p4_index: usize) -> Vec4 {
663        self.event.p4s[p4_index]
664    }
665
666    fn aux_at(&self, aux_index: usize) -> f64 {
667        self.event.aux[aux_index]
668    }
669
670    fn n_p4(&self) -> usize {
671        self.event.p4s.len()
672    }
673
674    fn n_aux(&self) -> usize {
675        self.event.aux.len()
676    }
677
678    fn weight(&self) -> f64 {
679        self.event.weight
680    }
681
682    fn metadata(&self) -> &DatasetMetadata {
683        &self.metadata
684    }
685}
686
687impl Display for OwnedEvent {
688    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
689        self.as_event().fmt(f)
690    }
691}
692
693impl Deref for OwnedEvent {
694    type Target = EventData;
695
696    fn deref(&self) -> &Self::Target {
697        &self.event
698    }
699}
700
701impl AsRef<EventData> for OwnedEvent {
702    fn as_ref(&self) -> &EventData {
703        self.data()
704    }
705}