darkmatter 0.0.1

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

pub fn stellar_to_halo_mass_behroozi(m_halo_solar: f64) -> f64 {
    let m1 = 1e12;
    let epsilon = 0.02;
    let alpha = -1.412;
    let delta_val = 0.608;
    let gamma_val = 1.124;
    let x = (m_halo_solar / m1).log10();
    let f_x = -x.abs().powf(alpha) + delta_val * (x.exp() / (1.0 + x.exp())).powf(gamma_val);
    m1 * epsilon * 10.0_f64.powf(f_x)
}

pub fn abundance_matching_mstar(m_halo_solar: f64) -> f64 {
    let m_pivot = 1e12;
    let m_star_pivot = 5e10;
    let alpha_low = 2.0;
    let alpha_high = 0.5;
    if m_halo_solar < m_pivot {
        m_star_pivot * (m_halo_solar / m_pivot).powf(alpha_low)
    } else {
        m_star_pivot * (m_halo_solar / m_pivot).powf(alpha_high)
    }
}

pub fn halo_spin_parameter(j: f64, e: f64, m: f64) -> f64 {
    j * e.abs().sqrt() / (G * m.powf(2.5))
}

pub fn disk_scale_radius(lambda: f64, r_vir: f64, f_r: f64) -> f64 {
    lambda / (2.0_f64).sqrt() * r_vir * f_r
}

pub fn disk_scale_height(r_d: f64, ratio: f64) -> f64 {
    r_d * ratio
}

pub fn tully_fisher(v_flat_km_s: f64) -> f64 {
    let log_l = 4.0 * v_flat_km_s.log10() - 1.5;
    10.0_f64.powf(log_l)
}

pub fn faber_jackson(sigma_km_s: f64) -> f64 {
    let log_l = 4.0 * sigma_km_s.log10() - 0.5;
    10.0_f64.powf(log_l)
}

pub fn adiabatic_contraction_blumenthal(r_i: f64, m_dm_i: f64, m_baryon: f64, r_f: f64) -> f64 {
    r_i * m_dm_i / (m_dm_i + m_baryon * (1.0 - r_f / r_i).max(0.0))
}

pub fn nfw_to_burkert_from_sidm(
    rho_s: f64,
    r_s: f64,
    sigma_over_m: f64,
    age_gyr: f64,
) -> (f64, f64) {
    let age = age_gyr * 3.156e16;
    let v_max = (4.625 * G * rho_s * r_s * r_s).sqrt();
    let rate = sigma_over_m * rho_s * v_max;
    let core_ratio = (rate * age).min(3.0);
    let r_core = r_s * core_ratio;
    let rho_core = rho_s * r_s / (r_core + r_s);
    (rho_core, r_core)
}

pub fn dwarf_spheroidal_mass_within_half_light(sigma_v: f64, r_half: f64) -> f64 {
    4.0 * sigma_v * sigma_v * r_half / G
}

pub fn wolf_mass_estimator(sigma_los: f64, r_half: f64) -> f64 {
    930.0 * sigma_los * sigma_los * r_half
}

pub fn dm_fraction_within_r(m_total: f64, m_stars: f64, m_gas: f64) -> f64 {
    (m_total - m_stars - m_gas).max(0.0) / m_total
}

pub fn too_big_to_fail_threshold_vmax(v_max_km_s: f64) -> bool {
    v_max_km_s > 30.0
}

pub fn missing_satellites_predicted(m_host_solar: f64, m_sub_min_solar: f64) -> f64 {
    0.008 * (m_host_solar / m_sub_min_solar).powf(0.9)
}

pub fn virial_mass_from_circular_velocity(v_c: f64, r_vir: f64) -> f64 {
    v_c * v_c * r_vir / G
}

pub fn angular_momentum_from_spin(lambda: f64, m_vir: f64, r_vir: f64) -> f64 {
    let v_vir = (G * m_vir / r_vir).sqrt();
    lambda * m_vir * v_vir * r_vir
}

pub fn specific_angular_momentum(j_total: f64, m: f64) -> f64 {
    j_total / m
}

pub fn maximum_disk_hypothesis(v_max: f64, r_d: f64) -> f64 {
    0.85 * v_max * v_max * r_d / G
}

pub fn schwarzschild_radius_galactic(m: f64) -> f64 {
    2.0 * G * m / (C * C)
}

pub fn galactic_escape_velocity(m_vir: f64, r: f64) -> f64 {
    (2.0 * G * m_vir / r).sqrt()
}

pub fn dm_annihilation_luminosity_galaxy(
    rho_dm_profile: impl Fn(f64) -> f64,
    sigma_v: f64,
    m_dm: f64,
    r_max: f64,
    steps: usize,
) -> f64 {
    let dr = r_max / steps as f64;
    let mut luminosity = 0.0;
    for i in 0..steps {
        let r = (i as f64 + 0.5) * dr;
        let rho = rho_dm_profile(r);
        let n = rho / m_dm;
        let shell_volume = 4.0 * PI * r * r * dr;
        luminosity += 0.5 * n * n * sigma_v * m_dm * C * C * shell_volume;
    }
    luminosity
}