darkmatter 0.0.1

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

pub fn gw_energy_density_phase_transition(
    alpha_pt: f64,
    beta_over_h: f64,
    t_star_gev: f64,
    g_star: f64,
) -> f64 {
    let h0 = 2.18e-18;
    let kappa = alpha_pt / (1.0 + alpha_pt);
    let omega_sw = 2.65e-6 * kappa * kappa * (100.0 / g_star).powf(1.0 / 3.0)
        / (beta_over_h * beta_over_h)
        * (alpha_pt / (1.0 + alpha_pt)).powi(2)
        * (t_star_gev / 100.0).powi(0);
    omega_sw * h0 * h0
}

pub fn peak_frequency_phase_transition(beta_over_h: f64, t_star_gev: f64, g_star: f64) -> f64 {
    1.9e-5 * beta_over_h * (t_star_gev / 100.0) * (g_star / 100.0).powf(1.0 / 6.0)
}

pub fn gw_spectrum_sound_waves(f_hz: f64, f_peak_hz: f64, omega_peak: f64) -> f64 {
    let s = f_hz / f_peak_hz;
    omega_peak * s.powi(3) * (7.0 / (4.0 + 3.0 * s * s)).powf(7.0 / 2.0)
}

pub fn gw_spectrum_bubble_collision(f_hz: f64, f_peak_hz: f64, omega_peak: f64) -> f64 {
    let s = f_hz / f_peak_hz;
    omega_peak * 3.8 * s.powi(2) * s / (2.8 * s * s + 1.0).powi(2)
}

pub fn gw_spectrum_turbulence(f_hz: f64, f_peak_hz: f64, omega_peak: f64) -> f64 {
    let s = f_hz / f_peak_hz;
    let h_star = f_peak_hz / 1e-5;
    omega_peak * s.powi(3) / ((1.0 + s).powf(11.0 / 3.0) * (1.0 + 8.0 * PI * f_hz / h_star))
}

pub fn binary_gw_frequency_shift(f_gw_hz: f64, rho_dm_kg_m3: f64, r_orbit_m: f64) -> f64 {
    let phi_dm = -4.0 / 3.0 * PI * G * rho_dm_kg_m3 * r_orbit_m * r_orbit_m;
    f_gw_hz * phi_dm / (C * C)
}

pub fn dynamical_friction_gw_dephasing(
    m1_solar: f64,
    m2_solar: f64,
    rho_dm_kg_m3: f64,
    f_gw_hz: f64,
    t_obs_s: f64,
) -> f64 {
    let m1 = m1_solar * 1.989e30;
    let m2 = m2_solar * 1.989e30;
    let m_total = m1 + m2;
    let mu = m1 * m2 / m_total;
    let f_orb = f_gw_hz / 2.0;
    let a = (G * m_total / (4.0 * PI * PI * f_orb * f_orb)).powf(1.0 / 3.0);
    let v_orb = 2.0 * PI * a * f_orb;
    let ln_lambda = 10.0;
    let f_df = 4.0 * PI * G * G * m2 * rho_dm_kg_m3 * ln_lambda / (v_orb * v_orb);
    let da_dt = -2.0 * a * f_df / (mu * v_orb);
    let df_dt = -1.5 * f_orb * da_dt / a;
    0.5 * df_dt * t_obs_s * t_obs_s * 2.0 * PI
}

pub fn spike_enhanced_gw_dephasing(
    m_bh_solar: f64,
    m_companion_solar: f64,
    rho_spike_kg_m3: f64,
    gamma_sp: f64,
    f_gw_hz: f64,
    t_obs_yr: f64,
) -> f64 {
    let m_bh = m_bh_solar * 1.989e30;
    let m_c = m_companion_solar * 1.989e30;
    let m_t = m_bh + m_c;
    let f_orb = f_gw_hz / 2.0;
    let a = (G * m_t / (4.0 * PI * PI * f_orb * f_orb)).powf(1.0 / 3.0);
    let v = 2.0 * PI * a * f_orb;
    let ln_l = (a * v * v / (G * m_c)).ln().max(1.0);
    let rho_at_a = rho_spike_kg_m3 * (a / 1e12).powf(-gamma_sp);
    let f_df = 4.0 * PI * G * G * m_c * rho_at_a * ln_l / (v * v);
    let t_obs = t_obs_yr * 3.156e7;
    f_df * t_obs * t_obs / a * 2.0 * PI
}

