use crate::constants::physical::G;
use std::f64::consts::PI;
pub fn nfw(r: f64, rho_s: f64, r_s: f64) -> f64 {
let x = r / r_s;
rho_s / (x * (1.0 + x) * (1.0 + x))
}
pub fn nfw_enclosed_mass(r: f64, rho_s: f64, r_s: f64) -> f64 {
let x = r / r_s;
4.0 * PI * rho_s * r_s * r_s * r_s * ((1.0 + x).ln() - x / (1.0 + x))
}
pub fn nfw_potential(r: f64, rho_s: f64, r_s: f64) -> f64 {
let x = r / r_s;
-4.0 * PI * G * rho_s * r_s * r_s * (1.0 + x).ln() / x
}
pub fn nfw_concentration_duffy(mass_solar: f64, redshift: f64) -> f64 {
let pivot = 2e12;
5.71 * (mass_solar / pivot).powf(-0.084) * (1.0 + redshift).powf(-0.47)
}
pub fn hernquist(r: f64, total_mass: f64, a: f64) -> f64 {
total_mass * a / (2.0 * PI * r * (r + a).powi(3))
}
pub fn hernquist_enclosed_mass(r: f64, total_mass: f64, a: f64) -> f64 {
total_mass * r * r / ((r + a) * (r + a))
}
pub fn hernquist_potential(r: f64, total_mass: f64, a: f64) -> f64 {
-G * total_mass / (r + a)
}
pub fn burkert(r: f64, rho_0: f64, r_0: f64) -> f64 {
rho_0 / ((1.0 + r / r_0) * (1.0 + (r / r_0) * (r / r_0)))
}
pub fn burkert_enclosed_mass(r: f64, rho_0: f64, r_0: f64) -> f64 {
let x = r / r_0;
PI * rho_0 * r_0 * r_0 * r_0 * ((1.0 + x * x).ln() + 2.0 * (1.0 + x).ln() - 2.0 * x.atan())
}
pub fn isothermal(sigma_v: f64, r: f64) -> f64 {
sigma_v * sigma_v / (2.0 * PI * G * r * r)
}
pub fn isothermal_enclosed_mass(sigma_v: f64, r: f64) -> f64 {
2.0 * sigma_v * sigma_v * r / G
}
pub fn einasto(r: f64, rho_e: f64, r_e: f64, alpha: f64) -> f64 {
let d_n = 3.0 * alpha - 1.0 / 3.0 + 0.0079 / alpha;
rho_e * (-d_n * ((r / r_e).powf(1.0 / alpha) - 1.0)).exp()
}
pub fn moore(r: f64, rho_s: f64, r_s: f64) -> f64 {
let x = r / r_s;
rho_s / (x.powf(1.5) * (1.0 + x).powf(1.5))
}
pub fn pseudo_isothermal(r: f64, rho_0: f64, r_c: f64) -> f64 {
rho_0 / (1.0 + (r / r_c) * (r / r_c))
}
pub fn pseudo_isothermal_enclosed_mass(r: f64, rho_0: f64, r_c: f64) -> f64 {
4.0 * PI * rho_0 * r_c * r_c * (r - r_c * (r / r_c).atan())
}
pub fn plummer(r: f64, total_mass: f64, b: f64) -> f64 {
3.0 * total_mass / (4.0 * PI * b * b * b) * (1.0 + (r / b) * (r / b)).powf(-2.5)
}
pub fn dekel_zhao(r: f64, rho_s: f64, r_s: f64, alpha: f64, beta: f64, gamma: f64) -> f64 {
let x = r / r_s;
rho_s / (x.powf(gamma) * (1.0 + x.powf(alpha)).powf((beta - gamma) / alpha))
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct HaloProfile {
pub virial_mass: f64,
pub virial_radius: f64,
pub concentration: f64,
pub scale_radius: f64,
pub rho_s: f64,
}
impl HaloProfile {
pub fn from_nfw(virial_mass: f64, concentration: f64, redshift: f64) -> Self {
let h_si = crate::constants::cosmic::HUBBLE_CONSTANT_SI;
let omega_m = crate::constants::cosmic::OMEGA_MATTER;
let omega_l = crate::constants::cosmic::OMEGA_LAMBDA;
let hz = h_si * (omega_m * (1.0 + redshift).powi(3) + omega_l).sqrt();
let rho_crit = 3.0 * hz * hz / (8.0 * PI * G);
let delta_vir = 200.0;
let r_vir = (3.0 * virial_mass / (4.0 * PI * delta_vir * rho_crit)).powf(1.0 / 3.0);
let r_s = r_vir / concentration;
let c = concentration;
let rho_s = delta_vir * rho_crit * c * c * c / (3.0 * ((1.0 + c).ln() - c / (1.0 + c)));
Self {
virial_mass,
virial_radius: r_vir,
concentration,
scale_radius: r_s,
rho_s,
}
}
pub fn density_at(&self, r: f64) -> f64 {
nfw(r, self.rho_s, self.scale_radius)
}
pub fn enclosed_mass_at(&self, r: f64) -> f64 {
nfw_enclosed_mass(r, self.rho_s, self.scale_radius)
}
pub fn circular_velocity_at(&self, r: f64) -> f64 {
(G * self.enclosed_mass_at(r) / r).sqrt()
}
pub fn potential_at(&self, r: f64) -> f64 {
nfw_potential(r, self.rho_s, self.scale_radius)
}
pub fn v_max(&self) -> f64 {
let r_max = 2.163 * self.scale_radius;
self.circular_velocity_at(r_max)
}
}