embedded3: the cut mask's surface velocity uses the interpolant's distance and normal (one body call per face instead of eight); the device shares the surface-velocity tables between a step's phases; sphere gate re-read 0.965/0.961
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
Performance Benchmarks / Run Benchmarks (push) Failing after 3s
CI / Clippy Check (push) Failing after 3s
CI / Build (ubuntu-latest) (push) Failing after 3s
CI / Format Check (push) Failing after 5s
Documentation / Build User Guide (push) Successful in 8s
CI / Build CPU-Only (Explicit) (push) Failing after 55s
Documentation / Build API Documentation (push) Failing after 57s

Co-Authored-By: Claude Fable 5.1 <[email protected]>
This commit is contained in:
Omar Sobh
2026-09-17 19:48:15 -05:00
co-authored by Claude Fable 5.1
parent 11d856b3d4
commit 58a5501cef
2 changed files with 89 additions and 11 deletions
@@ -361,16 +361,73 @@ impl Mask {
}
/// The surface velocity component `c` at the foot of the normal from
/// the face centre.
/// the face centre `x`. With a cut geometry the signed distance and
/// the normal come from the geometry's own corner values (the
/// trilinear interpolant at the face centre, its gradient by central
/// differences of the neighbouring face centres) — one call of the
/// body's velocity per face instead of eight of its distance.
pub fn surface_velocity_at(&self, body: &Body, x: [f64; 3], c: usize, t: f64) -> f64 {
let g = self.grid;
let eps = 1e-6 * g.dx.min(g.dy).min(g.dz);
let s = body.phi(x[0], x[1], x[2], t);
let (n1, n2, n3) = body.normal(x[0], x[1], x[2], t, eps);
let v = body.surface_velocity(x[0] - s * n1, x[1] - s * n2, x[2] - s * n3, t);
let (s, n) = match self.cut.as_ref() {
Some(cut) => {
let (s, n) = self.interpolant_distance_and_normal(cut, x);
(s, n)
}
None => {
let eps = 1e-6 * g.dx.min(g.dy).min(g.dz);
let s = body.phi(x[0], x[1], x[2], t);
let (n1, n2, n3) = body.normal(x[0], x[1], x[2], t, eps);
(s, [n1, n2, n3])
}
};
let v = body.surface_velocity(x[0] - s * n[0], x[1] - s * n[1], x[2] - s * n[2], t);
[v.0, v.1, v.2][c]
}
/// φ and its unit gradient at `x` from the trilinear interpolant of the
/// corner values (the cut geometry's own surface).
fn interpolant_distance_and_normal(&self, cut: &CutGeometry, x: [f64; 3]) -> (f64, [f64; 3]) {
let g = self.grid;
let (nx, ny, nz) = (g.nx as i64, g.ny as i64, g.nz as i64);
let h = [g.dx, g.dy, g.dz];
let node = |i: i64, j: i64, k: i64| -> f64 {
let i = i.clamp(0, nx);
let j = j.clamp(0, ny);
let k = k.clamp(0, nz);
cut.phi[((k * (ny + 1) + j) * (nx + 1) + i) as usize]
};
let gx = x[0] / h[0];
let gy = x[1] / h[1];
let gz = x[2] / h[2];
let (i0, j0, k0) = (gx.floor() as i64, gy.floor() as i64, gz.floor() as i64);
let (fx, fy, fz) = (gx - i0 as f64, gy - j0 as f64, gz - k0 as f64);
// Trilinear value and its partial derivatives.
let c = |di: i64, dj: i64, dk: i64| node(i0 + di, j0 + dj, k0 + dk);
let lerp = |a: f64, b: f64, f: f64| a + f * (b - a);
let c00 = lerp(c(0, 0, 0), c(1, 0, 0), fx);
let c10 = lerp(c(0, 1, 0), c(1, 1, 0), fx);
let c01 = lerp(c(0, 0, 1), c(1, 0, 1), fx);
let c11 = lerp(c(0, 1, 1), c(1, 1, 1), fx);
let c0 = lerp(c00, c10, fy);
let c1 = lerp(c01, c11, fy);
let s = lerp(c0, c1, fz);
let dx0 = lerp(c(1, 0, 0) - c(0, 0, 0), c(1, 1, 0) - c(0, 1, 0), fy);
let dx1 = lerp(c(1, 0, 1) - c(0, 0, 1), c(1, 1, 1) - c(0, 1, 1), fy);
let dphi_dx = lerp(dx0, dx1, fz) / h[0];
let dy0 = lerp(c(0, 1, 0) - c(0, 0, 0), c(1, 1, 0) - c(1, 0, 0), fx);
let dy1 = lerp(c(0, 1, 1) - c(0, 0, 1), c(1, 1, 1) - c(1, 0, 1), fx);
let dphi_dy = lerp(dy0, dy1, fz) / h[1];
let dz0 = lerp(c(0, 0, 1) - c(0, 0, 0), c(1, 0, 1) - c(1, 0, 0), fx);
let dz1 = lerp(c(0, 1, 1) - c(0, 1, 0), c(1, 1, 1) - c(1, 1, 0), fx);
let dphi_dz = lerp(dz0, dz1, fy) / h[2];
let norm = (dphi_dx * dphi_dx + dphi_dy * dphi_dy + dphi_dz * dphi_dz).sqrt();
if norm > 0.0 {
(s, [dphi_dx / norm, dphi_dy / norm, dphi_dz / norm])
} else {
(s, [1.0, 0.0, 0.0])
}
}
/// The volume fluxes of the surface velocity through every cell's wall
/// into the body, `U_b·W_c` (zero for a body at rest; the porous
/// manufactured surface's flux otherwise), made compatible: the net