use serde::{Deserialize, Serialize};
use crate::error::{MimamsaError, ensure_finite, require_all_finite, require_finite};
use tracing::instrument;
pub const H0_KM_S_MPC: f64 = 67.4; pub const MPC_IN_KM: f64 = 3.085_677_581e19;
pub const H0: f64 = H0_KM_S_MPC / MPC_IN_KM;
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct CosmologicalParameters {
pub h0: f64,
pub omega_m: f64,
pub omega_r: f64,
pub omega_lambda: f64,
pub omega_k: f64,
}
impl CosmologicalParameters {
#[must_use]
pub fn planck2018() -> Self {
let omega_m = 0.315;
let omega_r = 9.15e-5;
let omega_lambda = 1.0 - omega_m - omega_r;
Self {
h0: 67.4,
omega_m,
omega_r,
omega_lambda,
omega_k: 0.0,
}
}
#[must_use]
pub fn is_flat(&self) -> bool {
(self.omega_m + self.omega_r + self.omega_lambda + self.omega_k - 1.0).abs() < 1e-6
}
}
impl Default for CosmologicalParameters {
fn default() -> Self {
Self::planck2018()
}
}
#[inline]
pub fn hubble_parameter(params: &CosmologicalParameters, z: f64) -> Result<f64, MimamsaError> {
require_finite(z, "hubble_parameter")?;
let z1 = 1.0 + z;
let z2 = z1 * z1;
let z3 = z2 * z1;
let z4 = z3 * z1;
let h0_si = params.h0 / MPC_IN_KM;
ensure_finite(
h0_si
* (params.omega_r * z4
+ params.omega_m * z3
+ params.omega_k * z2
+ params.omega_lambda)
.sqrt(),
"hubble_parameter",
)
}
#[inline]
pub fn critical_density(h: f64) -> Result<f64, MimamsaError> {
require_finite(h, "critical_density")?;
ensure_finite(
3.0 * h * h / (8.0 * std::f64::consts::PI * crate::constants::G),
"critical_density",
)
}
#[instrument(level = "trace", skip(params))]
#[inline]
pub fn deceleration_parameter_now(params: &CosmologicalParameters) -> Result<f64, MimamsaError> {
require_all_finite(
&[params.omega_m, params.omega_r, params.omega_lambda],
"deceleration_parameter_now",
)?;
ensure_finite(
params.omega_m / 2.0 + params.omega_r - params.omega_lambda,
"deceleration_parameter_now",
)
}
#[instrument(level = "debug", skip(params))]
pub fn age_of_universe(
params: &CosmologicalParameters,
z_max: f64,
n: usize,
) -> Result<f64, MimamsaError> {
require_finite(z_max, "age_of_universe")?;
if n == 0 {
return Ok(0.0);
}
let dz = z_max / 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, "age_of_universe")
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_planck_is_flat() {
assert!(CosmologicalParameters::planck2018().is_flat());
}
#[test]
fn test_non_flat_universe() {
let mut params = CosmologicalParameters::planck2018();
params.omega_k = 0.1;
assert!(!params.is_flat());
}
#[test]
fn test_hubble_at_z0() {
let params = CosmologicalParameters::planck2018();
let h = hubble_parameter(¶ms, 0.0).unwrap();
let h0_si = params.h0 / MPC_IN_KM;
assert!((h - h0_si).abs() / h0_si < 0.01);
}
#[test]
fn test_hubble_increases_with_z() {
let params = CosmologicalParameters::planck2018();
let h0 = hubble_parameter(¶ms, 0.0).unwrap();
let h1 = hubble_parameter(¶ms, 1.0).unwrap();
let h10 = hubble_parameter(¶ms, 10.0).unwrap();
assert!(h1 > h0);
assert!(h10 > h1);
}
#[test]
fn test_deceleration_parameter() {
let params = CosmologicalParameters::planck2018();
let q = deceleration_parameter_now(¶ms).unwrap();
assert!(q < 0.0);
}
#[test]
fn test_age_of_universe() {
let params = CosmologicalParameters::planck2018();
let age_s = age_of_universe(¶ms, 1100.0, 10000).unwrap();
let age_gyr = age_s / (365.25 * 24.0 * 3600.0 * 1e9);
assert!(age_gyr > 13.0 && age_gyr < 14.5);
}
#[test]
fn test_critical_density_today() {
let h0_si = H0_KM_S_MPC / MPC_IN_KM;
let rho_c = critical_density(h0_si).unwrap();
assert!(rho_c > 8e-27 && rho_c < 1.1e-26);
}
}