use crate::structure::power_spectrum::*;
use std::f64::consts::PI;
pub struct HalofitSpectrum {
pub omega_m: f64,
pub omega_b: f64,
pub h: f64,
pub sigma_8: f64,
pub n_s: f64,
}
impl HalofitSpectrum {
pub fn new(omega_m: f64, omega_b: f64, h: f64, sigma_8: f64, n_s: f64) -> Self {
HalofitSpectrum {
omega_m,
omega_b,
h,
sigma_8,
n_s,
}
}
pub fn k_nonlinear(&self, z: f64) -> f64 {
let growth = 1.0 / (1.0 + z);
let k_nl_z0 = 0.3 * (self.sigma_8 / 0.8).powf(1.5);
k_nl_z0 / growth
}
pub fn n_eff(&self, k: f64, z: f64) -> f64 {
let dk = k * 0.01;
let p1 = self.linear_power(k - dk, z);
let p2 = self.linear_power(k + dk, z);
if p1 > 0.0 && p2 > 0.0 {
(p2 / p1).ln() / (2.0 * dk / k).ln()
} else {
-3.0 }
}
pub fn n_curv(&self, k: f64, z: f64) -> f64 {
let dk = k * 0.01;
let n1 = self.n_eff(k - dk, z);
let n2 = self.n_eff(k + dk, z);
(n2 - n1) / (2.0 * dk / k)
}
fn linear_power(&self, k: f64, z: f64) -> f64 {
matter_power_spectrum(k, z, self.omega_m, self.omega_b,
self.h, 2.1e-9, self.n_s)
}
pub fn nonlinear_power(&self, k: f64, z: f64) -> f64 {
let p_lin = self.linear_power(k, z);
let k_nl = self.k_nonlinear(z);
let y = k / k_nl;
let boost = if y < 0.1 {
1.0
} else if y > 10.0 {
y.powf(3.0) * 2.0
} else {
1.0 + y.powi(3) / (1.0 + y)
};
p_lin * boost
}
pub fn boost_factor(&self, k: f64, z: f64) -> f64 {
let p_nl = self.nonlinear_power(k, z);
let p_lin = self.linear_power(k, z);
if p_lin > 0.0 {
(p_nl / p_lin).sqrt()
} else {
1.0
}
}
}
pub fn nonlinear_dimensionless_power(
k: f64,
z: f64,
omega_m: f64,
omega_b: f64,
h: f64,
sigma_8: f64,
n_s: f64,
) -> f64 {
let halofit = HalofitSpectrum::new(omega_m, omega_b, h, sigma_8, n_s);
let p_nl = halofit.nonlinear_power(k, z);
k.powi(3) * p_nl / (2.0 * PI.powi(2))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_halofit_k_nonlinear() {
let halofit = HalofitSpectrum::new(0.3, 0.05, 0.7, 0.8, 0.96);
let k_nl = halofit.k_nonlinear(0.0);
assert!(k_nl > 0.01 && k_nl < 5.0, "k_nl = {}", k_nl);
}
#[test]
fn test_boost_factor_increases_at_small_scales() {
let halofit = HalofitSpectrum::new(0.3, 0.05, 0.7, 0.8, 0.96);
let boost_large = halofit.boost_factor(0.01, 0.0);
let boost_small = halofit.boost_factor(1.0, 0.0);
assert!(boost_small >= boost_large,
"boost_small = {}, boost_large = {}", boost_small, boost_large);
}
#[test]
fn test_nonlinear_power_positive() {
let halofit = HalofitSpectrum::new(0.3, 0.05, 0.7, 0.8, 0.96);
let p_nl = halofit.nonlinear_power(0.1, 0.0);
assert!(p_nl > 0.0);
}
}