Skip to main content

lindhard/ion/stopping/
straggling.rs

1//! Electronic energy-loss straggling.
2//!
3//! Bohr (1948), Mat. Fys. Medd. Dan. Vid. Selsk. 18 (8): for a thick-target
4//! free-electron estimate the variance of the energy loss per unit areal
5//! density is
6//!
7//! `Ω_B² = 4π z² Z2 (e²/(4π ε0))²` (J² m² per target atom),
8//!
9//! independent of energy in the non-relativistic regime. A relativistic factor
10//! `(1 − β²/2) / (1 − β²)` (Bethe and Livingston 1937; Fano 1963) is included
11//! in [`StragglingModel::BohrRelativistic`].
12//!
13//! # Not implemented
14//!
15//! The Chu and Yang-O'Connor-Wang corrections, which reduce Bohr's value at
16//! intermediate energies, are published as Hartree-Fock-Slater tables and as
17//! fits to data (declined, see `docs/stopping-models.md`), and the Lindhard-
18//! Scharff low-velocity correction was not verified against the original. They
19//! are omitted. Bohr is therefore an upper-end estimate below about
20//! 1 MeV/u; see `docs/stopping-models.md`.
21
22use 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/// Straggling model choice.
29#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
30pub enum StragglingModel {
31    /// Bohr, non-relativistic.
32    #[default]
33    Bohr,
34    /// Bohr with the relativistic factor `(1 - β²/2) / (1 - β²)`. The same
35    /// correction appears in J. F. Ziegler, J. P. Biersack, M. D. Ziegler,
36    /// *SRIM: The Stopping and Range of Ions in Matter* (SRIM Co., 2015),
37    /// Eq. 7-47, p. 7-25, written there with `β = c²/v²`.
38    BohrRelativistic,
39}
40
41/// Energy-loss variance per target atom of element `target_z`, J² m².
42pub 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
62/// Bragg-additive variance per average atom of a material, J² m²
63/// (`Σ_j x_j Ω_j²`).
64pub 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        // 4 pi * 14 * (2.307e-28 J m)^2 = 9.36e-54 J^2 m^2
101        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}