use alloc::format;
use core::cell::Cell;
use deep_causality_calculus::Rk4;
use deep_causality_haft::Arrow;
use crate::solvers::dec::diagnostics::{dec_divergence_residual, dec_max_speed};
use crate::solvers::dec::step_output::StepOutput;
use deep_causality_physics::PhysicsError;
use deep_causality_physics::SolenoidalField;
use deep_causality_physics::VelocityOneForm;
use super::DecNsSolver;
use crate::solvers::dec::DecNsScalar;
impl<const D: usize, R: DecNsScalar> DecNsSolver<'_, D, R> {
pub fn step(&self, state: &SolenoidalField<R>) -> Result<StepOutput<R>, PhysicsError> {
let u = VelocityOneForm::from_raw(state.as_one_form().clone());
let deferred: Cell<Option<PhysicsError>> = Cell::new(None);
let n1 = state.as_one_form().len();
let rk4 = Rk4::new(self.dt, |s: &VelocityOneForm<R>| {
match self.rate.eval_projected(s, &self.cg_options) {
Ok(rate) => rate,
Err(e) => {
deferred.set(Some(e));
VelocityOneForm::from_raw(
deep_causality_tensor::CausalTensor::new(
alloc::vec![R::zero(); n1],
alloc::vec![n1],
)
.expect("1-D tensor allocation cannot fail"),
)
}
}
});
let advanced = rk4.run(u);
if let Some(e) = deferred.take() {
return Err(e);
}
let (projected, _potential) = SolenoidalField::from_open_leray_projection_weighted_opts(
&advanced,
self.manifold,
self.rate.no_slip_edges(),
self.rate.inflow_edges(),
self.rate.reference_vertices(),
self.rate.no_slip_rows(),
&self.cg_options,
None,
)?;
let projected = projected
.constrain_edges(self.rate.no_slip_edges())
.with_lift(&self.lift);
let max_speed = dec_max_speed(self.manifold, projected.as_one_form())?;
self.cfl_check(max_speed)?;
let divergence_residual = dec_divergence_residual(self.manifold, projected.as_one_form())?;
Ok(StepOutput::new(projected, max_speed, divergence_residual))
}
pub(super) fn cfl_check(&self, max_speed: R) -> Result<(), PhysicsError> {
if max_speed > R::zero() {
let advective_limit = self.cfl_advective * self.dx_min / max_speed;
if self.dt > advective_limit {
return Err(PhysicsError::PhysicalInvariantBroken(format!(
"CFL violation (advective): dt {} exceeds the limit {} \
(C_adv {} · dx_min {} / max|u| {})",
self.dt, advective_limit, self.cfl_advective, self.dx_min, max_speed
)));
}
}
let nu = self.rate.nu();
if nu > R::zero() {
let two_d = R::from_usize(2 * D)
.expect("2·D lifts into R");
let diffusive_limit = self.cfl_diffusive * self.dx_min * self.dx_min / (two_d * nu);
if self.dt > diffusive_limit {
return Err(PhysicsError::PhysicalInvariantBroken(format!(
"CFL violation (diffusive): dt {} exceeds the limit {} \
(C_diff {} · dx_min² {} / (2·{D}·ν {}))",
self.dt,
diffusive_limit,
self.cfl_diffusive,
self.dx_min * self.dx_min,
nu
)));
}
}
Ok(())
}
}