use crate::cosmology::relic::{effective_dof, entropy_dof};
use std::f64::consts::PI;
pub fn comoving_number_density_eq(x: f64, g_dm: f64) -> f64 {
let factor = g_dm * x.powf(1.5) * (-x).exp() / (2.0 * PI).powf(1.5);
factor.max(0.0)
}
pub fn collision_term_annihilation(y: f64, y_eq: f64, sigma_v_cm3_s: f64, s_entropy: f64) -> f64 {
-sigma_v_cm3_s * s_entropy * (y * y - y_eq * y_eq)
}
pub fn boltzmann_rhs(x: f64, y: f64, m_dm_gev: f64, sigma_v_cm3_s: f64, g_dm: f64) -> f64 {
let t = m_dm_gev / x;
let g_star_s = entropy_dof(t);
let mpl = 1.22e19;
let s = 2.0 * PI * PI / 45.0 * g_star_s * t * t * t;
let h = (PI * PI * effective_dof(t) / 90.0).sqrt() * t * t / mpl;
let y_eq = comoving_number_density_eq(x, g_dm) / (s + 1e-100);
let lambda = sigma_v_cm3_s * s / (h * x + 1e-100);
-lambda * (y * y - y_eq * y_eq) / x
}
pub fn solve_boltzmann_freeze_out(
m_dm_gev: f64,
sigma_v_cm3_s: f64,
g_dm: f64,
x_start: f64,
x_end: f64,
n_steps: usize,
) -> Vec<(f64, f64)> {
let dx = (x_end - x_start) / n_steps as f64;
let t0 = m_dm_gev / x_start;
let g_star_s0 = entropy_dof(t0);
let s0 = 2.0 * PI * PI / 45.0 * g_star_s0 * t0 * t0 * t0;
let y_eq0 = comoving_number_density_eq(x_start, g_dm) / (s0 + 1e-100);
let mut y = y_eq0;
let mut x = x_start;
let mut result = Vec::with_capacity(n_steps + 1);
result.push((x, y));
for _ in 0..n_steps {
let k1 = dx * boltzmann_rhs(x, y, m_dm_gev, sigma_v_cm3_s, g_dm);
let k2 = dx * boltzmann_rhs(x + 0.5 * dx, y + 0.5 * k1, m_dm_gev, sigma_v_cm3_s, g_dm);
let k3 = dx * boltzmann_rhs(x + 0.5 * dx, y + 0.5 * k2, m_dm_gev, sigma_v_cm3_s, g_dm);
let k4 = dx * boltzmann_rhs(x + dx, y + k3, m_dm_gev, sigma_v_cm3_s, g_dm);
y += (k1 + 2.0 * k2 + 2.0 * k3 + k4) / 6.0;
y = y.max(1e-30);
x += dx;
result.push((x, y));
}
result
}
pub fn relic_density_from_solution(y_final: f64, m_dm_gev: f64, x_final: f64) -> f64 {
let t_final = m_dm_gev / x_final;
let g_star_s = entropy_dof(t_final);
let s_now = 2891.2;
let s_final = 2.0 * PI * PI / 45.0 * g_star_s * t_final * t_final * t_final;
let n_now = y_final * s_now;
let rho_crit_gev_cm3 = 1.054e-5;
let h2 = 0.49;
n_now * m_dm_gev / (rho_crit_gev_cm3 * h2) * s_now / (s_final + 1e-100)
}
pub fn freeze_out_x_from_solution(solution: &[(f64, f64)], g_dm: f64, m_dm_gev: f64) -> f64 {
for (x, y) in solution.iter().skip(1) {
let t = m_dm_gev / x;
let g_star_s = entropy_dof(t);
let s = 2.0 * PI * PI / 45.0 * g_star_s * t * t * t;
let y_eq = comoving_number_density_eq(*x, g_dm) / (s + 1e-100);
if *y > 2.5 * y_eq && y_eq > 0.0 {
return *x;
}
}
solution.last().map_or(25.0, |(x, _)| *x)
}
pub fn coannihilation_effective_sigma_v(
sigma_11: f64,
sigma_12: f64,
sigma_22: f64,
g1: f64,
g2: f64,
delta_m_over_m: f64,
x: f64,
) -> f64 {
let r = g2 / g1 * (1.0 + delta_m_over_m).powf(1.5) * (-x * delta_m_over_m).exp();
let g_eff = 1.0 + r;
(sigma_11 + 2.0 * r * sigma_12 + r * r * sigma_22) / (g_eff * g_eff)
}
pub fn freeze_in_yield(coupling_sq: f64, m_mediator_gev: f64, m_dm_gev: f64, mpl_gev: f64) -> f64 {
let g_star = effective_dof(m_mediator_gev);
let phase_space = (1.0 - (m_dm_gev / m_mediator_gev).powi(2)).max(0.01).sqrt();
135.0 * coupling_sq * phase_space
/ (1.74 * 8.0 * PI * PI * PI * g_star.powf(1.5) * m_mediator_gev)
* mpl_gev
}
pub fn freeze_in_relic_density(coupling_sq: f64, m_mediator_gev: f64, m_dm_gev: f64) -> f64 {
let mpl = 1.22e19;
let y_fi = freeze_in_yield(coupling_sq, m_mediator_gev, m_dm_gev, mpl);
let s_now = 2891.2;
let rho_crit = 1.054e-5;
y_fi * m_dm_gev * s_now / rho_crit
}
pub fn boltzmann_rhs_decay(
x: f64,
y: f64,
m_parent_gev: f64,
gamma_decay: f64,
m_dm_gev: f64,
g_parent: f64,
) -> f64 {
let t = m_parent_gev / x;
let g_star = effective_dof(t);
let mpl = 1.22e19;
let h = (PI * PI * g_star / 90.0).sqrt() * t * t / mpl;
let n_eq = g_parent * (m_parent_gev * t / (2.0 * PI)).powf(1.5) * (-m_parent_gev / t).exp();
let depletion = (-y * h * x / (n_eq + 1e-300)).exp().min(1.0);
gamma_decay * n_eq * m_dm_gev * depletion / (h * x * m_parent_gev * 1e-100_f64.max(h))
}
pub fn entropy_injection_ratio(t_reheat_gev: f64, t_decay_gev: f64) -> f64 {
if t_decay_gev >= t_reheat_gev {
return 1.0;
}
let g_rh = entropy_dof(t_reheat_gev);
let g_d = entropy_dof(t_decay_gev);
(g_rh / g_d).powf(1.0 / 3.0) * t_reheat_gev / t_decay_gev
}