use crate::CfdScalar;
use crate::solvers::qtt::compressible::marcher_2d::EulerStateTt2d;
use deep_causality_algebra::ConjugateScalar;
use deep_causality_physics::PhysicsError;
use deep_causality_tensor::{CausalTensorTrain, TensorTrain, Truncation};
#[derive(Clone)]
pub struct ForcingRegion<R>
where
R: CfdScalar + ConjugateScalar<Real = R>,
{
mask: CausalTensorTrain<R>,
target: [R; 4],
eta: R,
}
impl<R> ForcingRegion<R>
where
R: CfdScalar + ConjugateScalar<Real = R>,
{
pub fn new(mask: CausalTensorTrain<R>, target: [R; 4], eta: R) -> Result<Self, PhysicsError> {
if !eta.is_finite() || eta <= R::zero() {
return Err(PhysicsError::PhysicalInvariantBroken(
"ForcingRegion: penalization strength eta must be finite and positive".into(),
));
}
if target.iter().any(|t| !t.is_finite()) {
return Err(PhysicsError::PhysicalInvariantBroken(
"ForcingRegion: every target component must be finite".into(),
));
}
if target[0] <= R::zero() {
return Err(PhysicsError::PhysicalInvariantBroken(
"ForcingRegion: target density must be positive".into(),
));
}
Ok(Self { mask, target, eta })
}
pub fn mask(&self) -> &CausalTensorTrain<R> {
&self.mask
}
pub fn target(&self) -> [R; 4] {
self.target
}
pub fn eta(&self) -> R {
self.eta
}
pub fn apply(
&self,
state: &EulerStateTt2d<R>,
dt: R,
trunc: &Truncation<R>,
) -> Result<EulerStateTt2d<R>, PhysicsError> {
let ratio = dt / self.eta;
let w = if ratio < R::one() { ratio } else { R::one() };
let neg_w = R::zero() - w;
let force =
|u: &CausalTensorTrain<R>, t: R| -> Result<CausalTensorTrain<R>, PhysicsError> {
let deficit = if t == R::zero() {
u.clone()
} else {
u.add_scalar(R::zero() - t)?
};
let masked = self.mask.hadamard_rounded(&deficit, trunc)?;
Ok(u.add(&masked.scale(neg_w))?.round(trunc)?)
};
Ok([
force(&state[0], self.target[0])?,
force(&state[1], self.target[1])?,
force(&state[2], self.target[2])?,
force(&state[3], self.target[3])?,
])
}
}