use crate::BOLTZMANN_CONSTANT;
use crate::{AmountOfSubstance, Energy, PhysicsError, Pressure, Probability, Temperature, Volume};
use core::fmt::Debug;
use core::iter::Sum;
use deep_causality_num::{FromPrimitive, RealField};
use deep_causality_tensor::CausalTensor;
use deep_causality_topology::SimplicialManifold;
pub fn heat_diffusion_kernel<R>(
temp_manifold: &SimplicialManifold<R, R>,
diffusivity: R,
) -> Result<CausalTensor<R>, PhysicsError>
where
R: RealField + FromPrimitive + Default + PartialEq + Debug,
{
if diffusivity < R::zero() {
return Err(PhysicsError::PhysicalInvariantBroken(
"Negative diffusivity violates second law of thermodynamics".into(),
));
}
let laplacian = temp_manifold.laplacian(0);
let diff_tensor = laplacian * (-diffusivity);
Ok(diff_tensor)
}
pub fn ideal_gas_law_kernel<R>(
pressure: Pressure<R>,
volume: Volume<R>,
moles: AmountOfSubstance<R>,
temp: Temperature<R>,
) -> Result<R, PhysicsError>
where
R: RealField,
{
let p = pressure.value();
let v = volume.value();
let n = moles.value();
let t = temp.value();
if n == R::zero() || t == R::zero() {
return Err(PhysicsError::Singularity(
"Zero moles or zero temp in ideal gas calculation".into(),
));
}
let r = (p * v) / (n * t);
Ok(r)
}
pub fn carnot_efficiency_kernel<R>(
temp_hot: Temperature<R>,
temp_cold: Temperature<R>,
) -> Result<R, PhysicsError>
where
R: RealField,
{
let th = temp_hot.value();
let tc = temp_cold.value();
if th <= R::zero() || tc < R::zero() {
return Err(PhysicsError::ZeroKelvinViolation());
}
if tc >= th {
return Err(PhysicsError::PhysicalInvariantBroken(
"Cold reservoir >= Hot reservoir".into(),
));
}
let eff = R::one() - (tc / th);
Ok(eff)
}
pub fn boltzmann_factor_kernel<R>(
energy: Energy<R>,
temp: Temperature<R>,
) -> Result<Probability<R>, PhysicsError>
where
R: RealField + FromPrimitive,
{
if temp.value() == R::zero() {
return Err(PhysicsError::ZeroKelvinViolation());
}
let e = energy.value();
let t = temp.value();
let k = R::from_f64(BOLTZMANN_CONSTANT).ok_or_else(|| {
PhysicsError::NumericalInstability("R::from_f64(BOLTZMANN_CONSTANT) failed".into())
})?;
let beta = R::one() / (k * t);
let factor = (-beta * e).exp();
Probability::<R>::new(factor)
}
pub fn shannon_entropy_kernel<R>(probs: &CausalTensor<R>) -> Result<R, PhysicsError>
where
R: RealField + Sum,
{
let data = probs.as_slice();
if data.is_empty() {
return Err(PhysicsError::DimensionMismatch(
"Probability tensor is empty".into(),
));
}
if data.iter().any(|&p| p < R::zero()) {
return Err(PhysicsError::NormalizationError(
"Negative probability in Shannon Entropy".into(),
));
}
let entropy: R = data
.iter()
.filter(|&&p| p > R::zero()) .map(|&p| -p * p.ln())
.sum();
Ok(entropy)
}
pub fn heat_capacity_kernel<R>(
diff_energy: Energy<R>,
diff_temp: Temperature<R>,
) -> Result<R, PhysicsError>
where
R: RealField,
{
if diff_temp.value() == R::zero() {
return Err(PhysicsError::PhysicalInvariantBroken(
"Zero temperature difference in heat capacity".into(),
));
}
let de = diff_energy.value();
let dt = diff_temp.value();
Ok(de / dt)
}
pub fn partition_function_kernel<R>(
energies: &CausalTensor<R>,
temp: Temperature<R>,
) -> Result<R, PhysicsError>
where
R: RealField + FromPrimitive + Sum,
{
let t = temp.value();
let k = R::from_f64(BOLTZMANN_CONSTANT).ok_or_else(|| {
PhysicsError::NumericalInstability("R::from_f64(BOLTZMANN_CONSTANT) failed".into())
})?;
if t == R::zero() {
return Err(PhysicsError::ZeroKelvinViolation());
}
let beta = R::one() / (k * t);
if !beta.is_finite() {
return Err(PhysicsError::NumericalInstability(
"Invalid beta in partition function".into(),
));
}
let data = energies.as_slice();
let lo = R::from_f64(-700.0)
.ok_or_else(|| PhysicsError::NumericalInstability("R::from_f64(-700)".into()))?;
let hi = R::from_f64(700.0)
.ok_or_else(|| PhysicsError::NumericalInstability("R::from_f64(700)".into()))?;
let z: R = data
.iter()
.map(|&e| {
let x = (-beta * e).clamp(lo, hi);
x.exp()
})
.sum();
if !z.is_finite() {
return Err(PhysicsError::NumericalInstability(
"Non-finite partition function value".into(),
));
}
Ok(z)
}