use crate::config::parameters::{C, G, gravitational_radius};
pub struct KerrSpacetime {
pub mass: f64,
pub spin: f64,
pub rg: f64,
pub a: f64,
}
#[derive(Clone, Copy)]
pub struct SpacetimePoint {
pub t: f64,
pub r: f64,
pub theta: f64,
pub phi: f64,
}
impl SpacetimePoint {
pub fn new(t: f64, r: f64, theta: f64, phi: f64) -> Self {
Self { t, r, theta, phi }
}
}
impl KerrSpacetime {
pub fn new(mass: f64, spin: f64) -> Self {
let rg = gravitational_radius(mass);
Self {
mass,
spin,
rg,
a: spin * rg,
}
}
pub fn event_horizon(&self) -> f64 {
self.rg + (self.rg * self.rg - self.a * self.a).max(0.0).sqrt()
}
pub fn cauchy_horizon(&self) -> f64 {
self.rg - (self.rg * self.rg - self.a * self.a).max(0.0).sqrt()
}
pub fn ergosphere(&self, theta: f64) -> f64 {
self.rg
+ (self.rg * self.rg - self.a * self.a * theta.cos().powi(2))
.max(0.0)
.sqrt()
}
fn sigma(&self, r: f64, theta: f64) -> f64 {
r * r + self.a * self.a * theta.cos().powi(2)
}
fn delta(&self, r: f64) -> f64 {
r * r - 2.0 * self.rg * r + self.a * self.a
}
pub fn metric_g_tt(&self, r: f64, theta: f64) -> f64 {
let sigma = self.sigma(r, theta);
-(1.0 - 2.0 * self.rg * r / sigma)
}
pub fn frame_dragging(&self, r: f64, theta: f64) -> f64 {
let sigma = self.sigma(r, theta);
let a2 = self.a * self.a;
2.0 * self.rg * r * self.a * C
/ (sigma * (r * r + a2) + 2.0 * self.rg * r * a2 * theta.sin().powi(2))
}
pub fn orbital_frequency(&self, r: f64) -> f64 {
let m = self.mass;
let sign = if self.spin >= 0.0 { 1.0 } else { -1.0 };
C.powi(3) / (G * m) / ((r / self.rg).powf(1.5) + sign * self.spin)
}
pub fn trace_photon(
&self,
start: SpacetimePoint,
velocity: [f64; 3],
max_steps: usize,
dt: f64,
) -> Vec<SpacetimePoint> {
let rh = self.event_horizon();
let r_max = start.r * 10.0;
let mut trajectory = Vec::with_capacity(max_steps + 1);
trajectory.push(start);
let mut r = start.r;
let mut theta = start.theta;
let mut phi = start.phi;
let mut t = start.t;
let mut vr = velocity[0];
let mut vtheta = velocity[1];
let mut vphi = velocity[2];
for _ in 0..max_steps {
let sigma = self.sigma(r, theta);
let ar = -self.rg * C * C / sigma
+ r * (vtheta * vtheta + vphi * vphi * theta.sin().powi(2));
let atheta = -vphi * vphi * theta.sin() * theta.cos()
+ self.a * self.a * theta.sin() * theta.cos() * vr * vr / (sigma * sigma);
let aphi = -2.0 * vr * vphi / r.max(1e-10)
+ 2.0 * self.a * self.rg * vr / (sigma * r.max(1e-10));
vr += ar * dt;
vtheta += atheta * dt;
vphi += aphi * dt;
r += vr * dt;
theta = (theta + vtheta * dt / r.max(1e-10)).clamp(0.01, std::f64::consts::PI - 0.01);
phi += vphi * dt / (r * theta.sin()).max(1e-10);
t += dt;
trajectory.push(SpacetimePoint::new(t, r, theta, phi));
if r < rh || r > r_max {
break;
}
}
trajectory
}
pub fn christoffel_ttr(&self, r: f64, theta: f64) -> f64 {
let sigma = self.sigma(r, theta);
let delta = self.delta(r);
self.rg * (r * r - self.a * self.a * theta.cos().powi(2)) * delta / (sigma * sigma * sigma)
}
}