use deep_causality_core::{
AlternatableValue, CausalEffect, CausalFlow, CausalityError, CausalityErrorEnum, EffectLog,
PropagatingProcess,
};
use deep_causality_uncertain::{MaybeUncertain, ProbabilisticType};
use crate::solvers::dec::DecNsScalar;
use crate::solvers::dec::dec_ns_solver::DecNsSolver;
use deep_causality_physics::PhysicsError;
use deep_causality_physics::SolenoidalField;
use super::uncertain_inflow_zone::UncertainInflowZone;
#[derive(Debug)]
pub struct InflowMarchState<'m, const D: usize, R: DecNsScalar> {
solver: Option<DecNsSolver<'m, D, R>>,
field: SolenoidalField<R>,
last_good: R,
step: usize,
in_dropout: bool,
}
impl<'m, const D: usize, R: DecNsScalar> InflowMarchState<'m, D, R> {
pub fn new(
solver: DecNsSolver<'m, D, R>,
field: SolenoidalField<R>,
default_inflow: R,
) -> Self {
Self {
solver: Some(solver),
field,
last_good: default_inflow,
step: 0,
in_dropout: false,
}
}
pub fn field(&self) -> &SolenoidalField<R> {
&self.field
}
pub fn last_good(&self) -> R {
self.last_good
}
pub fn step(&self) -> usize {
self.step
}
pub fn in_dropout(&self) -> bool {
self.in_dropout
}
}
#[derive(Debug, Clone)]
pub struct InflowContext<R: ProbabilisticType> {
zone: UncertainInflowZone<R>,
stream: Vec<MaybeUncertain<R>>,
}
impl<R: ProbabilisticType + Copy> InflowContext<R> {
pub fn new(zone: UncertainInflowZone<R>, stream: Vec<MaybeUncertain<R>>) -> Self {
Self { zone, stream }
}
}
pub type InflowProcess<'m, const D: usize, R> =
PropagatingProcess<R, InflowMarchState<'m, D, R>, InflowContext<R>>;
fn error_process<'m, const D: usize, R: DecNsScalar + ProbabilisticType>(
state: InflowMarchState<'m, D, R>,
context: Option<InflowContext<R>>,
message: &str,
) -> InflowProcess<'m, D, R> {
PropagatingProcess::new(
Err(CausalityError::new(CausalityErrorEnum::Custom(
message.to_string(),
))),
state,
context,
EffectLog::new(),
)
}
pub fn inflow_march_step<'m, const D: usize, R>(
_incoming: CausalEffect<R>,
state: InflowMarchState<'m, D, R>,
context: Option<InflowContext<R>>,
) -> InflowProcess<'m, D, R>
where
R: DecNsScalar + ProbabilisticType,
{
let InflowMarchState {
solver,
field,
mut last_good,
step,
mut in_dropout,
} = state;
let Some(context) = context else {
let state = InflowMarchState {
solver,
field,
last_good,
step,
in_dropout,
};
return error_process(state, None, "uncertain inflow: missing InflowContext");
};
let Some(solver) = solver else {
let state = InflowMarchState {
solver: None,
field,
last_good,
step,
in_dropout,
};
return error_process(
state,
Some(context),
"uncertain inflow: solver was consumed by a prior failure",
);
};
if step >= context.stream.len() {
let state = InflowMarchState {
solver: Some(solver),
field,
last_good,
step,
in_dropout,
};
return error_process(
state,
Some(context),
"uncertain inflow: sensor stream exhausted before the step horizon",
);
}
let zone = context.zone;
let mut logs = EffectLog::new();
let source = zone.source();
let (inflow, dropout) = match source.resolve(&context.stream[step], &mut last_good) {
Ok(resolved) => resolved,
Err(e) => {
let state = InflowMarchState {
solver: Some(solver),
field,
last_good,
step,
in_dropout,
};
return error_process(
state,
Some(context),
&format!("uncertain inflow: sample resolution failed: {e}"),
);
}
};
source.record(&mut logs, step, dropout, in_dropout, last_good);
in_dropout = dropout;
let mut velocity = [R::zero(); D];
velocity[zone.flow_axis()] = inflow;
let solver = match solver.with_moving_wall(zone.wall_axis(), zone.max_side(), velocity) {
Ok(solver) => solver,
Err(e) => {
let state = InflowMarchState {
solver: None,
field,
last_good,
step,
in_dropout,
};
return error_process(
state,
Some(context),
&format!("uncertain inflow: boundary reconfiguration rejected: {e}"),
);
}
};
let advanced = match solver.step(&field) {
Ok(output) => output.into_state(),
Err(e) => {
let state = InflowMarchState {
solver: Some(solver),
field,
last_good,
step,
in_dropout,
};
return error_process(
state,
Some(context),
&format!("uncertain inflow: march step failed: {e}"),
);
}
};
let next = InflowMarchState {
solver: Some(solver),
field: advanced,
last_good,
step: step + 1,
in_dropout,
};
let process = PropagatingProcess::new(
Ok(if dropout {
CausalEffect::none()
} else {
CausalEffect::value(inflow)
}),
next,
Some(context),
logs,
);
if dropout {
process.alternate_value(inflow)
} else {
process
}
}
pub fn march_inflow<'m, const D: usize, R>(
solver: DecNsSolver<'m, D, R>,
field: SolenoidalField<R>,
zone: UncertainInflowZone<R>,
stream: Vec<MaybeUncertain<R>>,
steps: usize,
) -> Result<InflowProcess<'m, D, R>, PhysicsError>
where
R: DecNsScalar + ProbabilisticType,
{
if zone.flow_axis() >= D {
return Err(PhysicsError::DimensionMismatch(format!(
"march_inflow: flow axis {} out of range for D = {D}",
zone.flow_axis()
)));
}
if stream.len() < steps {
return Err(PhysicsError::DimensionMismatch(format!(
"march_inflow: sensor stream has {} samples but the horizon is {steps} steps",
stream.len()
)));
}
let mut probe = [R::zero(); D];
probe[zone.flow_axis()] = zone.default_inflow();
let solver = solver.with_moving_wall(zone.wall_axis(), zone.max_side(), probe)?;
let initial = InflowMarchState::new(solver, field, zone.default_inflow());
let context = InflowContext::new(zone, stream);
let seed = PropagatingProcess::new(
Ok(CausalEffect::value(zone.default_inflow())),
initial,
Some(context),
EffectLog::new(),
);
let flow = CausalFlow::from(seed).iterate_n(steps, |flow| flow.bind(inflow_march_step));
Ok(flow.into_process())
}