darkmatter 0.0.1

Dark matter simulation engine — gravitational fields, particle dynamics, halo stability, and cosmological constants
Documentation
use crate::constants::physical::HBAR;
use std::f64::consts::PI;

pub fn sommerfeld_coulomb(alpha: f64, v_over_c: f64) -> f64 {
    let eps = PI * alpha / v_over_c;
    if eps < 1e-3 {
        return 1.0 + eps;
    }
    eps / (1.0 - (-2.0 * eps).exp())
}

pub fn sommerfeld_yukawa_s_wave(alpha: f64, m_dm_gev: f64, m_phi_gev: f64, v_over_c: f64) -> f64 {
    let epsilon_v = v_over_c / alpha;
    let epsilon_phi = m_phi_gev / (alpha * m_dm_gev);
    if epsilon_phi < 1e-10 {
        return sommerfeld_coulomb(alpha, v_over_c);
    }
    let a_val = epsilon_v / epsilon_phi;
    let c_val = 6.0 * epsilon_phi / (PI * PI);
    let sinh_a = if a_val > 500.0 {
        return 1.0;
    } else {
        (PI * a_val).sinh()
    };
    let cosh_a = (PI * a_val).cosh();
    let cos_c = (2.0 * PI * (c_val - 0.25 * a_val * a_val).abs().sqrt()).cos();
    PI * sinh_a / (cosh_a - cos_c)
}

pub fn sommerfeld_yukawa_resonant(
    alpha: f64,
    m_dm_gev: f64,
    m_phi_gev: f64,
    v_over_c: f64,
    n: u32,
) -> f64 {
    let epsilon_v = v_over_c / alpha;
    let epsilon_phi = m_phi_gev / (alpha * m_dm_gev);
    let epsilon_res = 6.0 / (PI * PI * n as f64 * n as f64);
    let delta = epsilon_phi - epsilon_res;
    let numerator = epsilon_v.powi(2) + delta * delta;
    if numerator < 1e-100 {
        return 1.0 / (epsilon_v * epsilon_v + 1e-100);
    }
    epsilon_res / (epsilon_v * epsilon_v + delta * delta)
}

pub fn sommerfeld_p_wave(alpha: f64, v_over_c: f64) -> f64 {
    let eps = PI * alpha / v_over_c;
    if eps < 1e-3 {
        return 1.0 + 2.0 * eps * eps / 3.0;
    }
    let s = sommerfeld_coulomb(alpha, v_over_c);
    s * (1.0 + eps * eps / 4.0)
}

pub fn unitarity_bound(m_dm_gev: f64, v_over_c: f64) -> f64 {
    let m_kg = m_dm_gev * 1.782_661_92e-27;
    let p = m_kg * v_over_c * 3e8;
    4.0 * PI * HBAR * HBAR / (p * p)
}

pub fn effective_cross_section(sigma_0_cm2: f64, alpha: f64, v_km_s: f64) -> f64 {
    let v_over_c = v_km_s * 1e3 / 3e8;
    let s = sommerfeld_coulomb(alpha, v_over_c);
    let sigma_max = unitarity_bound_cm2(alpha, v_km_s);
    let sigma_enhanced = sigma_0_cm2 * s;
    sigma_enhanced.min(sigma_max)
}

fn unitarity_bound_cm2(alpha: f64, v_km_s: f64) -> f64 {
    let v = v_km_s * 1e3;
    let m_dm = alpha * 3e8 * HBAR / (v * 1e-15);
    let ub = unitarity_bound(m_dm / 1.782_661_92e-27, v / 3e8);
    ub * 1e4
}

pub fn thermal_average_sommerfeld_coulomb(alpha: f64, x: f64) -> f64 {
    let n_points = 200;
    let v_max = 10.0 / x.sqrt();
    let dv = v_max / n_points as f64;
    let mut integral = 0.0;
    for i in 0..n_points {
        let v = (i as f64 + 0.5) * dv;
        if v < 1e-15 {
            continue;
        }
        let s = sommerfeld_coulomb(alpha, v);
        let weight = (x / PI).sqrt() * 4.0 * PI * v * v * (-x * v * v).exp();
        integral += s * weight * dv;
    }
    integral.max(1.0)
}

pub fn freeze_out_boost(alpha: f64, m_dm_gev: f64, t_fo_gev: f64) -> f64 {
    let x_fo = m_dm_gev / t_fo_gev;
    thermal_average_sommerfeld_coulomb(alpha, x_fo)
}

pub fn velocity_weighted_sigma_v(sigma_0_cm3_s: f64, alpha: f64, v_dispersion_km_s: f64) -> f64 {
    let n_points = 100;
    let v_max = 4.0 * v_dispersion_km_s;
    let dv = v_max / n_points as f64;
    let sigma = v_dispersion_km_s * 1e3 / 3e8;
    let mut integral = 0.0;
    let mut norm = 0.0;
    for i in 0..n_points {
        let v_km_s = (i as f64 + 0.5) * dv;
        let v_oc = v_km_s * 1e3 / 3e8;
        if v_oc < 1e-15 {
            continue;
        }
        let s = sommerfeld_coulomb(alpha, v_oc);
        let mb = 4.0 * PI * v_oc * v_oc * (-v_oc * v_oc / (2.0 * sigma * sigma)).exp();
        integral += s * mb * dv;
        norm += mb * dv;
    }
    if norm < 1e-100 {
        return sigma_0_cm3_s;
    }
    sigma_0_cm3_s * integral / norm
}

pub fn boost_factor_dwarf(alpha: f64, v_dispersion_km_s: f64) -> f64 {
    velocity_weighted_sigma_v(1.0, alpha, v_dispersion_km_s)
}

pub fn boost_factor_cluster(alpha: f64, v_dispersion_km_s: f64) -> f64 {
    velocity_weighted_sigma_v(1.0, alpha, v_dispersion_km_s)
}

pub fn saturation_velocity(alpha: f64, m_dm_gev: f64, m_phi_gev: f64) -> f64 {
    let epsilon_phi = m_phi_gev / (alpha * m_dm_gev);
    alpha * epsilon_phi.sqrt() * 3e8 / 1e3
}