use crate::constants::*;
use std::f64::consts::PI;
#[derive(Debug, Clone, Copy)]
pub enum MassFunctionType {
PressSchechter,
ShethTormen,
Tinker,
}
pub struct HaloMassFunction {
pub omega_m: f64,
pub sigma_8: f64,
pub mf_type: MassFunctionType,
}
impl HaloMassFunction {
pub fn new(omega_m: f64, sigma_8: f64, mf_type: MassFunctionType) -> Self {
HaloMassFunction {
omega_m,
sigma_8,
mf_type,
}
}
pub fn sigma_mass(&self, mass_msun: f64, z: f64) -> f64 {
let m_star = 1e13;
let sigma_star = self.sigma_8;
let sigma = sigma_star * (mass_msun / m_star).powf(-1.0/3.0);
sigma / (1.0 + z)
}
pub fn peak_height(&self, mass_msun: f64, z: f64) -> f64 {
let delta_c = 1.686; let sigma = self.sigma_mass(mass_msun, z);
delta_c / sigma
}
pub fn multiplicity_function(&self, nu: f64) -> f64 {
match self.mf_type {
MassFunctionType::PressSchechter => {
(2.0 / PI).sqrt() * nu * (-nu.powi(2) / 2.0).exp()
},
MassFunctionType::ShethTormen => {
let a = 0.707;
let p = 0.3;
let a_nu2 = a * nu.powi(2);
let a_factor = (2.0 * a / PI).sqrt();
a_factor * (1.0 + a_nu2.powf(-p)) * (a_nu2).sqrt()
* (-a_nu2 / 2.0).exp()
},
MassFunctionType::Tinker => {
let alpha = 0.368;
let beta = 0.589;
let gamma = 0.864;
let phi = -0.729;
alpha * (1.0 + (beta / nu).powf(2.0 * phi))
* nu.powf(2.0 * beta) * (-gamma * nu.powi(2) / 2.0).exp()
},
}
}
pub fn dn_dm(&self, mass_msun: f64, z: f64) -> f64 {
let rho_crit = critical_density(70.0); let rho_m = self.omega_m * rho_crit;
let rho_m_msun_mpc3 = rho_m * (PARSEC * 1e6).powi(3) / M_SUN;
let nu = self.peak_height(mass_msun, z);
let f_nu = self.multiplicity_function(nu);
let sigma = self.sigma_mass(mass_msun, z);
let dln_sigma_dln_m = 1.0 / 3.0;
(rho_m_msun_mpc3 / mass_msun) * f_nu * (dln_sigma_dln_m / sigma)
}
pub fn number_density(&self, m_min: f64, m_max: f64, z: f64, n_bins: usize) -> f64 {
let log_m_min = m_min.ln();
let log_m_max = m_max.ln();
let dlogm = (log_m_max - log_m_min) / n_bins as f64;
let mut n_total = 0.0;
for i in 0..n_bins {
let log_m = log_m_min + (i as f64 + 0.5) * dlogm;
let m = log_m.exp();
let dn_dm = self.dn_dm(m, z);
n_total += dn_dm * m * dlogm;
}
n_total
}
pub fn halo_bias(&self, mass_msun: f64, z: f64) -> f64 {
let nu = self.peak_height(mass_msun, z);
let delta_c = 1.686;
match self.mf_type {
MassFunctionType::PressSchechter => {
1.0 + (nu.powi(2) - 1.0) / delta_c
},
MassFunctionType::ShethTormen => {
let a = 0.707;
let b_param = 0.5;
let c = 0.6;
let a_nu2 = a * nu.powi(2);
1.0 + (a_nu2 - 1.0) / delta_c
+ 2.0 * b_param / (delta_c * (1.0 + a_nu2.powf(c)))
},
MassFunctionType::Tinker => {
let y = (nu.powi(2)).max(0.01);
1.0 + 0.24 * y.ln() + 0.4 * nu.powi(2)
},
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_halo_mass_function() {
let hmf = HaloMassFunction::new(0.3, 0.8, MassFunctionType::ShethTormen);
let m = 1e14; let z = 0.0;
let dn_dm = hmf.dn_dm(m, z);
assert!(dn_dm > 0.0);
}
#[test]
fn test_halo_bias_increases_with_mass() {
let hmf = HaloMassFunction::new(0.3, 0.8, MassFunctionType::ShethTormen);
let b1 = hmf.halo_bias(1e12, 0.0);
let b2 = hmf.halo_bias(1e15, 0.0);
assert!(b2 > b1, "b1 = {}, b2 = {}", b1, b2);
}
#[test]
fn test_sigma_mass_decreases_with_mass() {
let hmf = HaloMassFunction::new(0.3, 0.8, MassFunctionType::ShethTormen);
let s1 = hmf.sigma_mass(1e12, 0.0);
let s2 = hmf.sigma_mass(1e14, 0.0);
assert!(s2 < s1);
}
}