embedded3 item 11: moving bodies (end-of-step mask, fresh-cell refill, space-time cut cell: step-averaged apertures, GCL wall flux, Reynolds-transport momentum), the 3D fresh-cell falsifier (plate / circle / stadium, wall + control-volume routes) and the Lipschitz sweep; ghost wall reproduces the 2D falsifier to the digit; cut wall 5–14× smoother on the circle, gates not met (fresh cell's first step); wall.rs split (impose.rs)
CI / Build (macos-latest) (push) Waiting to run
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 / 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 / Clippy Check (push) Failing after 3s
CI / Build (ubuntu-latest) (push) Failing after 4s
CI / Format Check (push) Failing after 4s
Performance Benchmarks / Run Benchmarks (push) Failing after 5s
Documentation / Build User Guide (push) Successful in 6s
CI / Build CPU-Only (Explicit) (push) Failing after 1m6s
Documentation / Build API Documentation (push) Failing after 1m9s

Co-Authored-By: Claude Fable 5.1 <[email protected]>
This commit is contained in:
Omar Sobh
2026-09-17 16:20:35 -05:00
co-authored by Claude Fable 5.1
parent 0e4c97ed24
commit 5b1621e6ad
12 changed files with 1406 additions and 419 deletions
@@ -188,9 +188,34 @@ impl Mask {
anchor,
fluid_cells,
cut: Some(cut),
step_apertures: None,
step_open: None,
})
}
/// Set the step-averaged apertures and the space-time classification
/// from the previous mask's geometry.
pub fn set_step_apertures(&mut self, old: &Mask) {
let (Some(cut), Some(old_cut)) = (self.cut.as_ref(), old.cut.as_ref()) else {
return;
};
let avg = |a: &[f64], b: &[f64]| -> Vec<f64> {
a.iter().zip(b).map(|(x, y)| 0.5 * (x + y)).collect()
};
let au = avg(&cut.a_u, &old_cut.a_u);
let av = avg(&cut.a_v, &old_cut.a_v);
let aw = avg(&cut.a_w, &old_cut.a_w);
let open = |a: &[f64]| -> Vec<bool> { a.iter().map(|&x| x > 0.0).collect() };
let active = self
.cell_fluid
.iter()
.zip(&old.cell_fluid)
.map(|(&n, &o)| n || o)
.collect();
self.step_open = Some((open(&au), open(&av), open(&aw), active));
self.step_apertures = Some((au, av, aw));
}
pub(super) fn lattice(&self) -> Lattice {
Lattice {
g: self.grid,
@@ -319,6 +344,51 @@ impl Mask {
(table, correction)
}
/// The moving rigid body's wall fluxes by the discrete geometric
/// conservation law: `(V_c^{n+1} V_c^n)/dt` per active cell (a dying
/// cell's remaining volume leaves through its step-averaged apertures),
/// the net (the cut geometry's closure defect) redistributed over the
/// wall cells by wall area.
pub fn gcl_flux_table(&self, old: &Mask, dt: f64) -> (Vec<f64>, f64) {
let mut table = vec![0.0; self.grid.cells()];
let (Some(cut), Some(old_cut)) = (self.cut.as_ref(), old.cut.as_ref()) else {
return (table, 0.0);
};
let g = self.grid;
let dv = g.dx * g.dy * g.dz;
let (mut net, mut area) = (0.0, 0.0);
for (idx, entry) in table.iter_mut().enumerate() {
if !self.cell_active(idx) {
continue;
}
*entry = (cut.vol[idx] - old_cut.vol[idx]) * dv / dt;
net += *entry;
let w = cut.wall[idx];
area += (w[0] * w[0] + w[1] * w[1] + w[2] * w[2]).sqrt();
}
if std::env::var_os("RTX_E3_DEBUG").is_some() {
let dead = (0..table.len())
.filter(|&i| !self.cell_fluid[i] && old.cell_fluid[i])
.count();
let fresh = (0..table.len())
.filter(|&i| self.cell_fluid[i] && !old.cell_fluid[i])
.count();
let (vn, vn1): (f64, f64) = (old_cut.vol.iter().sum(), cut.vol.iter().sum());
eprintln!(
" gcl: dead {dead} fresh {fresh} net {net:.3e} area {area:.3e} ΣV old {vn:.6} new {vn1:.6}{:.3e})",
vn1 - vn
);
}
let correction = if area > 0.0 { net / area } else { 0.0 };
if correction != 0.0 {
for (idx, w) in cut.wall.iter().enumerate() {
let a = (w[0] * w[0] + w[1] * w[1] + w[2] * w[2]).sqrt();
table[idx] -= correction * a;
}
}
(table, correction)
}
/// The volume flux of the surface velocity through a cell's wall into
/// the body, `U_b·W_c`, uncorrected. Zero without a cut geometry.
pub fn wall_flux(&self, body: &Body, idx: usize, t: f64) -> f64 {
@@ -348,14 +418,27 @@ impl Mask {
/// shear `Σ_f μ A_w (u_f U_b)/d_f` over the unknown faces. `None`
/// without a cut geometry.
pub fn cut_wall_force(&self, body: &Body, f: &Field, mu: f64, t: f64) -> Option<[f64; 3]> {
let (p, s) = self.cut_wall_force_parts(body, f, mu, t)?;
Some([p[0] + s[0], p[1] + s[1], p[2] + s[2]])
}
/// The cut-cell load route split into its pressure and shear parts.
pub fn cut_wall_force_parts(
&self,
body: &Body,
f: &Field,
mu: f64,
t: f64,
) -> Option<([f64; 3], [f64; 3])> {
let cut = self.cut.as_ref()?;
let g = self.grid;
let (nx, ny, nz) = (g.nx, g.ny, g.nz);
let mut pressure = [0.0; 3];
let mut force = [0.0; 3];
for (idx, w) in cut.wall.iter().enumerate() {
if self.cell_fluid[idx] {
for c in 0..3 {
force[c] += f.p[idx] * w[c];
pressure[c] += f.p[idx] * w[c];
}
}
}
@@ -395,6 +478,6 @@ impl Mask {
}
}
}
Some(force)
Some((pressure, force))
}
}