Skip to main content

polysim_core/polymer/
ensemble.rs

1use crate::error::PolySimError;
2
3use super::PolymerChain;
4
5/// A collection of polymer chains representing a polydisperse sample.
6#[derive(Debug, Clone)]
7pub struct PolymerEnsemble {
8    chains: Vec<PolymerChain>,
9}
10
11impl PolymerEnsemble {
12    /// Creates a new ensemble from a vector of chains.
13    ///
14    /// # Errors
15    ///
16    /// Returns [`PolySimError::EmptyEnsemble`] if `chains` is empty.
17    pub fn new(chains: Vec<PolymerChain>) -> Result<Self, PolySimError> {
18        if chains.is_empty() {
19            return Err(PolySimError::EmptyEnsemble);
20        }
21        Ok(Self { chains })
22    }
23
24    /// Returns a reference to the individual chains.
25    pub fn chains(&self) -> &[PolymerChain] {
26        &self.chains
27    }
28
29    /// Number of chains in the ensemble (always ≥ 1).
30    pub fn len(&self) -> usize {
31        self.chains.len()
32    }
33
34    /// Always returns `false` — an ensemble is guaranteed non-empty by construction.
35    pub fn is_empty(&self) -> bool {
36        false
37    }
38
39    /// Number-average molecular weight: Mn = Σ Mi / N
40    pub fn mn(&self) -> f64 {
41        let sum: f64 = self.chains.iter().map(|c| c.mn).sum();
42        sum / self.chains.len() as f64
43    }
44
45    /// Weight-average molecular weight: Mw = Σ Mi² / Σ Mi
46    pub fn mw(&self) -> f64 {
47        let sum_mi: f64 = self.chains.iter().map(|c| c.mn).sum();
48        let sum_mi2: f64 = self.chains.iter().map(|c| c.mn * c.mn).sum();
49        sum_mi2 / sum_mi
50    }
51
52    /// Polydispersity index: PDI = Mw / Mn
53    pub fn pdi(&self) -> f64 {
54        self.mw() / self.mn()
55    }
56}