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 / 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
CI / Build (macos-latest) (push) Canceled after 0s
CI / WASM Build + Size Check (push) Canceled after 0s
CI / Distributed Training Tests (push) Canceled after 0s
CI / CI Success (push) Canceled after 0s
CI / Test (macos-latest) (push) Canceled after 0s
CI / Test (ubuntu-latest) (push) Canceled after 0s
CI / Python Bindings (maturin) (macos-latest) (push) Canceled after 0s
CI / Python Bindings (maturin) (ubuntu-latest) (push) Canceled after 0s

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
@@ -288,6 +288,25 @@ impl Mask {
/// without it (slip or periodic sides) the z faces carry nothing.
#[allow(clippy::too_many_arguments)]
pub fn control_volume_force_with_walls(
&self,
f: &Field,
dt: f64,
rho: f64,
mu: f64,
source: Option<&dyn Fn(f64, f64, f64) -> (f64, f64, f64)>,
bx: (usize, usize, usize, usize, usize, usize),
no_slip_z: bool,
) -> [f64; 3] {
self.control_volume_force_moving(f, dt, rho, mu, source, bx, no_slip_z, None)
}
/// The box route on a moving body: the unsteady term is the change of
/// the momentum `ρ V u` over the step with the previous step's cell
/// volumes `vol_old` (`Solver::previous_volumes`), so the fluid the
/// wall sweeps counts (`Σ ρ V (u − uⁿ)/dt` misses `ρ (V − Vⁿ) uⁿ/dt`).
/// `None` falls back to the fixed-volume form.
#[allow(clippy::too_many_arguments)]
pub fn control_volume_force_moving(
&self,
f: &Field,
dt: f64,
@@ -296,6 +315,7 @@ impl Mask {
source: Option<&dyn Fn(f64, f64, f64) -> (f64, f64, f64)>,
(i0, i1, j0, j1, k0, k1): (usize, usize, usize, usize, usize, usize),
no_slip_z: bool,
vol_old: Option<&[f64]>,
) -> [f64; 3] {
let g = self.grid();
let (nx, ny, nz, dx, dy, dz) = (g.nx, g.ny, g.nz, g.dx, g.dy, g.dz);
@@ -427,19 +447,28 @@ impl Mask {
for j in j0..j1 {
for i in i0..i1 {
let idx = g.cell(k, j, i);
if !self.is_fluid_cell(idx) {
let v_new = if self.is_fluid_cell(idx) {
self.vol(idx)
} else {
0.0
};
let v_old = vol_old.map_or(v_new, |vo| vo[idx]);
if v_new == 0.0 && v_old == 0.0 {
continue;
}
let dv = dv * self.vol(idx);
let (fu0, fu1) = (g.uface(k, j, i), g.uface(k, j, i + 1));
let (fv0, fv1) = (g.vface(k, j, i), g.vface(k, j + 1, i));
let (fw0, fw1) = (g.wface(k, j, i), g.wface(k + 1, j, i));
let du = 0.5 * ((u[fu0] - f.u_old[fu0]) + (u[fu1] - f.u_old[fu1]));
let dvv = 0.5 * ((v[fv0] - f.v_old[fv0]) + (v[fv1] - f.v_old[fv1]));
let dw = 0.5 * ((w[fw0] - f.w_old[fw0]) + (w[fw1] - f.w_old[fw1]));
force[0] -= rho * du / dt * dv;
force[1] -= rho * dvv / dt * dv;
force[2] -= rho * dw / dt * dv;
let cen = |a: &[f64], b: &[f64], f0: usize, f1: usize| {
(0.5 * (a[f0] + a[f1]), 0.5 * (b[f0] + b[f1]))
};
let (un, uo) = cen(u, &f.u_old, fu0, fu1);
let (vn, vo) = cen(v, &f.v_old, fv0, fv1);
let (wn, wo) = cen(w, &f.w_old, fw0, fw1);
force[0] -= rho * (v_new * un - v_old * uo) / dt * dv;
force[1] -= rho * (v_new * vn - v_old * vo) / dt * dv;
force[2] -= rho * (v_new * wn - v_old * wo) / dt * dv;
let dv = dv * v_new;
if let Some(s) = source {
let (sx, sy, sz) = s(
(i as f64 + 0.5) * dx,