use pantometry::prelude::*;
#[test]
fn shot_noise_is_root_n_and_does_not_care_how_many_cores_found_it() {
let mean_signal = 10_000.0;
let draw = move |_: u64, mut rng: Rng| rng.poisson(mean_signal) as f64;
let single = Ensemble::new(31, 50_000)
.estimate(draw)
.expect("many samples");
let many = Ensemble::new(31, 50_000)
.with_threads(12)
.estimate(draw)
.expect("many samples");
assert_eq!(single.mean.to_bits(), many.mean.to_bits());
assert_eq!(
single.standard_error.to_bits(),
many.standard_error.to_bits()
);
assert!(
many.within(4.0, mean_signal),
"{mean_signal} is {:.2} standard errors from {:.2}",
(many.mean - mean_signal).abs() / many.standard_error,
many.mean
);
let want = mean_signal.sqrt();
let got = many.standard_deviation();
assert!(
(got / want - 1.0).abs() < 0.02,
"shot noise {got:.2} against √n = {want:.2}"
);
assert!(
((many.mean / got) / want - 1.0).abs() < 0.03,
"SNR {:.1} against √n = {want:.1}",
many.mean / got
);
}
#[test]
fn a_sweep_of_whole_simulations_is_reproducible_across_threads() {
let sweep = |_: u64, mut rng: Rng| {
let length = 62.0 * (1.0 + 0.03 * (2.0 * rng.unit() - 1.0));
let ambient = Temperature::celsius(25.0);
let mut net = ThermalNetwork::new("motor");
let w = net.node(
"winding",
Substance::copper(),
Volume::cm3(18.0),
Length::mm(2.0),
ambient,
);
let h = net.node_losing_to(
"housing",
Substance::aluminium_6061(),
Volume::cm3(220.0),
Length::mm(4.0),
ambient,
Environment::still_air(ambient, Area::cm2(420.0)),
);
net.link(w, h, Conductance::w_per_k(0.9)).unwrap();
net.absorbing(w).unwrap();
let coil = Winding::of_copper("coil", Length::m(length), 0.35e-6, ambient)
.driven_at(Current::a(2.0));
let mut watts = coil.dissipation().to_si();
for _ in 0..30 {
let settled = net.steady_state(Power::w(watts)).unwrap();
watts = coil.dissipation_at(settled.temperature(w)).to_si();
}
net.steady_state(Power::w(watts))
.unwrap()
.temperature(w)
.to_si()
- 273.15
};
let one = Ensemble::new(1234, 400).run(sweep);
let twelve = Ensemble::new(1234, 400).with_threads(12).run(sweep);
for (i, (a, b)) in one.iter().zip(&twelve).enumerate() {
assert_eq!(a.to_bits(), b.to_bits(), "sample {i}: {a} against {b}");
}
let hottest = one.iter().cloned().fold(f64::MIN, f64::max);
let coolest = one.iter().cloned().fold(f64::MAX, f64::min);
assert!(
hottest - coolest > 1.0,
"the tolerance should matter; spread {:.3} K",
hottest - coolest
);
assert!(
(60.0..110.0).contains(&coolest),
"the coolest sample settles at {coolest:.1} C"
);
}