use crate::constants::*;
use std::f64::consts::PI;
pub fn saha_equation(temp_k: f64, n_h: f64) -> f64 {
let binding_energy = 13.6 * EV_TO_J;
let prefactor = ((M_E * K_B * temp_k) / (2.0 * PI * HBAR * HBAR)).powf(1.5);
let boltzmann = (-binding_energy / (K_B * temp_k)).exp();
let k = prefactor * boltzmann / n_h;
if k > 1e6 {
return 1.0; }
if k < 1e-20 {
return 0.0; }
let discriminant = k * k + 4.0 * k;
let x_e = (-k + discriminant.sqrt()) / 2.0;
x_e.min(1.0).max(0.0)
}
pub fn ionization_fraction(z: f64, universe: &crate::dynamics::Universe) -> f64 {
let temp = T_CMB * (1.0 + z);
let omega_b = 0.049; let rho_b = omega_b * universe.critical_density();
let n_b = rho_b / M_P;
let n_h = n_b * (1.0 + z).powi(3);
saha_equation(temp, n_h)
}
pub fn recombination_redshift(universe: &crate::dynamics::Universe) -> f64 {
let mut z_low = 500.0;
let mut z_high = 2000.0;
for _ in 0..50 {
let z_mid = (z_low + z_high) / 2.0;
let x_e = ionization_fraction(z_mid, universe);
if x_e > 0.5 {
z_high = z_mid;
} else {
z_low = z_mid;
}
}
(z_low + z_high) / 2.0
}
pub fn optical_depth(z: f64, universe: &crate::dynamics::Universe) -> f64 {
let n_steps = 1000;
let dz = z / n_steps as f64;
let mut tau = 0.0;
for i in 0..n_steps {
let zi = i as f64 * dz;
let x_e = ionization_fraction(zi, universe);
let omega_b = 0.049;
let rho_b = omega_b * universe.critical_density();
let n_b = rho_b / M_P;
let n_e = x_e * n_b * (1.0 + zi).powi(3);
let h = universe.hubble_z(zi) * 1e3 / (PARSEC * 1e6); let dt_dz = 1.0 / ((1.0 + zi) * h);
tau += n_e * SIGMA_T * C * dt_dz * dz;
}
tau
}
pub fn visibility_function(z: f64, universe: &crate::dynamics::Universe) -> f64 {
let dz = 1.0;
let tau = optical_depth(z, universe);
let tau_plus = optical_depth(z + dz, universe);
let dtau_dz = (tau_plus - tau) / dz;
-dtau_dz * (-tau).exp()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_saha_high_temp() {
let temp = 1e5; let n_h = 1e6;
let x_e = saha_equation(temp, n_h);
assert!(x_e > 0.99);
}
#[test]
fn test_saha_low_temp() {
let temp = 1000.0; let n_h = 1e6;
let x_e = saha_equation(temp, n_h);
assert!(x_e < 0.1);
}
#[test]
fn test_recombination_redshift() {
let universe = crate::dynamics::Universe::benchmark();
let z_rec = recombination_redshift(&universe);
assert!(z_rec > 900.0 && z_rec < 1400.0);
}
}