lindhard/ion/stopping/
straggling.rs1use super::bethe::EffectiveCharge;
23use super::{check_energy, target, Ion, StoppingError};
24use crate::constants::COULOMB_E2;
25use crate::material::Material;
26use std::f64::consts::PI;
27
28#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
30pub enum StragglingModel {
31 #[default]
33 Bohr,
34 BohrRelativistic,
39}
40
41pub fn variance_per_atom(
43 model: StragglingModel,
44 charge: EffectiveCharge,
45 ion: &Ion,
46 target_z: u8,
47 energy_ev: f64,
48) -> Result<f64, StoppingError> {
49 check_energy(energy_ev)?;
50 target(target_z)?;
51 let mc2 = ion.mass_kg() * crate::constants::SPEED_OF_LIGHT.powi(2);
52 let gamma = 1.0 + energy_ev * crate::units::J_PER_EV / mc2;
53 let beta2 = 1.0 - 1.0 / (gamma * gamma);
54 let z = charge.charge(ion.z(), beta2.sqrt());
55 let base = 4.0 * PI * z * z * f64::from(target_z) * COULOMB_E2 * COULOMB_E2;
56 Ok(match model {
57 StragglingModel::Bohr => base,
58 StragglingModel::BohrRelativistic => base * (1.0 - 0.5 * beta2) / (1.0 - beta2),
59 })
60}
61
62pub fn variance_per_atom_material(
65 model: StragglingModel,
66 charge: EffectiveCharge,
67 ion: &Ion,
68 material: &Material,
69 energy_ev: f64,
70) -> Result<f64, StoppingError> {
71 let mut s = 0.0;
72 for c in material.components() {
73 s += c.atom_fraction() * variance_per_atom(model, charge, ion, c.z(), energy_ev)?;
74 }
75 Ok(s)
76}
77
78#[cfg(test)]
79mod tests {
80 use super::*;
81
82 #[test]
83 fn bohr_scales_as_z1_squared_z2() {
84 let f = |z1, z2| {
85 variance_per_atom(
86 StragglingModel::Bohr,
87 EffectiveCharge::Bare,
88 &Ion::new(z1).unwrap(),
89 z2,
90 1e5,
91 )
92 .unwrap()
93 };
94 assert!((f(2, 14) / f(1, 14) - 4.0).abs() < 1e-12);
95 assert!((f(1, 28) / f(1, 14) - 2.0).abs() < 1e-12);
96 }
97
98 #[test]
99 fn bohr_magnitude_for_proton_in_si() {
100 let v = variance_per_atom(
102 StragglingModel::Bohr,
103 EffectiveCharge::Bare,
104 &Ion::proton(),
105 14,
106 1e6,
107 )
108 .unwrap();
109 assert!((v / 9.36e-54 - 1.0).abs() < 5e-3, "{v}");
110 }
111
112 #[test]
113 fn relativistic_factor_exceeds_one() {
114 let g = |m| variance_per_atom(m, EffectiveCharge::Bare, &Ion::proton(), 14, 5e8).unwrap();
115 assert!(g(StragglingModel::BohrRelativistic) > g(StragglingModel::Bohr));
116 }
117}