use crate::{AlfvenSpeed, MagneticPressure};
use crate::{Density, PhysicalField, PhysicsError};
use core::fmt::Debug;
use deep_causality_algebra::RealField;
use deep_causality_multivector::MultiVector;
use deep_causality_num::FromPrimitive;
use deep_causality_tensor::CausalTensor;
use deep_causality_topology::SimplicialManifold;
pub fn alfven_speed_kernel<R>(
b_field: &PhysicalField<R>,
density: &Density<R>,
permeability: R,
) -> Result<AlfvenSpeed<R>, PhysicsError>
where
R: RealField,
{
let b_mag = b_field.inner().squared_magnitude().sqrt();
let rho = density.value();
if permeability <= R::zero() {
return Err(PhysicsError::PhysicalInvariantBroken(
"Permeability must be positive".into(),
));
}
if rho < R::zero() {
return Err(PhysicsError::PhysicalInvariantBroken(
"Density cannot be negative".into(),
));
}
if rho == R::zero() {
return Err(PhysicsError::Singularity(
"Zero density in Alfven speed".into(),
));
}
let denom = (permeability * rho).sqrt();
let va = b_mag / denom;
AlfvenSpeed::new(va)
}
pub fn magnetic_pressure_kernel<R>(
b_field: &PhysicalField<R>,
permeability: R,
) -> Result<MagneticPressure<R>, PhysicsError>
where
R: RealField + FromPrimitive,
{
let b_sq = b_field.inner().squared_magnitude();
if permeability <= R::zero() {
return Err(PhysicsError::PhysicalInvariantBroken(
"Permeability must be positive".into(),
));
}
let two = R::from_f64(2.0)
.ok_or_else(|| PhysicsError::NumericalInstability("R::from_f64(2.0) failed".into()))?;
let pb = b_sq / (two * permeability);
MagneticPressure::new(pb)
}
pub fn ideal_induction_kernel<R>(
v_manifold: &SimplicialManifold<R, R>,
b_manifold: &SimplicialManifold<R, R>,
) -> Result<CausalTensor<R>, PhysicsError>
where
R: RealField + FromPrimitive + Default + PartialEq + Debug,
{
let complex = v_manifold.complex();
let skeletons = complex.skeletons();
if skeletons.len() < 4 {
return Err(PhysicsError::DimensionMismatch(format!(
"ideal induction needs a 3D complex: a 2-form contracts to a 1-form only there. This \
complex has {} skeletons, so it is {}-dimensional",
skeletons.len(),
skeletons.len().saturating_sub(1)
)));
}
if v_manifold.complex() != b_manifold.complex() {
return Err(PhysicsError::DimensionMismatch(
"ideal induction reads the velocity 1-form and the magnetic 2-form on one complex; \
the two manifolds carry different complexes"
.into(),
));
}
let n0 = skeletons[0].simplices().len();
let n1 = skeletons[1].simplices().len();
let n2 = skeletons[2].simplices().len();
if v_manifold.data().len() < n0 + n1 + n2 {
return Err(PhysicsError::DimensionMismatch(
"v_manifold data too small".into(),
));
}
if b_manifold.data().len() < n0 + n1 + n2 {
return Err(PhysicsError::DimensionMismatch(
"b_manifold data too small".into(),
));
}
let v_slice = &v_manifold.data().as_slice()[n0..n0 + n1];
let b_slice = &b_manifold.data().as_slice()[n0 + n1..n0 + n1 + n2];
let v_form = CausalTensor::new(v_slice.to_vec(), vec![n1])?;
let b_form = CausalTensor::new(b_slice.to_vec(), vec![n2])?;
let i_v_b = v_manifold.interior_product(&v_form, &b_form, 2)?;
if complex.coboundary_operators().len() <= 1 {
return Err(PhysicsError::CalculationError(
"Coboundary operator for 1-forms not available".into(),
));
}
let d_1 = &complex.coboundary_operators()[1];
if d_1.shape() != (n2, n1) {
return Err(PhysicsError::DimensionMismatch(format!(
"ideal induction needs the coboundary on 1-forms with shape ({n2}, {n1}); \
coboundary_operators()[1] has shape {:?}",
d_1.shape()
)));
}
let d_i_v_b = d_1.vec_mult_real(i_v_b.as_slice())?;
let dt_b: Vec<R> = d_i_v_b.into_iter().map(|x| R::zero() - x).collect();
let len = dt_b.len();
CausalTensor::new(dt_b, vec![len]).map_err(PhysicsError::from)
}