embedded3 S2-7: oblique instrument gains registration sweep / slope subset / z-flow skip / merged-cell count (RTX_E3_OBLIQUE_{C0_SHIFTS,SLOPES,ZFLOW,NS}) and the operator probe (oblique_operator_probe: predictor acceleration on the exact centroid-valued field by aperture band, cut-cell divergence under four valuations, spurious pressure; z-flow control); two host prototypes, default off, device refuses them: RTX_E3_CV_SIDES=exact (the momentum CV's side apertures from the interpolant on the half faces / cell-centre planes) and RTX_E3_WALL_ORDER2=centroid (the quadratic wall gradient's second point at the neighbour's centroid distance); quad_fraction / tri_area_fraction lifted to module fns (default path digit-identical: oblique record, host suite 21/21, device cut tests)
CI / Build CPU-Only (Explicit) (push) Failing after 4s
Documentation / Build API Documentation (push) Failing after 6s
Documentation / Build User Guide (push) Successful in 5s
CI / Format Check (push) Failing after 10s
CI / Build (ubuntu-latest) (push) Failing after 1m30s
CI / Clippy Check (push) Failing after 1m50s
Performance Benchmarks / Run Benchmarks (push) Successful in 2m15s
CI / Build (macos-latest) (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
CI / WASM Build + Size Check (push) Canceled after 0s
CI / Distributed Training Tests (push) Canceled after 0s
CI / CI Success (push) Canceled after 0s

Co-Authored-By: Claude Fable 5.1 <[email protected]>
This commit is contained in:
Omar Sobh
2026-09-19 22:26:37 -05:00
co-authored by Claude Fable 5.1
parent 0cf9d20afb
commit b80af59ca4
7 changed files with 590 additions and 40 deletions
@@ -219,6 +219,8 @@ impl Mask {
wall_advancing: false,
exchange_convection_off: false,
wall_exchange_axis: false,
cv_sides_exact: false,
wall_order2_centroid: false,
grad_weights: None,
diffusion_centroid: false,
face_shifts: None,
@@ -375,6 +377,12 @@ impl Mask {
ap[d] = [minus, plus];
}
}
// S2-7: the sides' own apertures instead of the whole-face averages.
if self.cv_sides_exact {
if let Some(exact) = self.cut.as_ref().and_then(|cut| self.exact_cv_sides(cut, c, p)) {
ap = exact;
}
}
let mut wall = [0.0; 3];
for d in 0..3 {
wall[d] = -(ap[d][1] - ap[d][0]) * area[d];
@@ -415,6 +423,86 @@ impl Mask {
}
}
/// S2-7: the control volume's side apertures from the interpolant on the
/// sides' OWN corners. An unknown face's control volume is the tile
/// between the two adjacent cells' centres: across `c` its sides are
/// two HALF faces (the far half of `cell_minus`'s face, the near half
/// of `cell_plus`'s), in the own direction the two cells' centre
/// planes. φ is linear along every edge, so the mid-edge values are
/// exact for the interpolant; each half face / centre plane is a quad
/// through the faces' own `quad_fraction`. The averages of whole-face
/// apertures the default takes are wrong by O(1) wherever the wall
/// crosses a side (the in-plane momentum residual on oblique walls).
/// `None` at a domain side (the default stays).
fn exact_cv_sides(&self, cut: &CutGeometry, c: usize, p: [i64; 3]) -> Option<[[f64; 2]; 3]> {
let lat = self.lattice();
let g = self.grid;
let mut pm = p;
pm[c] -= 1;
let cells = [g.kji(lat.cell(pm)?), g.kji(lat.cell(p)?)];
// The corner of cell (k, j, i) at unit offsets `o = [di, dj, dk]`.
let corner = |cell: (usize, usize, usize), o: [usize; 3]| {
cut.corner_phi(cell.0 + o[2], cell.1 + o[1], cell.2 + o[0])
};
// The value at a cell's corner or, with `half`, at the mid-point of
// its edge along `c` (the interpolant's mean of the two corners).
let value = |cell: (usize, usize, usize), mut o: [usize; 3], half: bool| -> f64 {
if half {
o[c] = 0;
let a = corner(cell, o);
o[c] = 1;
0.5 * (a + corner(cell, o))
} else {
corner(cell, o)
}
};
let mut ap = [[1.0; 2]; 3];
for d in 0..3 {
if d == c {
// The two cells' centre planes across `c`: corners at the
// mid-points of the cells' `c` edges.
let (d1, d2) = ((c + 1) % 3, (c + 2) % 3);
for (side, cell) in cells.iter().enumerate() {
let mid = |o1: usize, o2: usize| {
let mut o = [0; 3];
o[d1] = o1;
o[d2] = o2;
value(*cell, o, true)
};
ap[d][side] = super::cut::quad_fraction(mid(0, 0), mid(1, 0), mid(0, 1), mid(1, 1));
}
} else {
let e = 3 - c - d;
for side in 0..2 {
// cell_minus's `d` face at `side`, its half nearer the
// unknown face (c from ½ to 1); cell_plus's, c from 0 to ½.
let half = |cell: (usize, usize, usize), far: bool| -> f64 {
let at = |oc: u8, oe: usize| {
let mut o = [0; 3];
o[d] = side;
o[e] = oe;
match oc {
0 => value(cell, o, false),
1 => value(cell, o, true),
_ => {
o[c] = 1;
value(cell, o, false)
}
}
};
if far {
super::cut::quad_fraction(at(1, 0), at(2, 0), at(1, 1), at(2, 1))
} else {
super::cut::quad_fraction(at(0, 0), at(1, 0), at(0, 1), at(1, 1))
}
};
ap[d][side] = 0.5 * (half(cells[0], true) + half(cells[1], false));
}
}
}
Some(ap)
}
/// The surface velocity component `c` at the foot of the normal from
/// the face centre `x`. With a cut geometry the signed distance and
/// the normal come from the geometry's own corner values (the