use num_traits::{One, Zero, real::Real};
use diffable::traits::{
Cat, Dual, Euclidean, Field, Right, Sinister, Tensor,
calculus::{Jet, JetRegion, TensorProduct},
ι,
};
pub type MetricTensor<V> = TensorProduct<Sinister<Dual<V>>, Dual<V>>;
pub trait ScalarConst: Sized {
fn from_const(x: f64) -> Self;
}
impl ScalarConst for f64 {
fn from_const(x: f64) -> Self {
x
}
}
impl<𝒞: Cat, S, const N: usize> ScalarConst for Jet<𝒞, S, N>
where
S: ScalarConst + Field + ι,
S::C: JetRegion<𝒞>,
{
fn from_const(x: f64) -> Self {
Jet::new(S::from_const(x), [S::zero(); N])
}
}
pub trait MetricField<const N: usize> {
fn g<V>(&self, x: V) -> MetricTensor<V>
where
V: Euclidean + Tensor<Hand = Right>,
V::F: Real + ScalarConst;
}
pub struct Minkowski;
impl MetricField<4> for Minkowski {
fn g<V>(&self, x: V) -> MetricTensor<V>
where
V: Euclidean + Tensor<Hand = Right>,
V::F: Real + ScalarConst,
{
let _ = x;
TensorProduct::from_fn_ij(|i, j| {
if i == j {
if i == 0 { -V::F::one() } else { V::F::one() }
} else {
V::F::zero()
}
})
}
}
pub struct SphericalPolar;
impl MetricField<3> for SphericalPolar {
fn g<V>(&self, x: V) -> MetricTensor<V>
where
V: Euclidean + Tensor<Hand = Right>,
V::F: Real + ScalarConst,
{
let r = x[0];
let r2 = r * r;
let s2 = r2 * x[1].sin() * x[1].sin();
TensorProduct::from_fn_ij(|i, j| {
if i == j {
match i {
0 => V::F::one(),
1 => r2,
_ => s2,
}
} else {
V::F::zero()
}
})
}
}
pub struct Schwarzschild {
pub rs: f64,
pub c: f64,
}
impl MetricField<4> for Schwarzschild {
fn g<V>(&self, x: V) -> MetricTensor<V>
where
V: Euclidean + Tensor<Hand = Right>,
V::F: Real + ScalarConst,
{
let r = x[1];
let f = V::F::one() - V::F::from_const(self.rs) / r;
let c2 = V::F::from_const(self.c * self.c);
let r2 = r * r;
let s2 = r2 * x[2].sin() * x[2].sin();
TensorProduct::from_fn_ij(|i, j| {
if i == j {
match i {
0 => -f * c2,
1 => V::F::one() / f,
2 => r2,
_ => s2,
}
} else {
V::F::zero()
}
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use diffable::coords::Coords;
#[test]
fn minkowski_signature() {
let m = Minkowski;
let g = m.g(Coords([1.0, 2.0, 3.0, 4.0]));
assert_eq!(
(g[(0, 0)], g[(1, 1)], g[(2, 2)], g[(3, 3)]),
(-1.0, 1.0, 1.0, 1.0)
);
assert_eq!(g[(0, 1)], 0.0);
}
#[test]
fn spherical_polar_diagonal() {
let m = SphericalPolar;
let x = Coords([5.0, std::f64::consts::FRAC_PI_3, 0.0]);
let g = m.g(x);
let r2 = 25.0;
let s2 = r2 * (std::f64::consts::FRAC_PI_3.sin()).powi(2);
assert_eq!(g[(0, 0)], 1.0);
assert!((g[(1, 1)] - r2).abs() < 1e-12);
assert!((g[(2, 2)] - s2).abs() < 1e-12);
assert_eq!(g[(1, 2)], 0.0);
}
}