use crate::PhysicsError;
use crate::Temperature;
use crate::kernels::dynamics::quantities::Speed;
use crate::kernels::fluids::quantities::{
Pressure, SpecificEnthalpy, Velocity3, VelocityGradient, ViscousStress,
};
use deep_causality_num::{FromPrimitive, RealField};
pub fn speed_of_sound_ideal_gas_kernel<R>(
gamma: R,
r_specific: R,
temperature: &Temperature<R>,
) -> Result<Speed<R>, PhysicsError>
where
R: RealField,
{
let arg = gamma * r_specific * temperature.value();
if arg <= R::zero() {
return Err(PhysicsError::PhysicalInvariantBroken(
"speed_of_sound_ideal_gas_kernel: γ·R_s·T must be positive".into(),
));
}
Speed::new(arg.sqrt())
}
pub fn specific_enthalpy_kernel<R>(cp: R, temperature: &Temperature<R>) -> SpecificEnthalpy<R>
where
R: RealField,
{
SpecificEnthalpy::new_unchecked(cp * temperature.value())
}
pub fn total_enthalpy_kernel<R>(
h: &SpecificEnthalpy<R>,
u: &Velocity3<R>,
) -> Result<SpecificEnthalpy<R>, PhysicsError>
where
R: RealField + FromPrimitive,
{
let half = R::from_f64(0.5)
.ok_or_else(|| PhysicsError::NumericalInstability("R::from_f64(0.5) failed".into()))?;
let u_raw = u.value();
let speed_sq = u_raw[0] * u_raw[0] + u_raw[1] * u_raw[1] + u_raw[2] * u_raw[2];
Ok(SpecificEnthalpy::new_unchecked(h.value() + half * speed_sq))
}
pub fn total_pressure_isentropic_kernel<R>(
p: &Pressure<R>,
mach: R,
gamma: R,
) -> Result<Pressure<R>, PhysicsError>
where
R: RealField + FromPrimitive,
{
let one = R::one();
if gamma <= one {
return Err(PhysicsError::PhysicalInvariantBroken(
"total_pressure_isentropic_kernel: γ must be > 1".into(),
));
}
let half = R::from_f64(0.5)
.ok_or_else(|| PhysicsError::NumericalInstability("R::from_f64(0.5) failed".into()))?;
let base = one + (gamma - one) * half * mach * mach;
if base <= R::zero() {
return Err(PhysicsError::PhysicalInvariantBroken(
"total_pressure_isentropic_kernel: base of exponent must be positive".into(),
));
}
let exponent = gamma / (gamma - one);
Pressure::new(p.value() * base.powf(exponent))
}
pub fn total_temperature_isentropic_kernel<R>(
t: &Temperature<R>,
mach: R,
gamma: R,
) -> Result<Temperature<R>, PhysicsError>
where
R: RealField + FromPrimitive,
{
let one = R::one();
if gamma <= one {
return Err(PhysicsError::PhysicalInvariantBroken(
"total_temperature_isentropic_kernel: γ must be > 1".into(),
));
}
let half = R::from_f64(0.5)
.ok_or_else(|| PhysicsError::NumericalInstability("R::from_f64(0.5) failed".into()))?;
let factor = one + (gamma - one) * half * mach * mach;
Temperature::new(t.value() * factor)
}
pub fn entropy_production_rate_kernel<R>(
temperature: &Temperature<R>,
tau: &ViscousStress<R>,
grad_u: &VelocityGradient<R>,
thermal_conductivity: R,
grad_temperature: &[R; 3],
) -> Result<R, PhysicsError>
where
R: RealField,
{
let t = temperature.value();
if t <= R::zero() {
return Err(PhysicsError::PhysicalInvariantBroken(
"entropy_production_rate_kernel: temperature must be > 0".into(),
));
}
if thermal_conductivity < R::zero() {
return Err(PhysicsError::PhysicalInvariantBroken(
"entropy_production_rate_kernel: thermal_conductivity must be ≥ 0".into(),
));
}
let tv = tau.value();
let gv = grad_u.value();
let phi = tv[0][0] * gv[0][0]
+ tv[0][1] * gv[0][1]
+ tv[0][2] * gv[0][2]
+ tv[1][0] * gv[1][0]
+ tv[1][1] * gv[1][1]
+ tv[1][2] * gv[1][2]
+ tv[2][0] * gv[2][0]
+ tv[2][1] * gv[2][1]
+ tv[2][2] * gv[2][2];
let grad_t_norm_sq = grad_temperature[0] * grad_temperature[0]
+ grad_temperature[1] * grad_temperature[1]
+ grad_temperature[2] * grad_temperature[2];
Ok(phi / t + thermal_conductivity * grad_t_norm_sq / (t * t))
}