fastiron_stats/structures/
raw.rs

1//! Modelling code
2//!
3//! This module contains all code used to model data produced by the main executable.
4
5use std::{fmt::Display, fs::File, iter::zip, ops::Index};
6
7//~~~~~~~~~~~~~~
8// Tallies data
9//~~~~~~~~~~~~~~
10
11/// Number of fields presented in the tallies report produced
12/// by `fastiron`.
13pub const N_TALLIED_DATA: usize = 17;
14
15/// Enum used to represent & map tallied data and their indexes.
16#[derive(Debug, Clone, Copy)]
17pub enum TalliedData {
18    /// Cycle index.
19    Cycle = 0,
20    /// Number of particles at the start of the cycle.
21    Start = 1,
22    /// Number of particles sourced.
23    Source = 2,
24    /// Number of particles Russian-Rouletted.
25    Rr = 3,
26    /// Number of split particles.
27    Split = 4,
28    /// Number of absorbed particles.
29    Absorb = 5,
30    /// Number of particles that underwent a scatter reaction.
31    Scatter = 6,
32    /// Number of particles that underwent a fission reaction.
33    Fission = 7,
34    /// Number of particles produced by a fission reaction.
35    Produce = 8,
36    /// Number of particles that underwent a reaction.
37    Collision = 9,
38    /// Number of particles that escaped the problem.
39    Escape = 10,
40    /// Number of particles that reached census.
41    Census = 11,
42    /// Number of segments computed this cycle.
43    NumSeg = 12,
44    /// Overall scalar flux value this cycle.
45    ScalarFlux = 13,
46    /// Time spent section this cycle.
47    PopulationControl = 14,
48    /// Time spent section this cycle.
49    CycleTracking = 15,
50    /// Time spent section this cycle.
51    CycleSync = 16,
52}
53
54/// Custom [`Display`] implementation for easier tics generation when plotting.
55impl Display for TalliedData {
56    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
57        write!(
58            f,
59            "{}",
60            match *self {
61                TalliedData::Cycle => "Cycle",
62                TalliedData::Start => "Start",
63                TalliedData::Source => "Source",
64                TalliedData::Rr => "Rr",
65                TalliedData::Split => "Split",
66                TalliedData::Absorb => "Absorb",
67                TalliedData::Scatter => "Scatter",
68                TalliedData::Fission => "Fission",
69                TalliedData::Produce => "Produce",
70                TalliedData::Collision => "Collision",
71                TalliedData::Escape => "Escape",
72                TalliedData::Census => "Census",
73                TalliedData::NumSeg => "NumSeg",
74                TalliedData::ScalarFlux => "ScalarFlux",
75                TalliedData::PopulationControl => "PopulationControl",
76                TalliedData::CycleTracking => "CycleTracking",
77                TalliedData::CycleSync => "CycleSync",
78            }
79        )
80    }
81}
82
83/// Structure used to model tallied events, interpreted as discrete finite random
84/// variables.
85///
86/// This structure is not meant to be modified. It should be initialized with all
87/// values using the provided constructor.
88#[derive(Debug)]
89pub struct TalliedVariable {
90    /// Values taken by the random variables.
91    pub values: Vec<f64>,
92    /// Associated mean.
93    pub mean: f64,
94    /// Associated variance.
95    pub variance: f64,
96}
97
98impl TalliedVariable {
99    /// Constructor. Takes a slice as values and computes key associated values
100    /// before returning the object.
101    pub fn new(values: &[f64]) -> Self {
102        let n_val = values.len() as f64;
103        let val = values.to_vec();
104        let mut mean = val.iter().sum();
105        mean /= n_val;
106        let mut var = val.iter().map(|xi| (xi - mean) * (xi - mean)).sum();
107        var /= n_val;
108
109        Self {
110            values: val,
111            mean,
112            variance: var,
113        }
114    }
115
116    /// Returns the number of values taken by the (discrete, finite) random variable
117    pub fn n_val(&self) -> usize {
118        self.values.len()
119    }
120}
121
122/// Returns the covariance of two given [TalliedVariable].
123pub fn covariance(x: &TalliedVariable, y: &TalliedVariable) -> f64 {
124    assert_eq!(x.n_val(), y.n_val());
125    let iter = zip(x.values.iter(), y.values.iter());
126    let mut cov = iter.map(|(xi, yi)| (xi - x.mean) * (yi - y.mean)).sum();
127    cov /= x.n_val() as f64;
128    cov
129}
130
131/// Returns the correlation coefficient of two given [TalliedVariable].
132///
133/// The function checks if `x` and `y` have non-zero variance. If this is the case,
134/// 0 is returned. It means variables are independent. While this may be technically
135/// false, it allows for generic computations.
136pub fn correlation(x: &TalliedVariable, y: &TalliedVariable) -> f64 {
137    if (x.variance == 0.0) | (y.variance == 0.0) {
138        //
139        return 0.0;
140    }
141    let cov = covariance(x, y);
142    cov / (x.variance * y.variance).sqrt()
143}
144
145/// Structure modelling a report produced by the main executable.
146pub struct TalliesReport {
147    /// Data represented as variable.
148    pub tallies_data: [TalliedVariable; N_TALLIED_DATA],
149}
150
151/// Custom [`From`] implementation for processing at initialization.
152impl From<File> for TalliesReport {
153    fn from(file: File) -> Self {
154        let mut reader = csv::ReaderBuilder::new().delimiter(b';').from_reader(file);
155        let mut values: [Vec<f64>; N_TALLIED_DATA] = Default::default();
156        values.iter_mut().for_each(|v| v.reserve(100));
157        // for each line
158        for result in reader.records() {
159            let mut record = result.unwrap();
160            record.trim();
161            // for each column
162            (0..N_TALLIED_DATA).for_each(|idx| {
163                let val = record.get(idx).unwrap();
164                values[idx].push(val.parse().unwrap())
165            })
166        }
167        // convert value vectors to our structure
168        Self {
169            tallies_data: values.map(|val| TalliedVariable::new(&val)),
170        }
171    }
172}
173
174impl Index<TalliedData> for TalliesReport {
175    type Output = TalliedVariable;
176
177    fn index(&self, tallied_data: TalliedData) -> &Self::Output {
178        &self.tallies_data[tallied_data as usize]
179    }
180}
181
182//~~~~~~~~~~~~
183// Timer data
184//~~~~~~~~~~~~
185
186/// Number of sections in a timers report.
187pub const N_TIMERS: usize = 6;
188
189/// Array of the sections of a timers report.
190pub const TIMERS_ARR: [TimerSV; N_TIMERS] = [
191    TimerSV::Main,
192    TimerSV::PopulationControl,
193    TimerSV::CycleTracking,
194    TimerSV::CycleTrackingProcess,
195    TimerSV::CycleTrackingSort,
196    TimerSV::CycleSync,
197];
198
199/// Enum used to represent & map timers breakdown and their indexes.
200#[derive(Debug, Clone, Copy)]
201pub enum TimerSV {
202    Main = 0,
203    PopulationControl = 1,
204    CycleTracking = 2,
205    CycleTrackingProcess = 3,
206    CycleTrackingSort = 4,
207    CycleSync = 5,
208}
209
210/// Custom [`Display`] implementation for easier tics generation when plotting.
211impl Display for TimerSV {
212    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
213        write!(
214            f,
215            "{}",
216            match *self {
217                TimerSV::Main => "Section::Main",
218                TimerSV::PopulationControl => "Section::PopulationControl",
219                TimerSV::CycleTracking => "Section::CycleTracking",
220                TimerSV::CycleTrackingProcess => "Section::CycleTrackingProcess",
221                TimerSV::CycleTrackingSort => "Section::CycleTrackingSort",
222                TimerSV::CycleSync => "Section::CycleSync",
223            }
224        )
225    }
226}
227
228/// Structure used to represent the summarized data of the timers.
229#[derive(Default, Clone, Copy, Debug)]
230pub struct SummarizedVariable {
231    /// Average value taken by the timer.
232    pub mean: f64,
233    /// Lowest value taken by the timer.
234    pub lowest: f64,
235    /// Highest value taken by the timer.
236    pub highest: f64,
237    /// Sum of all value taken by the timer.
238    pub total: f64,
239}
240
241/// Structure used to represent the entire timer report of a single simulation
242pub struct TimerReport {
243    /// Array of the section timers.
244    pub timers_data: [SummarizedVariable; N_TIMERS],
245}
246
247/// Custom [`From`] implementation for processing at initialization.
248impl From<File> for TimerReport {
249    fn from(file: File) -> Self {
250        let mut res = [SummarizedVariable::default(); N_TIMERS];
251        let mut reader = csv::ReaderBuilder::new().delimiter(b';').from_reader(file);
252
253        // for each line
254        for (timer_idx, result) in reader.records().enumerate() {
255            let mut record = result.unwrap();
256            record.trim();
257            // lmao
258            res[timer_idx].lowest = record.get(2).unwrap().parse().unwrap();
259            res[timer_idx].mean = record.get(3).unwrap().parse().unwrap();
260            res[timer_idx].highest = record.get(4).unwrap().parse().unwrap();
261            res[timer_idx].total = record.get(5).unwrap().parse().unwrap();
262        }
263
264        Self { timers_data: res }
265    }
266}
267
268impl Index<TimerSV> for TimerReport {
269    type Output = SummarizedVariable;
270
271    fn index(&self, timer: TimerSV) -> &Self::Output {
272        &self.timers_data[timer as usize]
273    }
274}