use super::friedmann::{CosmologicalParameters, MPC_IN_KM, hubble_parameter};
use crate::error::{MimamsaError, ensure_finite, require_finite};
use tracing::instrument;
#[instrument(level = "debug", skip(params))]
pub fn comoving_distance(
params: &CosmologicalParameters,
z: f64,
n: usize,
) -> Result<f64, MimamsaError> {
require_finite(z, "comoving_distance")?;
if n == 0 {
return Ok(0.0);
}
let c = crate::constants::C;
let dz = z / n as f64;
let mut integral = 0.0;
for i in 0..n {
let z0 = i as f64 * dz;
let z1 = (i + 1) as f64 * dz;
let f0 = 1.0 / hubble_parameter(params, z0)?;
let f1 = 1.0 / hubble_parameter(params, z1)?;
integral += 0.5 * (f0 + f1) * dz;
}
ensure_finite(c * integral, "comoving_distance")
}
#[instrument(level = "debug", skip(params))]
pub fn luminosity_distance(
params: &CosmologicalParameters,
z: f64,
n: usize,
) -> Result<f64, MimamsaError> {
require_finite(z, "luminosity_distance")?;
ensure_finite(
(1.0 + z) * comoving_distance(params, z, n)?,
"luminosity_distance",
)
}
#[instrument(level = "debug", skip(params))]
pub fn angular_diameter_distance(
params: &CosmologicalParameters,
z: f64,
n: usize,
) -> Result<f64, MimamsaError> {
require_finite(z, "angular_diameter_distance")?;
ensure_finite(
comoving_distance(params, z, n)? / (1.0 + z),
"angular_diameter_distance",
)
}
#[instrument(level = "debug", skip(params))]
pub fn lookback_time(
params: &CosmologicalParameters,
z: f64,
n: usize,
) -> Result<f64, MimamsaError> {
require_finite(z, "lookback_time")?;
if n == 0 {
return Ok(0.0);
}
let dz = z / n as f64;
let mut integral = 0.0;
for i in 0..n {
let z0 = i as f64 * dz;
let z1 = (i + 1) as f64 * dz;
let f0 = 1.0 / ((1.0 + z0) * hubble_parameter(params, z0)?);
let f1 = 1.0 / ((1.0 + z1) * hubble_parameter(params, z1)?);
integral += 0.5 * (f0 + f1) * dz;
}
ensure_finite(integral, "lookback_time")
}
#[inline]
pub fn hubble_distance(params: &CosmologicalParameters) -> Result<f64, MimamsaError> {
require_finite(params.h0, "hubble_distance")?;
let c = crate::constants::C;
let h0_si = params.h0 / MPC_IN_KM;
ensure_finite(c / h0_si, "hubble_distance")
}
#[inline]
pub fn scale_factor(z: f64) -> Result<f64, MimamsaError> {
require_finite(z, "scale_factor")?;
ensure_finite(1.0 / (1.0 + z), "scale_factor")
}
#[inline]
pub fn redshift_from_scale_factor(a: f64) -> Result<f64, MimamsaError> {
require_finite(a, "redshift_from_scale_factor")?;
ensure_finite(1.0 / a - 1.0, "redshift_from_scale_factor")
}
#[inline]
pub fn cmb_temperature(z: f64) -> Result<f64, MimamsaError> {
require_finite(z, "cmb_temperature")?;
ensure_finite(2.7255 * (1.0 + z), "cmb_temperature")
}
#[cfg(test)]
mod tests {
use super::*;
const GYR_S: f64 = 365.25 * 24.0 * 3600.0 * 1e9;
const MPC_M: f64 = 3.085_677_581e22;
const GPC_M: f64 = 1e3 * MPC_M;
#[test]
fn test_scale_factor_now() {
assert!((scale_factor(0.0).unwrap() - 1.0).abs() < 1e-15);
}
#[test]
fn test_scale_factor_cmb() {
let a = scale_factor(1100.0).unwrap();
assert!(a > 8e-4 && a < 1e-3);
}
#[test]
fn test_cmb_temperature_now() {
assert!((cmb_temperature(0.0).unwrap() - 2.7255).abs() < 1e-4);
}
#[test]
fn test_cmb_temperature_decoupling() {
let t = cmb_temperature(1100.0).unwrap();
assert!(t > 2900.0 && t < 3100.0);
}
#[test]
fn test_comoving_distance_z1() {
let params = CosmologicalParameters::planck2018();
let d = comoving_distance(¶ms, 1.0, 1000).unwrap();
let d_gpc = d / (GPC_M);
assert!(d_gpc > 3.0 && d_gpc < 3.6);
}
#[test]
fn test_angular_diameter_distance_turnover() {
let params = CosmologicalParameters::planck2018();
let d1 = angular_diameter_distance(¶ms, 1.0, 1000).unwrap();
let d2 = angular_diameter_distance(¶ms, 1.6, 1000).unwrap();
let d3 = angular_diameter_distance(¶ms, 3.0, 1000).unwrap();
assert!(d2 > d1);
assert!(d2 > d3);
}
#[test]
fn test_hubble_distance() {
let params = CosmologicalParameters::planck2018();
let dh = hubble_distance(¶ms).unwrap();
let dh_gpc = dh / (GPC_M);
assert!(dh_gpc > 4.0 && dh_gpc < 5.0);
}
#[test]
fn test_lookback_time_z1() {
let params = CosmologicalParameters::planck2018();
let t = lookback_time(¶ms, 1.0, 1000).unwrap();
let t_gyr = t / GYR_S;
assert!(t_gyr > 7.0 && t_gyr < 8.5);
}
#[test]
fn test_roundtrip_scale_factor_redshift() {
let z = 2.5;
let a = scale_factor(z).unwrap();
let z2 = redshift_from_scale_factor(a).unwrap();
assert!((z - z2).abs() < 1e-12);
}
}