use crate::constants::physical::{C, G};
use std::f64::consts::PI;
#[derive(Clone, Debug, PartialEq)]
pub struct SpikeProfile {
pub m_bh_kg: f64,
pub rho_0: f64,
pub r_0: f64,
pub gamma_sp: f64,
}
pub fn spike_density(r: f64, m_bh_kg: f64, rho_0: f64, r_0: f64, gamma_sp: f64) -> f64 {
let r_sp = spike_radius(m_bh_kg, rho_0, r_0, gamma_sp);
if r >= r_sp || r <= 0.0 {
return 0.0;
}
let r_isco = 6.0 * G * m_bh_kg / (C * C);
if r < r_isco {
return 0.0;
}
rho_0 * (r_0 / r).powf(gamma_sp) * (1.0 - r_isco / r).powi(3)
}
pub fn spike_radius(m_bh_kg: f64, rho_0: f64, r_0: f64, gamma_sp: f64) -> f64 {
let r_h = G * m_bh_kg / (200.0 * 1e3 * 1e3);
let factor = m_bh_kg / (4.0 * PI * rho_0 * r_0.powf(gamma_sp));
factor.powf(1.0 / (3.0 - gamma_sp)).min(r_h)
}
pub fn spike_slope_from_nfw(gamma_init: f64) -> f64 {
(9.0 - 2.0 * gamma_init) / (4.0 - gamma_init)
}
pub fn spike_slope_from_initial(gamma_init: f64) -> f64 {
spike_slope_from_nfw(gamma_init)
}
pub fn annihilation_plateau_density(m_dm_gev: f64, sigma_v_cm3_s: f64, t_bh_gyr: f64) -> f64 {
let m_dm_kg = m_dm_gev * 1.782_661_92e-27;
let t_s = t_bh_gyr * 3.156e16;
let sigma_v_si = sigma_v_cm3_s * 1e-6;
m_dm_kg / (sigma_v_si * t_s)
}
pub fn spike_density_with_annihilation(
r: f64,
m_bh_kg: f64,
rho_0: f64,
r_0: f64,
gamma_sp: f64,
rho_plateau: f64,
) -> f64 {
let rho_sp = spike_density(r, m_bh_kg, rho_0, r_0, gamma_sp);
if rho_sp > rho_plateau {
rho_plateau
} else {
rho_sp
}
}
pub fn annihilation_luminosity_spike(
profile: &SpikeProfile,
sigma_v_cm3_s: f64,
m_dm_gev: f64,
t_bh_gyr: f64,
n_steps: usize,
) -> f64 {
let rho_plat = annihilation_plateau_density(m_dm_gev, sigma_v_cm3_s, t_bh_gyr);
let r_isco = 6.0 * G * profile.m_bh_kg / (C * C);
let r_sp = spike_radius(
profile.m_bh_kg,
profile.rho_0,
profile.r_0,
profile.gamma_sp,
);
let sigma_v_si = sigma_v_cm3_s * 1e-6;
let m_dm_kg = m_dm_gev * 1.782_661_92e-27;
let log_r_min = r_isco.ln();
let log_r_max = r_sp.ln();
let d_log_r = (log_r_max - log_r_min) / n_steps as f64;
let mut luminosity = 0.0;
for i in 0..n_steps {
let log_r = log_r_min + (i as f64 + 0.5) * d_log_r;
let r = log_r.exp();
let rho = spike_density_with_annihilation(
r,
profile.m_bh_kg,
profile.rho_0,
profile.r_0,
profile.gamma_sp,
rho_plat,
);
luminosity += rho * rho * r * r * r * d_log_r;
}
luminosity * 4.0 * PI * sigma_v_si / (2.0 * m_dm_kg * m_dm_kg)
}
pub fn adiabatic_growth_density(
r_f: f64,
rho_initial: &dyn Fn(f64) -> f64,
m_bh_kg: f64,
m_halo_fn: &dyn Fn(f64) -> f64,
n_iter: usize,
) -> f64 {
let mut r_i = r_f;
for _ in 0..n_iter {
let m_f = G * m_bh_kg + m_halo_fn(r_f) * G;
let m_i = m_halo_fn(r_i) * G;
if m_i < 1e-100 {
break;
}
r_i = r_f * m_f / m_i;
}
let rho_i = rho_initial(r_i);
rho_i * (r_i / r_f).powi(2) * m_halo_fn(r_i) / (m_halo_fn(r_f) + m_bh_kg)
}
pub fn dynamical_relaxation_spike_slope(gamma_sp: f64) -> f64 {
1.5_f64.min(gamma_sp)
}
pub fn spike_enclosed_mass(
m_bh_kg: f64,
rho_0: f64,
r_0: f64,
gamma_sp: f64,
r_max: f64,
n_steps: usize,
) -> f64 {
let r_isco = 6.0 * G * m_bh_kg / (C * C);
let log_r_min = r_isco.ln();
let log_r_max = r_max.ln();
let d_log_r = (log_r_max - log_r_min) / n_steps as f64;
let mut mass = 0.0;
for i in 0..n_steps {
let log_r = log_r_min + (i as f64 + 0.5) * d_log_r;
let r = log_r.exp();
let rho = spike_density(r, m_bh_kg, rho_0, r_0, gamma_sp);
mass += rho * r * r * r * d_log_r;
}
mass * 4.0 * PI
}
pub fn isco_radius(m_bh_kg: f64) -> f64 {
6.0 * G * m_bh_kg / (C * C)
}
pub fn schwarzschild_radius(m_bh_kg: f64) -> f64 {
2.0 * G * m_bh_kg / (C * C)
}
pub fn gravitational_capture_radius(m_bh_kg: f64, v_dm_km_s: f64) -> f64 {
let v = v_dm_km_s * 1e3;
2.0 * G * m_bh_kg / (v * v)
}