polysim_core/distribution/flory.rs
1use rand::{Rng, RngCore};
2
3use super::ChainLengthDistribution;
4
5/// Flory (most probable) chain length distribution.
6///
7/// Characteristic of step-growth (polycondensation) polymers.
8/// The number distribution is geometric: P(n) = (1−p)·p^(n−1),
9/// where p = 1 − 1/Xn is the extent of reaction.
10///
11/// PDI is inherently ≈ 1+p (approaches 2.0 for high conversion);
12/// the `pdi` parameter is ignored.
13pub struct Flory;
14
15impl ChainLengthDistribution for Flory {
16 fn sample(
17 &self,
18 mn: f64,
19 _pdi: f64,
20 m0: f64,
21 num_chains: usize,
22 rng: &mut dyn RngCore,
23 ) -> Vec<usize> {
24 let xn = (mn / m0).max(1.0);
25 let p = 1.0 - 1.0 / xn;
26
27 (0..num_chains)
28 .map(|_| {
29 if p <= 0.0 {
30 return 1;
31 }
32 // Geometric sampling: n = ceil(ln(U) / ln(p))
33 let u: f64 = rng.random_range(f64::EPSILON..1.0);
34 let n = (u.ln() / p.ln()).ceil() as usize;
35 n.max(1)
36 })
37 .collect()
38 }
39
40 fn name(&self) -> &'static str {
41 "flory"
42 }
43}