use crate::constants::{
JARVINEN_ADAMS_BASELINE_CA0, JARVINEN_ADAMS_PRESERVED_DRAG_M2, real_from_f64,
};
use crate::{Area, Density, Force, PhysicsError, Pressure, Speed};
use alloc::format;
use deep_causality_algebra::RealField;
use deep_causality_num::FromPrimitive;
fn interp_digitized_table<R>(
table: &[(f64, f64)],
x: R,
what: &'static str,
) -> Result<R, PhysicsError>
where
R: RealField + FromPrimitive,
{
if !x.is_finite() {
return Err(PhysicsError::PhysicalInvariantBroken(
"Interpolation input must be finite".into(),
));
}
let lo: R = real_from_f64(table[0].0);
let hi: R = real_from_f64(table[table.len() - 1].0);
if x < lo || x > hi {
return Err(PhysicsError::PhysicalInvariantBroken(format!(
"{what}: input outside the digitized domain"
)));
}
for w in table.windows(2) {
let (x0, y0) = w[0];
let (x1, y1) = w[1];
let x0r: R = real_from_f64(x0);
let x1r: R = real_from_f64(x1);
if x <= x1r {
let y0r: R = real_from_f64(y0);
let y1r: R = real_from_f64(y1);
let t = (x - x0r) / (x1r - x0r);
return Ok(y0r + t * (y1r - y0r));
}
}
Err(PhysicsError::NumericalInstability(
"interp_digitized_table: bracket not found".into(),
))
}
pub fn srp_thrust_coefficient_kernel<R>(
thrust: Force<R>,
q_inf: Pressure<R>,
s_ref: Area<R>,
) -> Result<R, PhysicsError>
where
R: RealField + FromPrimitive,
{
let t = thrust.value();
if t < R::zero() {
return Err(PhysicsError::PhysicalInvariantBroken(
"Thrust cannot be negative".into(),
));
}
let q = q_inf.value();
if q <= R::zero() {
return Err(PhysicsError::Singularity(
"Freestream dynamic pressure must be positive".into(),
));
}
let s = s_ref.value();
if s <= R::zero() {
return Err(PhysicsError::Singularity(
"Reference area must be positive".into(),
));
}
Ok(t / (q * s))
}
pub fn momentum_flux_ratio_kernel<R>(
rho_jet: Density<R>,
u_jet: Speed<R>,
rho_inf: Density<R>,
u_inf: Speed<R>,
) -> Result<R, PhysicsError>
where
R: RealField + FromPrimitive,
{
let denom = rho_inf.value() * u_inf.value() * u_inf.value();
if denom <= R::zero() {
return Err(PhysicsError::Singularity(
"Freestream momentum flux must be positive".into(),
));
}
Ok(rho_jet.value() * u_jet.value() * u_jet.value() / denom)
}
pub fn srp_preserved_drag_fraction_kernel<R>(c_t: R) -> Result<R, PhysicsError>
where
R: RealField + FromPrimitive,
{
if c_t < R::zero() {
return Err(PhysicsError::PhysicalInvariantBroken(
"Thrust coefficient cannot be negative".into(),
));
}
interp_digitized_table(
&JARVINEN_ADAMS_PRESERVED_DRAG_M2,
c_t,
"srp_preserved_drag_fraction_kernel",
)
}
pub fn jarvinen_adams_baseline_axial_coefficient_kernel<R>(mach: R) -> Result<R, PhysicsError>
where
R: RealField + FromPrimitive,
{
interp_digitized_table(
&JARVINEN_ADAMS_BASELINE_CA0,
mach,
"jarvinen_adams_baseline_axial_coefficient_kernel",
)
}
pub fn srp_total_axial_force_coefficient_kernel<R>(c_t: R, mach: R) -> Result<R, PhysicsError>
where
R: RealField + FromPrimitive,
{
let fraction = srp_preserved_drag_fraction_kernel(c_t)?;
let ca0 = jarvinen_adams_baseline_axial_coefficient_kernel(mach)?;
Ok(c_t + fraction * ca0)
}
pub fn srp_flow_regime_margin_kernel<R>(c_t: R, transition_c_t: R) -> Result<R, PhysicsError>
where
R: RealField + FromPrimitive,
{
if c_t < R::zero() || !c_t.is_finite() {
return Err(PhysicsError::PhysicalInvariantBroken(
"Thrust coefficient must be finite and non-negative".into(),
));
}
if transition_c_t <= R::zero() || !transition_c_t.is_finite() {
return Err(PhysicsError::PhysicalInvariantBroken(
"Transition thrust coefficient must be finite and positive".into(),
));
}
Ok(c_t - transition_c_t)
}