use serde::{Deserialize, Serialize};
use tracing::{instrument, warn};
use crate::error::{MimamsaError, ensure_finite, require_all_finite, require_finite};
pub use crate::constants::{C, G};
#[instrument(level = "trace")]
#[inline]
pub fn schwarzschild_radius(mass_kg: f64) -> Result<f64, MimamsaError> {
require_finite(mass_kg, "schwarzschild_radius")?;
ensure_finite(2.0 * G * mass_kg / (C * C), "schwarzschild_radius")
}
#[instrument(level = "trace")]
#[inline]
pub fn gravitational_time_dilation(mass_kg: f64, r: f64) -> Result<f64, MimamsaError> {
require_all_finite(&[mass_kg, r], "gravitational_time_dilation")?;
let rs = schwarzschild_radius(mass_kg)?;
if r <= rs {
warn!(r, rs, "time dilation requested inside event horizon");
return Err(MimamsaError::Singularity {
location: format!("r={r:.3e}"),
detail: format!("inside event horizon (r_s={rs:.3e})"),
});
}
ensure_finite((1.0 - rs / r).sqrt(), "gravitational_time_dilation")
}
#[instrument(level = "trace")]
#[inline]
pub fn gravitational_redshift(mass_kg: f64, r_emit: f64) -> Result<f64, MimamsaError> {
require_all_finite(&[mass_kg, r_emit], "gravitational_redshift")?;
ensure_finite(
1.0 / gravitational_time_dilation(mass_kg, r_emit)?,
"gravitational_redshift",
)
}
#[instrument(level = "trace")]
#[inline]
pub fn schwarzschild_isco(mass_kg: f64) -> Result<f64, MimamsaError> {
require_finite(mass_kg, "schwarzschild_isco")?;
ensure_finite(3.0 * schwarzschild_radius(mass_kg)?, "schwarzschild_isco")
}
#[instrument(level = "trace")]
#[inline]
pub fn photon_sphere_radius(mass_kg: f64) -> Result<f64, MimamsaError> {
require_finite(mass_kg, "photon_sphere_radius")?;
ensure_finite(1.5 * schwarzschild_radius(mass_kg)?, "photon_sphere_radius")
}
#[instrument(level = "trace")]
pub fn schwarzschild_orbital_velocity(mass_kg: f64, r: f64) -> Result<f64, MimamsaError> {
require_all_finite(&[mass_kg, r], "schwarzschild_orbital_velocity")?;
let rs = schwarzschild_radius(mass_kg)?;
if r <= rs {
warn!(r, rs, "orbital velocity requested inside event horizon");
return Err(MimamsaError::Singularity {
location: format!("r={r:.3e}"),
detail: "inside event horizon".to_string(),
});
}
let v_newton = (G * mass_kg / r).sqrt();
ensure_finite(
v_newton / (1.0 - rs / r).sqrt(),
"schwarzschild_orbital_velocity",
)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
pub enum MetricSignature {
MostlyPlus,
MostlyMinus,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
pub enum SpacetimeMetric {
Minkowski,
Schwarzschild,
Kerr,
FLRW,
ReissnerNordstrom,
}
#[cfg(test)]
mod tests {
use super::*;
const M_SUN: f64 = 1.989e30;
#[test]
fn test_schwarzschild_radius_sun() {
let rs = schwarzschild_radius(M_SUN).unwrap();
assert!((rs - 2953.0).abs() < 5.0);
}
#[test]
fn test_schwarzschild_radius_earth() {
let rs = schwarzschild_radius(5.972e24).unwrap();
assert!((rs - 0.00887).abs() < 0.001);
}
#[test]
fn test_time_dilation_earth_surface() {
let factor = gravitational_time_dilation(5.972e24, 6.371e6).unwrap();
assert!((factor - 1.0).abs() < 1e-8);
}
#[test]
fn test_singularity_inside_horizon() {
assert!(gravitational_time_dilation(M_SUN, 1000.0).is_err());
}
#[test]
fn test_isco_schwarzschild() {
let rs = schwarzschild_radius(M_SUN).unwrap();
let isco = schwarzschild_isco(M_SUN).unwrap();
assert!((isco - 3.0 * rs).abs() < 1e-6);
}
#[test]
fn test_photon_sphere() {
let rs = schwarzschild_radius(M_SUN).unwrap();
let rph = photon_sphere_radius(M_SUN).unwrap();
assert!((rph - 1.5 * rs).abs() < 1e-6);
}
}