use crate::CfdScalar;
use crate::solvers::dec::DecNsSolver;
use crate::solvers::dec::boundary::BoundaryZone;
use deep_causality_physics::PhysicsError;
use deep_causality_topology::{HodgeDecomposeOptions, LatticeComplex, Manifold};
pub(crate) struct DecNs;
impl DecNs {
pub(crate) fn config() -> DecNsConfigNeedsViscosity {
DecNsConfigNeedsViscosity { _seal: () }
}
}
pub struct DecNsConfigNeedsViscosity {
_seal: (),
}
impl DecNsConfigNeedsViscosity {
pub fn viscosity<R: CfdScalar>(self, nu: R) -> DecNsConfigNeedsTimeStep<R> {
DecNsConfigNeedsTimeStep { nu }
}
}
pub struct DecNsConfigNeedsTimeStep<R: CfdScalar> {
nu: R,
}
impl<R: CfdScalar> DecNsConfigNeedsTimeStep<R> {
pub fn time_step(self, dt: R) -> DecNsConfigReady<R> {
DecNsConfigReady::new(self.nu, dt)
}
}
pub struct DecNsConfigReady<R: CfdScalar> {
nu: R,
dt: R,
cg_options: HodgeDecomposeOptions<R>,
cfl_advective: R,
cfl_diffusive: R,
warm_start: bool,
staircase_noslip: bool,
spectral_diffusion: bool,
}
impl<R: CfdScalar> DecNsConfigReady<R> {
fn new(nu: R, dt: R) -> Self {
let safety = R::from_f64(0.9).expect("0.9 lifts into R");
Self {
nu,
dt,
cg_options: HodgeDecomposeOptions::default(),
cfl_advective: safety,
cfl_diffusive: safety,
warm_start: false,
staircase_noslip: false,
spectral_diffusion: false,
}
}
pub fn cg_options(mut self, opts: HodgeDecomposeOptions<R>) -> Self {
self.cg_options = opts;
self
}
pub fn cfl_factors(mut self, advective: R, diffusive: R) -> Self {
self.cfl_advective = advective;
self.cfl_diffusive = diffusive;
self
}
pub fn warm_start(mut self) -> Self {
self.warm_start = true;
self
}
pub fn staircase_noslip(mut self) -> Self {
self.staircase_noslip = true;
self
}
pub fn spectral_diffusion(mut self) -> Self {
self.spectral_diffusion = true;
self
}
pub fn build(self) -> Result<DecNsConfig<R>, PhysicsError> {
if !self.nu.is_finite() {
return Err(PhysicsError::NumericalInstability(
"DecNsConfig: viscosity must be finite".into(),
));
}
if self.nu < R::zero() {
return Err(PhysicsError::PhysicalInvariantBroken(
"DecNsConfig: viscosity cannot be negative".into(),
));
}
if !self.dt.is_finite() || self.dt <= R::zero() {
return Err(PhysicsError::PhysicalInvariantBroken(
"DecNsConfig: dt must be finite and positive".into(),
));
}
if !self.cfl_advective.is_finite()
|| self.cfl_advective <= R::zero()
|| !self.cfl_diffusive.is_finite()
|| self.cfl_diffusive <= R::zero()
{
return Err(PhysicsError::PhysicalInvariantBroken(
"DecNsConfig: CFL safety factors must be finite and positive".into(),
));
}
Ok(DecNsConfig {
nu: self.nu,
dt: self.dt,
cg_options: self.cg_options,
cfl_advective: self.cfl_advective,
cfl_diffusive: self.cfl_diffusive,
warm_start: self.warm_start,
staircase_noslip: self.staircase_noslip,
spectral_diffusion: self.spectral_diffusion,
})
}
}
#[derive(Debug, Clone)]
pub struct DecNsConfig<R: CfdScalar> {
nu: R,
dt: R,
cg_options: HodgeDecomposeOptions<R>,
cfl_advective: R,
cfl_diffusive: R,
warm_start: bool,
staircase_noslip: bool,
spectral_diffusion: bool,
}
impl<R: CfdScalar> DecNsConfig<R> {
pub fn nu(&self) -> R {
self.nu
}
pub fn dt(&self) -> R {
self.dt
}
pub fn cg_options(&self) -> &HodgeDecomposeOptions<R> {
&self.cg_options
}
pub fn materialize_with_zones<'m, const D: usize, Z>(
&self,
manifold: &'m Manifold<LatticeComplex<D, R>, R>,
zones: Z,
) -> Result<DecNsSolver<'m, D, R>, PhysicsError>
where
Z: BoundaryZone<D, R>,
{
let mut solver = DecNsSolver::with_zones(manifold, self.nu, self.dt, zones)?
.with_cg_options(self.cg_options.clone())
.with_cfl_factors(self.cfl_advective, self.cfl_diffusive)?;
if self.warm_start {
solver = solver.with_warm_start();
}
if self.staircase_noslip {
solver = solver.with_staircase_noslip();
}
if self.spectral_diffusion {
solver = solver.with_spectral_diffusion()?;
}
Ok(solver)
}
}