use crate::PhysicsError;
use crate::kernels::dynamics::quantities::{Length, Speed};
use crate::kernels::fluids::quantities::{Density, KinematicViscosity, Viscosity};
use deep_causality_num::RealField;
#[inline]
fn ensure_nonzero<R: RealField>(val: R, ctx: &str) -> Result<R, PhysicsError> {
if val == R::zero() {
Err(PhysicsError::PhysicalInvariantBroken(format!(
"{}: divisor is zero",
ctx
)))
} else {
Ok(val)
}
}
pub fn reynolds_number_kernel<R>(
u: &Speed<R>,
length: &Length<R>,
nu: &KinematicViscosity<R>,
) -> Result<R, PhysicsError>
where
R: RealField,
{
let v = ensure_nonzero(nu.value(), "reynolds_number_kernel")?;
Ok(u.value() * length.value() / v)
}
pub fn mach_number_kernel<R>(u: &Speed<R>, sound_speed: &Speed<R>) -> Result<R, PhysicsError>
where
R: RealField,
{
let a = ensure_nonzero(sound_speed.value(), "mach_number_kernel")?;
Ok(u.value() / a)
}
pub fn froude_number_kernel<R>(
u: &Speed<R>,
gravity: R,
length: &Length<R>,
) -> Result<R, PhysicsError>
where
R: RealField,
{
let g_l = gravity * length.value();
if g_l <= R::zero() {
return Err(PhysicsError::PhysicalInvariantBroken(
"froude_number_kernel: gravity · length must be positive".into(),
));
}
Ok(u.value() / g_l.sqrt())
}
pub fn weber_number_kernel<R>(
rho: &Density<R>,
u: &Speed<R>,
length: &Length<R>,
surface_tension: R,
) -> Result<R, PhysicsError>
where
R: RealField,
{
let sigma = ensure_nonzero(surface_tension, "weber_number_kernel")?;
let u_val = u.value();
Ok(rho.value() * u_val * u_val * length.value() / sigma)
}
pub fn prandtl_number_kernel<R>(
nu: &KinematicViscosity<R>,
thermal_diffusivity: R,
) -> Result<R, PhysicsError>
where
R: RealField,
{
let alpha = ensure_nonzero(thermal_diffusivity, "prandtl_number_kernel")?;
Ok(nu.value() / alpha)
}
pub fn peclet_number_kernel<R>(
u: &Speed<R>,
length: &Length<R>,
thermal_diffusivity: R,
) -> Result<R, PhysicsError>
where
R: RealField,
{
let alpha = ensure_nonzero(thermal_diffusivity, "peclet_number_kernel")?;
Ok(u.value() * length.value() / alpha)
}
pub fn strouhal_number_kernel<R>(
frequency: R,
length: &Length<R>,
u: &Speed<R>,
) -> Result<R, PhysicsError>
where
R: RealField,
{
let u_val = ensure_nonzero(u.value(), "strouhal_number_kernel")?;
Ok(frequency * length.value() / u_val)
}
pub fn knudsen_number_kernel<R>(mean_free_path: R, length: &Length<R>) -> Result<R, PhysicsError>
where
R: RealField,
{
let l = ensure_nonzero(length.value(), "knudsen_number_kernel")?;
Ok(mean_free_path / l)
}
pub fn richardson_number_kernel<R>(
gravity: R,
expansion_coefficient: R,
delta_temperature: R,
length: &Length<R>,
u: &Speed<R>,
) -> Result<R, PhysicsError>
where
R: RealField,
{
let u_val = ensure_nonzero(u.value(), "richardson_number_kernel")?;
Ok(gravity * expansion_coefficient * delta_temperature * length.value() / (u_val * u_val))
}
pub fn rayleigh_number_kernel<R>(
gravity: R,
expansion_coefficient: R,
delta_temperature: R,
length: &Length<R>,
nu: &KinematicViscosity<R>,
thermal_diffusivity: R,
) -> Result<R, PhysicsError>
where
R: RealField,
{
let v = ensure_nonzero(nu.value(), "rayleigh_number_kernel: nu")?;
let alpha = ensure_nonzero(thermal_diffusivity, "rayleigh_number_kernel: alpha")?;
let l = length.value();
Ok(gravity * expansion_coefficient * delta_temperature * l * l * l / (v * alpha))
}
pub fn grashof_number_kernel<R>(
gravity: R,
expansion_coefficient: R,
delta_temperature: R,
length: &Length<R>,
nu: &KinematicViscosity<R>,
) -> Result<R, PhysicsError>
where
R: RealField,
{
let v = ensure_nonzero(nu.value(), "grashof_number_kernel")?;
let l = length.value();
Ok(gravity * expansion_coefficient * delta_temperature * l * l * l / (v * v))
}
pub fn eckert_number_kernel<R>(
u: &Speed<R>,
specific_heat: R,
delta_temperature: R,
) -> Result<R, PhysicsError>
where
R: RealField,
{
let denom = ensure_nonzero(
specific_heat * delta_temperature,
"eckert_number_kernel: c_p · ΔT",
)?;
let u_val = u.value();
Ok(u_val * u_val / denom)
}
pub fn schmidt_number_kernel<R>(
nu: &KinematicViscosity<R>,
mass_diffusivity: R,
) -> Result<R, PhysicsError>
where
R: RealField,
{
let d = ensure_nonzero(mass_diffusivity, "schmidt_number_kernel")?;
Ok(nu.value() / d)
}
pub fn lewis_number_kernel<R>(
thermal_diffusivity: R,
mass_diffusivity: R,
) -> Result<R, PhysicsError>
where
R: RealField,
{
let d = ensure_nonzero(mass_diffusivity, "lewis_number_kernel")?;
Ok(thermal_diffusivity / d)
}
pub fn particle_stokes_number_kernel<R>(
particle_relaxation_time: R,
u: &Speed<R>,
length: &Length<R>,
) -> Result<R, PhysicsError>
where
R: RealField,
{
let l = ensure_nonzero(length.value(), "particle_stokes_number_kernel")?;
Ok(particle_relaxation_time * u.value() / l)
}
pub fn capillary_number_kernel<R>(
mu: &Viscosity<R>,
u: &Speed<R>,
surface_tension: R,
) -> Result<R, PhysicsError>
where
R: RealField,
{
let sigma = ensure_nonzero(surface_tension, "capillary_number_kernel")?;
Ok(mu.value() * u.value() / sigma)
}
pub fn bond_number_kernel<R>(
rho: &Density<R>,
gravity: R,
length: &Length<R>,
surface_tension: R,
) -> Result<R, PhysicsError>
where
R: RealField,
{
let sigma = ensure_nonzero(surface_tension, "bond_number_kernel")?;
let l = length.value();
Ok(rho.value() * gravity * l * l / sigma)
}
pub fn nusselt_number_kernel<R>(
heat_transfer_coefficient: R,
length: &Length<R>,
thermal_conductivity: R,
) -> Result<R, PhysicsError>
where
R: RealField,
{
let k = ensure_nonzero(thermal_conductivity, "nusselt_number_kernel")?;
Ok(heat_transfer_coefficient * length.value() / k)
}