use crate::constants::cosmic::{CMB_TEMPERATURE_K, HUBBLE_CONSTANT_SI, OMEGA_BARYON, OMEGA_DM};
use crate::constants::physical::{C, HBAR, K_B};
use std::f64::consts::{E, PI};
pub fn isocurvature_power_spectrum_cdm(k_mpc: f64, amplitude: f64, n_iso: f64) -> f64 {
let k_pivot = 0.05;
amplitude * (k_mpc / k_pivot).powf(n_iso - 1.0)
}
pub fn isocurvature_power_spectrum_baryon(k_mpc: f64, amplitude: f64, n_iso: f64) -> f64 {
isocurvature_power_spectrum_cdm(k_mpc, amplitude, n_iso)
}
pub fn isocurvature_fraction(alpha_iso: f64, a_s: f64) -> f64 {
alpha_iso / (1.0 + alpha_iso) * a_s
}
pub fn silk_damping_scale_mpc(omega_b_h2: f64, omega_m_h2: f64) -> f64 {
8.130 * (omega_m_h2 / 0.15).powf(-0.5) * (omega_b_h2 / 0.023).powf(-0.5)
}
pub fn silk_damping_mass_solar(omega_b_h2: f64, omega_m_h2: f64) -> f64 {
let rd_mpc = silk_damping_scale_mpc(omega_b_h2, omega_m_h2);
let rd_m = rd_mpc * 3.0857e22;
let rho_crit =
3.0 * HUBBLE_CONSTANT_SI * HUBBLE_CONSTANT_SI / (8.0 * PI * crate::constants::physical::G);
let rho_m = rho_crit * (omega_m_h2 / (HUBBLE_CONSTANT_SI * 3.0857e22 / (1e5)).powi(2));
4.0 / 3.0 * PI * (rd_m / 2.0).powi(3) * rho_m / 1.989e30
}
pub fn silk_damping_envelope(k_mpc: f64, k_silk_mpc: f64) -> f64 {
(-(k_mpc / k_silk_mpc).powi(2)).exp()
}
pub fn bao_correlation_function(r_mpc: f64, r_bao_mpc: f64, sigma_bao_mpc: f64) -> f64 {
let dr = r_mpc - r_bao_mpc;
0.05 * (-dr * dr / (2.0 * sigma_bao_mpc * sigma_bao_mpc)).exp()
/ (2.0 * PI * sigma_bao_mpc * sigma_bao_mpc).sqrt()
+ xi_smooth(r_mpc)
}
fn xi_smooth(r_mpc: f64) -> f64 {
if r_mpc < 1.0 {
return 1.0;
}
(1.0 / r_mpc).powf(1.8)
}
pub fn bao_peak_position(omega_m_h2: f64, omega_b_h2: f64) -> f64 {
let omega_r_h2 = 4.15e-5 * (CMB_TEMPERATURE_K / 2.7255).powi(4);
let z_eq = omega_m_h2 / omega_r_h2 - 1.0;
let z_d = 1059.68;
let rb = 3.0 * omega_b_h2 / (4.0 * omega_r_h2 * z_d);
let c_s_avg = C / (3.0 * (1.0 + rb)).sqrt();
let radiation_correction = 1.0 / (1.0 + (1.0 + z_d) / z_eq).sqrt();
c_s_avg * radiation_correction / HUBBLE_CONSTANT_SI / (1.0 + z_d).sqrt()
* (2.0 / (3.0 * omega_m_h2 * 100.0 * 1e3 / 3.0857e22).sqrt())
/ 3.0857e22
}
pub fn transfer_function_eisenstein_hu(
k_mpc: f64,
omega_m_h2: f64,
omega_b_h2: f64,
h: f64,
) -> f64 {
let theta = CMB_TEMPERATURE_K / 2.7;
let z_eq = 2.5e4 * omega_m_h2 * theta.powf(-4.0);
let k_eq = 0.0746 * omega_m_h2 * theta.powf(-2.0);
let b1 = 0.313 * omega_m_h2.powf(-0.419) * (1.0 + 0.607 * omega_m_h2.powf(0.674));
let b2 = 0.238 * omega_m_h2.powf(0.223);
let z_d = 1291.0 * omega_m_h2.powf(0.251) / (1.0 + 0.659 * omega_m_h2.powf(0.828))
* (1.0 + b1 * omega_b_h2.powf(b2));
let r_d = 31.5e3 * omega_b_h2 * theta.powf(-4.0) / z_d;
let r_eq = 31.5e3 * omega_b_h2 * theta.powf(-4.0) / z_eq;
let s =
2.0 / (3.0 * k_eq) * (6.0 / r_eq).sqrt() * ((1.0 + r_d).sqrt() + (r_d + r_eq).sqrt()).ln()
/ (1.0 + (r_eq).sqrt()).ln();
let q = k_mpc * theta * theta / (omega_m_h2 * h);
let f_b = omega_b_h2 / omega_m_h2;
let f_c = 1.0 - f_b;
let alpha_c = f_c.powf(-0.567) * (1.0 + (f_b / f_c).powf(0.5));
let l = (E + 1.8 * alpha_c * q).ln();
let c_val = 14.2 / alpha_c + 386.0 / (1.0 + 69.9 * q.powf(1.08));
let t0 = l / (l + c_val * q * q);
let ks = k_mpc * s;
let j0 = ks.sin() / ks;
f_c * t0 + f_b * j0 * (-0.5 * (k_mpc / 5.0).powi(2)).exp()
}
pub fn matter_power_spectrum(
k_mpc: f64,
a_s: f64,
n_s: f64,
omega_m_h2: f64,
omega_b_h2: f64,
h: f64,
) -> f64 {
let k_pivot = 0.05;
let p_prim = a_s * (k_mpc / k_pivot).powf(n_s - 1.0);
let t = transfer_function_eisenstein_hu(k_mpc, omega_m_h2, omega_b_h2, h);
p_prim * t * t * k_mpc
}
pub fn growth_factor_approx(z: f64, omega_m: f64, omega_lambda: f64) -> f64 {
let a = 1.0 / (1.0 + z);
let omega_m_z = omega_m * (1.0 + z).powi(3) / (omega_m * (1.0 + z).powi(3) + omega_lambda);
let omega_l_z = omega_lambda / (omega_m * (1.0 + z).powi(3) + omega_lambda);
a * 5.0 * omega_m_z
/ (2.0
* (omega_m_z.powf(4.0 / 7.0) - omega_l_z
+ (1.0 + omega_m_z / 2.0) * (1.0 + omega_l_z / 70.0)))
}
pub fn sigma_8_from_power(
a_s: f64,
n_s: f64,
omega_m_h2: f64,
omega_b_h2: f64,
h: f64,
n_steps: usize,
) -> f64 {
let r = 8.0;
let k_min = 1e-4_f64;
let k_max = 10.0_f64;
let dlnk = (k_max / k_min).ln() / n_steps as f64;
let mut integral = 0.0;
for i in 0..n_steps {
let lnk = k_min.ln() + (i as f64 + 0.5) * dlnk;
let k = lnk.exp();
let pk = matter_power_spectrum(k, a_s, n_s, omega_m_h2, omega_b_h2, h);
let x = k * r;
let w = 3.0 * (x.sin() - x * x.cos()) / (x * x * x);
integral += pk * w * w * k * k * k * dlnk;
}
(integral / (2.0 * PI * PI)).sqrt()
}
pub fn baryon_to_dm_ratio() -> f64 {
OMEGA_BARYON / OMEGA_DM
}
pub fn de_broglie_wavelength_dm_mpc(m_dm_gev: f64, v_km_s: f64) -> f64 {
let m_kg = m_dm_gev * 1.782_661_92e-27;
let v = v_km_s * 1e3;
HBAR / (m_kg * v) / 3.0857e22
}
pub fn thermal_velocity_dm_km_s(m_dm_gev: f64, t_kev: f64) -> f64 {
let m_kg = m_dm_gev * 1.782_661_92e-27;
let t_k = t_kev * 1e3 * 1.602e-19 / K_B;
(3.0 * K_B * t_k / m_kg).sqrt() / 1e3
}