embedded3: moving-body box route (Δ(Vu) unsteady term, Solver::previous_volumes), floor-source / closure-lag / applied-force instruments, moving.rs split; falsifier prints the route residual under each
CI / WASM Build + Size Check (push) Blocked by required conditions
CI / Distributed Training Tests (push) Blocked by required conditions
CI / CI Success (push) Blocked by required conditions
CI / Test (macos-latest) (push) Blocked by required conditions
CI / Test (ubuntu-latest) (push) Blocked by required conditions
CI / Python Bindings (maturin) (macos-latest) (push) Blocked by required conditions
CI / Python Bindings (maturin) (ubuntu-latest) (push) Blocked by required conditions
CI / Build (macos-latest) (push) Waiting to run
CI / Format Check (push) Failing after 4s
CI / Clippy Check (push) Failing after 4s
CI / Build (ubuntu-latest) (push) Failing after 3s
Performance Benchmarks / Run Benchmarks (push) Failing after 5s
Documentation / Build User Guide (push) Successful in 7s
CI / Build CPU-Only (Explicit) (push) Failing after 29s
Documentation / Build API Documentation (push) Failing after 31s

Co-Authored-By: Claude Fable 5.1 <[email protected]>
This commit is contained in:
Omar Sobh
2026-09-18 06:27:04 -05:00
co-authored by Claude Fable 5.1
parent 62b46194dd
commit 53b1babb91
6 changed files with 354 additions and 134 deletions
@@ -140,4 +140,74 @@ impl Mask {
}
Some(force)
}
/// The closure lag of a moving body's pressure correction: the
/// corrector applies `p'` on the STEP apertures while the operator
/// route reads the summed pressure on the END apertures, so the exact
/// discrete force carries `Σ_c p'_c (W_step,c W_end,c)` (a force on
/// the body) that the route lacks. Zero for a body at rest.
#[must_use]
pub fn closure_lag(&self, p_prime: &[f64]) -> [f64; 3] {
let g = self.grid;
let area = [g.dy * g.dz, g.dx * g.dz, g.dx * g.dy];
let mut lag = [0.0; 3];
if self.step_apertures.is_none() {
return lag;
}
for k in 0..g.nz {
for j in 0..g.ny {
for i in 0..g.nx {
let idx = g.cell(k, j, i);
if !self.cell_active(idx) {
continue;
}
let pp = p_prime[idx];
if pp == 0.0 {
continue;
}
// W_c = −Σ A_f n_f: x-part (α_e α_w) A_x, etc.
let (ue, uw) = (g.uface(k, j, i + 1), g.uface(k, j, i));
let (vn, vs) = (g.vface(k, j + 1, i), g.vface(k, j, i));
let (wt, wb) = (g.wface(k + 1, j, i), g.wface(k, j, i));
let d = [
(self.au_step(ue) - self.a_u(ue)) - (self.au_step(uw) - self.a_u(uw)),
(self.av_step(vn) - self.a_v(vn)) - (self.av_step(vs) - self.a_v(vs)),
(self.aw_step(wt) - self.a_w(wt)) - (self.aw_step(wb) - self.a_w(wb)),
];
for c in 0..3 {
lag[c] += pp * (-d[c]) * area[c];
}
}
}
}
lag
}
/// The force the discrete momentum equation actually applied over the
/// last step, read post-step: the pressure part on the end field, the
/// implicit wall shear on the PREDICTED velocities `u*` (the corrector
/// moves `u` without re-applying the shear) and the explicit wall
/// exchange on the OLD velocities. On a body at rest at a steady state
/// this equals `cut_wall_force`; on a moving body it is the number the
/// box route should reproduce.
pub fn cut_wall_force_applied(
&self,
body: &Body,
f: &Field,
mu: f64,
rho: f64,
t: f64,
) -> Option<[f64; 3]> {
let mut star = f.clone();
star.u.copy_from_slice(&f.u_star);
star.v.copy_from_slice(&f.v_star);
star.w.copy_from_slice(&f.w_star);
let (p, s) = self.cut_wall_force_parts(body, &star, mu, t)?;
let mut old = f.clone();
old.u.copy_from_slice(&f.u_old);
old.v.copy_from_slice(&f.v_old);
old.w.copy_from_slice(&f.w_old);
let x = self.cut_wall_exchange_force(body, &old, mu, rho, t, None)?;
Some([p[0] + s[0] + x[0], p[1] + s[1] + x[1], p[2] + s[2] + x[2]])
}
}