Skip to main content

polysim_core/properties/
ensemble.rs

1use std::fmt;
2
3use crate::polymer::PolymerEnsemble;
4
5/// Summary statistics for a polymer ensemble.
6#[derive(Debug, Clone)]
7pub struct EnsembleStats {
8    pub num_chains: usize,
9    pub mn: f64,
10    pub mw: f64,
11    pub pdi: f64,
12    pub mn_min: f64,
13    pub mn_max: f64,
14    pub mn_median: f64,
15    pub mn_std_dev: f64,
16}
17
18impl EnsembleStats {
19    /// Computes statistics from an ensemble.
20    pub fn from_ensemble(ensemble: &PolymerEnsemble) -> Self {
21        let mut masses: Vec<f64> = ensemble.chains().iter().map(|c| c.mn).collect();
22        masses.sort_by(|a, b| a.partial_cmp(b).unwrap());
23
24        let n = masses.len();
25        let mn = ensemble.mn();
26        let mw = ensemble.mw();
27
28        let mn_median = if n % 2 == 1 {
29            masses[n / 2]
30        } else {
31            (masses[n / 2 - 1] + masses[n / 2]) / 2.0
32        };
33
34        let variance = masses.iter().map(|m| (m - mn).powi(2)).sum::<f64>() / n as f64;
35        let mn_std_dev = variance.sqrt();
36
37        Self {
38            num_chains: n,
39            mn,
40            mw,
41            pdi: ensemble.pdi(),
42            mn_min: masses[0],
43            mn_max: masses[n - 1],
44            mn_median,
45            mn_std_dev,
46        }
47    }
48}
49
50impl fmt::Display for EnsembleStats {
51    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
52        writeln!(f, "Ensemble Statistics")?;
53        writeln!(f, "  Chains:    {}", self.num_chains)?;
54        writeln!(f, "  Mn:        {:.1} g/mol", self.mn)?;
55        writeln!(f, "  Mw:        {:.1} g/mol", self.mw)?;
56        writeln!(f, "  PDI:       {:.3}", self.pdi)?;
57        writeln!(f, "  Median:    {:.1} g/mol", self.mn_median)?;
58        writeln!(f, "  Std dev:   {:.1} g/mol", self.mn_std_dev)?;
59        write!(
60            f,
61            "  Mn range:  {:.1} - {:.1} g/mol",
62            self.mn_min, self.mn_max
63        )
64    }
65}