use crate::constants::physical::{C, G};
use std::f64::consts::PI;
pub fn two_point_correlation(r_mpc: f64, r_0: f64, gamma: f64) -> f64 {
(r_mpc / r_0).powf(-gamma)
}
pub const CORRELATION_LENGTH_DM: f64 = 5.0;
pub fn galaxy_bias(xi_galaxy: f64, xi_dm: f64) -> f64 {
if xi_dm > 0.0 {
(xi_galaxy / xi_dm).sqrt()
} else {
1.0
}
}
pub fn bias_from_peak_height(nu: f64) -> f64 {
let delta_c = 1.686;
1.0 + (nu * nu - 1.0) / delta_c
}
pub fn power_spectrum_to_correlation(
p_k: impl Fn(f64) -> f64,
r: f64,
k_min: f64,
k_max: f64,
steps: usize,
) -> f64 {
let dk = (k_max / k_min).ln() / steps as f64;
let mut sum = 0.0;
for i in 0..steps {
let ln_k = k_min.ln() + (i as f64 + 0.5) * dk;
let k = ln_k.exp();
let kr = k * r;
let sinc = if kr.abs() < 1e-10 { 1.0 } else { kr.sin() / kr };
sum += k * k * k * p_k(k) * sinc * dk / (2.0 * PI * PI);
}
sum
}
pub fn sigma_8_from_power_spectrum(
p_k: impl Fn(f64) -> f64,
k_min: f64,
k_max: f64,
steps: usize,
) -> f64 {
let r = 8.0;
let dk = (k_max / k_min).ln() / steps as f64;
let mut sum = 0.0;
for i in 0..steps {
let ln_k = k_min.ln() + (i as f64 + 0.5) * dk;
let k = ln_k.exp();
let x = k * r;
let w = if x < 0.01 {
1.0 - x * x / 10.0
} else {
3.0 * (x.sin() - x * x.cos()) / (x * x * x)
};
sum += k * k * k * p_k(k) * w * w * dk / (2.0 * PI * PI);
}
sum.sqrt()
}
pub const BAO_SCALE_MPC: f64 = 147.0;
pub fn angular_diameter_distance(z: f64, h0_km_s_mpc: f64, omega_m: f64) -> f64 {
let c_km_s = C * 1e-3;
let mpc = 3.0857e22;
let steps = 1000;
let dz = z / steps as f64;
let mut integral = 0.0;
for i in 0..steps {
let zi = (i as f64 + 0.5) * dz;
let ez = (omega_m * (1.0 + zi).powi(3) + (1.0 - omega_m)).sqrt();
integral += dz / ez;
}
c_km_s / h0_km_s_mpc * integral / (1.0 + z) * mpc * 1e3
}
pub fn luminosity_distance(z: f64, h0_km_s_mpc: f64, omega_m: f64) -> f64 {
angular_diameter_distance(z, h0_km_s_mpc, omega_m) * (1.0 + z) * (1.0 + z)
}
pub fn comoving_distance(z: f64, h0_km_s_mpc: f64, omega_m: f64) -> f64 {
angular_diameter_distance(z, h0_km_s_mpc, omega_m) * (1.0 + z)
}
pub fn three_point_correlation_hierarchical(xi_12: f64, xi_23: f64, xi_31: f64, q: f64) -> f64 {
q * (xi_12 * xi_23 + xi_23 * xi_31 + xi_31 * xi_12)
}
pub fn halo_model_one_halo_power(
k: f64,
m_min: f64,
m_max: f64,
dn_dm: impl Fn(f64) -> f64,
u_k_m: impl Fn(f64, f64) -> f64,
rho_mean: f64,
steps: usize,
) -> f64 {
let dlnm = ((m_max / m_min).ln()) / steps as f64;
let mut sum = 0.0;
for i in 0..steps {
let lnm = m_min.ln() + (i as f64 + 0.5) * dlnm;
let m = lnm.exp();
let uk = u_k_m(k, m);
sum += dn_dm(m) * m * m * uk * uk * dlnm / (rho_mean * rho_mean);
}
sum
}
pub fn halo_model_two_halo_power(k: f64, p_lin: f64, bias1: f64, bias2: f64) -> f64 {
let scale_dependent_correction = 1.0 / (1.0 + (k * 0.1).powi(2));
bias1 * bias2 * p_lin * scale_dependent_correction
}
pub fn nfw_fourier_profile(k: f64, m: f64, rho_s: f64, r_s: f64, c: f64) -> f64 {
let r_vir = c * r_s;
let m_nfw = 4.0 * PI * rho_s * r_s.powi(3) * ((1.0 + c).ln() - c / (1.0 + c));
let norm = m / m_nfw;
let steps = 200;
let dr = r_vir / steps as f64;
let mut integral = 0.0;
for i in 0..steps {
let r = (i as f64 + 0.5) * dr;
let x = r / r_s;
let rho = rho_s / (x * (1.0 + x) * (1.0 + x));
let kr = k * r;
let sinc = if kr < 1e-10 { 1.0 } else { kr.sin() / kr };
integral += 4.0 * PI * r * r * rho * sinc * dr;
}
norm * integral / m
}
pub fn gravitational_redshift_cluster(m: f64, r: f64) -> f64 {
G * m / (r * C * C)
}