pub fn gw_strain_from_dm_halo(
    m_halo_solar: f64,
    r_halo_kpc: f64,
    f_hz: f64,
    d_luminosity_mpc: f64,
) -> f64 {
    let m = m_halo_solar * 1.989e30;
    let r = r_halo_kpc * 3.0857e19;
    let d = d_luminosity_mpc * 3.0857e22;
    let omega = 2.0 * PI * f_hz;
    let q = m * r * r * omega * omega;
    2.0 * G * q / (C.powi(4) * d)
}

pub fn lisa_sensitivity_strain(f_hz: f64) -> f64 {
    let f_star = 19.09e-3;
    let s1 = 5.76e-48 * (1.0 + (0.4e-3 / f_hz).powi(2));
    let s2 = 3.6e-41;
    let r = 1.0 + (f_hz / 25e-3).powi(2);
    let s_n = s1 / (2.0 * PI * f_hz).powi(4)
        + s2
        + s1 * (f_hz / f_star).powi(2) / (2.0 * PI * f_hz).powi(4);
    (s_n * r).sqrt()
}

pub fn snr_stochastic_background(
    omega_gw: f64,
    f_min_hz: f64,
    f_max_hz: f64,
    t_obs_s: f64,
    n_steps: usize,
) -> f64 {
    let h0 = 2.18e-18;
    let d_log_f = (f_max_hz / f_min_hz).ln() / n_steps as f64;
    let mut snr2 = 0.0;
    for i in 0..n_steps {
        let log_f = f_min_hz.ln() + (i as f64 + 0.5) * d_log_f;
        let f = log_f.exp();
        let s_h = 3.0 * h0 * h0 * omega_gw / (2.0 * PI * PI * f * f * f);
        let s_n = lisa_sensitivity_strain(f).powi(2);
        if s_n > 0.0 {
            snr2 += (s_h / s_n).powi(2) * f * d_log_f;
        }
    }
    (2.0 * t_obs_s * snr2).sqrt()
}

pub fn gw_energy_from_dm_annihilation(
    sigma_v_cm3_s: f64,
    rho_dm_gev_cm3: f64,
    m_dm_gev: f64,
    fraction_to_gw: f64,
) -> f64 {
    let n_dm = rho_dm_gev_cm3 / m_dm_gev;
    let ann_rate = 0.5 * n_dm * n_dm * sigma_v_cm3_s;
    let energy_per_ann = 2.0 * m_dm_gev * 1.602e-10;
    ann_rate * energy_per_ann * fraction_to_gw
}

pub fn characteristic_strain(omega_gw: f64, f_hz: f64) -> f64 {
    let h0 = 2.18e-18;
    let h_c2 = 3.0 * h0 * h0 * omega_gw / (2.0 * PI * PI * f_hz * f_hz);
    h_c2.sqrt()
}

pub fn gw_merger_rate_density(n_halo_mpc3: f64, merger_fraction: f64, t_delay_gyr: f64) -> f64 {
    let t_delay_s = t_delay_gyr * 3.156e16;
    let rate_per_vol = n_halo_mpc3 * merger_fraction;
    rate_per_vol / t_delay_s
}

pub fn inspiral_chirp_mass(m1_solar: f64, m2_solar: f64) -> f64 {
    let m1 = m1_solar * 1.989e30;
    let m2 = m2_solar * 1.989e30;
    let mc = (m1 * m2).powf(3.0 / 5.0) / (m1 + m2).powf(1.0 / 5.0);
    mc / 1.989e30
}

pub fn inspiral_gw_strain(chirp_mass_solar: f64, f_gw_hz: f64, d_luminosity_mpc: f64) -> f64 {
    let mc = chirp_mass_solar * 1.989e30;
    let d = d_luminosity_mpc * 3.0857e22;
    4.0 * (G * mc).powf(5.0 / 3.0) * (PI * f_gw_hz).powf(2.0 / 3.0) / (C.powi(4) * d)
}

pub fn graviton_energy(f_gw_hz: f64) -> f64 {
    HBAR * 2.0 * PI * f_gw_hz
}

pub fn quantum_gw_detector_noise_floor(f_hz: f64, m_detector_kg: f64, l_arm_m: f64) -> f64 {
    let omega = 2.0 * PI * f_hz;
    (HBAR / (m_detector_kg * omega * l_arm_m * l_arm_m)).sqrt()
}