darkmatter 0.0.2

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

pub fn fermi_dirac_dm(p_kg_m_s: f64, t_gev: f64, m_dm_gev: f64, mu_gev: f64) -> f64 {
    let m = m_dm_gev * 1.782_661_92e-27;
    let c = 3e8;
    let e = (p_kg_m_s * p_kg_m_s * c * c + m * m * c.powi(4)).sqrt();
    let t_j = t_gev * 1.602e-10;
    let mu_j = mu_gev * 1.602e-10;
    1.0 / ((e - mu_j) / t_j).exp() + 1.0
}

pub fn bose_einstein_dm(p_kg_m_s: f64, t_gev: f64, m_dm_gev: f64, mu_gev: f64) -> f64 {
    let m = m_dm_gev * 1.782_661_92e-27;
    let c = 3e8;
    let e = (p_kg_m_s * p_kg_m_s * c * c + m * m * c.powi(4)).sqrt();
    let t_j = t_gev * 1.602e-10;
    let mu_j = mu_gev * 1.602e-10;
    let exp_val = ((e - mu_j) / t_j).exp();
    if exp_val <= 1.0 {
        return 0.0;
    }
    1.0 / (exp_val - 1.0)
}

pub fn number_density_fermionic(t_gev: f64, m_dm_gev: f64) -> f64 {
    let m = m_dm_gev * 1.782_661_92e-27;
    let c = 3e8;
    let t_j = t_gev * 1.602e-10;
    if t_j > m * c * c * 10.0 {
        3.0 * 1.2021 / (4.0 * PI * PI) * (t_j / (HBAR * c)).powi(3)
    } else {
        let p_max = (2.0 * m * t_j * 10.0).sqrt();
        let n_steps = 200;
        let dp = p_max / n_steps as f64;
        let mut n = 0.0;
        for i in 0..n_steps {
            let p = (i as f64 + 0.5) * dp;
            let f = fermi_dirac_dm(p, t_gev, m_dm_gev, 0.0);
            n += 4.0 * PI * p * p * f * dp / (2.0 * PI * HBAR).powi(3);
        }
        n
    }
}

pub fn number_density_bosonic(t_gev: f64, m_dm_gev: f64) -> f64 {
    let c = 3e8;
    let m = m_dm_gev * 1.782_661_92e-27;
    let t_j = t_gev * 1.602e-10;
    if t_j > m * c * c * 10.0 {
        1.2021 / (PI * PI) * (t_j / (HBAR * c)).powi(3)
    } else {
        let p_max = (2.0 * m * t_j * 10.0).sqrt();
        let n_steps = 200;
        let dp = p_max / n_steps as f64;
        let mut n = 0.0;
        for i in 0..n_steps {
            let p = (i as f64 + 0.5) * dp;
            let f = bose_einstein_dm(p, t_gev, m_dm_gev, 0.0);
            n += 4.0 * PI * p * p * f * dp / (2.0 * PI * HBAR).powi(3);
        }
        n
    }
}

pub fn tremaine_gunn_bound_ev(sigma_v_km_s: f64, r_halo_kpc: f64) -> f64 {
    let sigma_v = sigma_v_km_s * 1e3;
    let r = r_halo_kpc * 3.0857e19;
    let m_min = (HBAR.powi(3) * 9.0 * PI / (G * r * r * sigma_v)).powf(0.25);
    m_min / 1.782_661_92e-36
}

pub fn max_phase_space_density_fermionic(m_dm_gev: f64, g_dm: f64) -> f64 {
    let m = m_dm_gev * 1.782_661_92e-27;
    g_dm / (2.0 * PI * HBAR).powi(3) * m * m * m
}

pub fn coarse_grained_phase_space_density(rho_kg_m3: f64, sigma_v_m_s: f64) -> f64 {
    rho_kg_m3 / (2.0 * PI * sigma_v_m_s * sigma_v_m_s).powf(1.5)
}

pub fn phase_space_constraint_satisfied(
    rho_kg_m3: f64,
    sigma_v_km_s: f64,
    m_dm_gev: f64,
    g_dm: f64,
) -> bool {
    let sigma_v = sigma_v_km_s * 1e3;
    let q = coarse_grained_phase_space_density(rho_kg_m3, sigma_v);
    let q_max = max_phase_space_density_fermionic(m_dm_gev, g_dm);
    q <= q_max
}

pub fn liouville_phase_space_density_nfw(rho_0: f64, sigma_v_km_s: f64) -> f64 {
    let sigma_v = sigma_v_km_s * 1e3;
    rho_0 / (2.0 * PI * sigma_v * sigma_v).powf(1.5)
}

pub fn warm_dm_free_streaming_cutoff_ev(t_dm_gev: f64, m_dm_gev: f64) -> f64 {
    let m = m_dm_gev * 1.782_661_92e-27;
    let t_j = t_dm_gev * 1.602e-10;
    let c = 3e8;
    let v_rms = (3.0 * t_j / m).sqrt().min(c);
    let k_fs = (4.0 * PI * G * m / v_rms).sqrt() / HBAR;
    2.0 * PI / k_fs / 3.0857e22
}

pub fn king_distribution(e_total: f64, sigma_v: f64, rho_0: f64) -> f64 {
    if e_total > 0.0 {
        return 0.0;
    }
    let w2 = sigma_v * sigma_v;
    rho_0 / (2.0 * PI * w2).powf(1.5) * ((-e_total / w2).exp() - 1.0)
}

pub fn isothermal_distribution(v_m_s: f64, sigma_v_m_s: f64) -> f64 {
    let w2 = sigma_v_m_s * sigma_v_m_s;
    (2.0 * PI * w2).powf(-1.5) * (-v_m_s * v_m_s / (2.0 * w2)).exp()
}

pub fn degenerate_fermi_pressure(m_dm_gev: f64, rho_kg_m3: f64) -> f64 {
    let m = m_dm_gev * 1.782_661_92e-27;
    let n = rho_kg_m3 / m;
    let p_f = HBAR * (3.0 * PI * PI * n).powf(1.0 / 3.0);
    p_f * p_f * n / (5.0 * m)
}

pub fn chandrasekhar_mass_fermionic_dm(m_dm_gev: f64) -> f64 {
    let m = m_dm_gev * 1.782_661_92e-27;
    let c = 3e8;
    let m_pl2 = HBAR * c / G;
    let m_ch = m_pl2 / (m * m) * (HBAR / (m * c)).sqrt() * 0.77;
    m_ch / 1.989e30
}

pub fn phase_space_temperature(rho_kg_m3: f64, sigma_v_m_s: f64, m_dm_gev: f64) -> f64 {
    let m = m_dm_gev * 1.782_661_92e-27;
    let t_kinematic = m * sigma_v_m_s * sigma_v_m_s / K_B;
    let n = rho_kg_m3 / m;
    let lambda_db = HBAR / (m * sigma_v_m_s);
    let degeneracy = n * lambda_db.powi(3);
    t_kinematic * (1.0 + degeneracy)
}