use crate::math::constants;
pub fn remaining_nuclei(initial_count: f64, decay_constant: f64, time: f64) -> f64 {
initial_count * (-decay_constant * time).exp()
}
pub fn activity(initial_count: f64, decay_constant: f64, time: f64) -> f64 {
decay_constant * remaining_nuclei(initial_count, decay_constant, time)
}
pub fn half_life(decay_constant: f64) -> f64 {
assert!(decay_constant > 0.0, "decay constant must be positive");
2.0_f64.ln() / decay_constant
}
pub fn decay_constant(half_life: f64) -> f64 {
assert!(half_life > 0.0, "half-life must be positive");
2.0_f64.ln() / half_life
}
pub fn mean_lifetime(decay_constant: f64) -> f64 {
assert!(decay_constant > 0.0, "decay constant must be positive");
1.0 / decay_constant
}
pub fn remaining_after_half_lives(initial_count: f64, num_half_lives: f64) -> f64 {
initial_count / 2.0_f64.powf(num_half_lives)
}
pub fn num_half_lives(time: f64, half_life: f64) -> f64 {
assert!(half_life > 0.0, "half-life must be positive");
time / half_life
}
pub fn mass_defect(
num_protons: u32,
num_neutrons: u32,
nucleus_mass: f64,
) -> f64 {
num_protons as f64 * constants::M_PROTON + num_neutrons as f64 * constants::M_NEUTRON
- nucleus_mass
}
pub fn binding_energy(mass_defect: f64) -> f64 {
mass_defect * constants::C * constants::C
}
pub fn binding_energy_per_nucleon(total_binding_energy: f64, mass_number: u32) -> f64 {
assert!(mass_number > 0, "mass number must be positive");
total_binding_energy / mass_number as f64
}
pub fn q_value(reactant_mass: f64, product_mass: f64) -> f64 {
(reactant_mass - product_mass) * constants::C * constants::C
}
pub fn mass_energy(mass: f64) -> f64 {
mass * constants::C * constants::C
}
pub fn energy_from_amu(mass_difference_amu: f64) -> f64 {
mass_difference_amu * 931.5 * 1e6 * constants::E_CHARGE
}
pub fn reaction_rate(number_density: f64, cross_section: f64, flux: f64) -> f64 {
number_density * cross_section * flux
}
pub fn nuclear_mean_free_path(number_density: f64, cross_section: f64) -> f64 {
assert!(number_density > 0.0, "number density must be positive");
assert!(cross_section > 0.0, "cross section must be positive");
1.0 / (number_density * cross_section)
}
pub fn nuclear_radius(mass_number: u32) -> f64 {
1.2e-15 * (mass_number as f64).powf(1.0 / 3.0)
}
pub fn nuclear_density() -> f64 {
let r0 = 1.2e-15;
3.0 * constants::M_PROTON / (4.0 * constants::PI * r0 * r0 * r0)
}
pub fn absorbed_dose(energy: f64, mass: f64) -> f64 {
assert!(mass > 0.0, "mass must be positive");
energy / mass
}
pub fn equivalent_dose(absorbed_dose: f64, weighting_factor: f64) -> f64 {
absorbed_dose * weighting_factor
}
pub fn radiation_intensity_distance(
initial_intensity: f64,
initial_distance: f64,
new_distance: f64,
) -> f64 {
assert!(new_distance > 0.0, "new distance must be positive");
initial_intensity * (initial_distance / new_distance).powi(2)
}
#[cfg(test)]
mod tests {
use super::*;
fn approx(a: f64, b: f64, tol: f64) -> bool {
(a - b).abs() < tol
}
fn approx_rel(a: f64, b: f64, tol: f64) -> bool {
((a - b) / b).abs() < tol
}
#[test]
fn test_half_life() {
let lambda = decay_constant(1.80825048e11);
let hl = half_life(lambda);
assert!(approx_rel(hl, 1.80825048e11, 0.001));
}
#[test]
fn test_remaining_nuclei() {
let lambda = decay_constant(100.0);
let n = remaining_nuclei(1000.0, lambda, 100.0);
assert!(approx(n, 500.0, 0.1));
}
#[test]
fn test_remaining_after_half_lives() {
let n = remaining_after_half_lives(1000.0, 3.0);
assert!(approx(n, 125.0, 0.01));
}
#[test]
fn test_mass_defect_helium4() {
let m_he4 = 6.6447e-27; let dm = mass_defect(2, 2, m_he4);
assert!(dm > 0.0); }
#[test]
fn test_binding_energy() {
let dm = 0.03_f64 * constants::AMU; let e = binding_energy(dm);
assert!(e > 0.0);
}
#[test]
fn test_nuclear_radius() {
let r = nuclear_radius(56);
assert!(approx_rel(r, 4.59e-15, 0.05));
}
#[test]
fn test_radiation_inverse_square() {
let i = radiation_intensity_distance(100.0, 1.0, 2.0);
assert!(approx(i, 25.0, 1e-6));
}
#[test]
fn test_activity() {
let lambda = decay_constant(100.0);
let a = activity(1000.0, lambda, 0.0);
assert!(approx(a, 6.93147, 1e-3));
}
#[test]
fn test_q_value() {
let q = q_value(2.0 * constants::AMU, 1.99 * constants::AMU);
assert!(q > 0.0);
}
#[test]
fn test_energy_from_amu() {
let e = energy_from_amu(1.0);
assert!(approx_rel(e, 1.49243e-10, 0.001));
}
#[test]
fn test_mean_lifetime() {
let lambda = decay_constant(100.0);
let tau = mean_lifetime(lambda);
assert!(approx_rel(tau, 144.2695, 1e-4));
}
#[test]
fn test_num_half_lives() {
let n = num_half_lives(300.0, 100.0);
assert!(approx(n, 3.0, 1e-9));
}
#[test]
fn test_binding_energy_per_nucleon() {
let bepn = binding_energy_per_nucleon(1.4e-11, 56);
assert!(approx_rel(bepn, 2.5e-13, 0.01));
}
#[test]
fn test_mass_energy() {
let e = mass_energy(1.0);
assert!(approx_rel(e, 8.98755179e16, 1e-6));
}
#[test]
fn test_reaction_rate() {
let r = reaction_rate(1e28, 1e-28, 1e14);
assert!(approx_rel(r, 1e14, 1e-9));
}
#[test]
fn test_nuclear_mean_free_path() {
let mfp = nuclear_mean_free_path(1e28, 1e-28);
assert!(approx(mfp, 1.0, 1e-9));
}
#[test]
fn test_nuclear_density() {
let rho = nuclear_density();
assert!(approx_rel(rho, 2.3e17, 0.1));
}
#[test]
fn test_absorbed_dose() {
let d = absorbed_dose(0.01, 70.0);
assert!(approx_rel(d, 1.42857e-4, 1e-4));
}
#[test]
fn test_equivalent_dose() {
let h = equivalent_dose(0.001, 20.0);
assert!(approx(h, 0.02, 1e-9));
}
}