From 0cf9d20afb30a66c9c2bad07d067d95b54582a65 Mon Sep 17 00:00:00 2001 From: Omar Sobh Date: Sat, 19 Sep 2026 20:38:29 -0500 Subject: [PATCH] =?UTF-8?q?embedded3=20A1-b:=20the=20tiled=20momentum=20vo?= =?UTF-8?q?lume=20as=20a=20host=20prototype=20(RTX=5FE3=5FMOMENTUM=5FVOLUM?= =?UTF-8?q?E=3Dtiled;=20pressure=20force,=20source=20and=20inertia=20on=20?= =?UTF-8?q?=C2=BD(V=E2=88=92+V+)hA;=20default=20off;=20the=20device=20refu?= =?UTF-8?q?ses=20it)=20=E2=80=94=20REFUTED=20on=20the=20oblique=20instrume?= =?UTF-8?q?nt=20(level=20moves,=20the=20h-independent=20remainder=20and=20?= =?UTF-8?q?the=20near-wall=20pressure=20residual=20do=20not)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Fable 5.1 --- .../embedded3/step/cut_predictor.rs | 22 ++++++++++++++----- .../incompressible/embedded3/step/device.rs | 4 ++++ .../incompressible/embedded3/step/mod.rs | 10 +++++++++ 3 files changed, 31 insertions(+), 5 deletions(-) diff --git a/crates/specialized/rtx-cfd/src/solvers/incompressible/embedded3/step/cut_predictor.rs b/crates/specialized/rtx-cfd/src/solvers/incompressible/embedded3/step/cut_predictor.rs index d3aaaa0..ad10418 100644 --- a/crates/specialized/rtx-cfd/src/solvers/incompressible/embedded3/step/cut_predictor.rs +++ b/crates/specialized/rtx-cfd/src/solvers/incompressible/embedded3/step/cut_predictor.rs @@ -344,8 +344,20 @@ impl Solver { let p_plus = lat.cell(cell_plus).map_or(0.0, |ci| field.p[ci]); let p_minus = lat.cell(cell_minus).map_or(0.0, |ci| field.p[ci]); let idx_f = lat.face(c, p).expect("the face"); - let pressure = -(p_plus - p_minus) * cv.alpha * area[c] * mask.grad_weight(c, idx_f); - let v_u = cv.alpha * h[c] * area[c]; + // A1-b: the control volume the pressure force, the source and the + // inertia act on — the aperture prism (default) or the tiled + // half-cells `½(V_− + V_+)` the convective / diffusive sides use. + let cell_mean = || { + let vol = |q: [i64; 3]| lat.cell(q).map_or(cv.alpha, |ci| mask.vol(ci)); + 0.5 * (vol(cell_minus) + vol(cell_plus)) + }; + let alpha_p = if self.params.momentum_volume_tiled { + cell_mean() + } else { + cv.alpha + }; + let pressure = -(p_plus - p_minus) * alpha_p * area[c] * mask.grad_weight(c, idx_f); + let v_u = alpha_p * h[c] * area[c]; let source = self.momentum_source.as_ref().map_or(0.0, |f| { let s = f(x[0], x[1], x[2], t_old); [s.0, s.1, s.2][c] * v_u @@ -361,9 +373,9 @@ impl Solver { let shear = mu * a_w * c1; // The explicit part of the quadratic wall gradient (order 2). let shear_explicit = nb.map_or(0.0, |f| mu * a_w * c2 * (old[c][f] - ub)); - let fraction = if self.params.momentum_volume_cell_mean { - let vol = |q: [i64; 3]| lat.cell(q).map_or(cv.alpha, |ci| mask.vol(ci)); - 0.5 * (vol(cell_minus) + vol(cell_plus)) + let fraction = if self.params.momentum_volume_cell_mean || self.params.momentum_volume_tiled + { + cell_mean() } else { cv.alpha }; diff --git a/crates/specialized/rtx-cfd/src/solvers/incompressible/embedded3/step/device.rs b/crates/specialized/rtx-cfd/src/solvers/incompressible/embedded3/step/device.rs index 74713c0..83e4ddf 100644 --- a/crates/specialized/rtx-cfd/src/solvers/incompressible/embedded3/step/device.rs +++ b/crates/specialized/rtx-cfd/src/solvers/incompressible/embedded3/step/device.rs @@ -169,6 +169,10 @@ impl DeviceStep { !solver.params.pressure_centroid, "the centroid pressure gradient (S2-5) is a host prototype: the device kernels do not carry it" ); + assert!( + !solver.params.momentum_volume_tiled, + "the tiled momentum volume (A1-b) is a host prototype: the device kernels do not carry it" + ); let rt = runtime(); let nu = (grid.nx + 1) * grid.ny * grid.nz; let nv = grid.nx * (grid.ny + 1) * grid.nz; diff --git a/crates/specialized/rtx-cfd/src/solvers/incompressible/embedded3/step/mod.rs b/crates/specialized/rtx-cfd/src/solvers/incompressible/embedded3/step/mod.rs index 42f152c..fee560f 100644 --- a/crates/specialized/rtx-cfd/src/solvers/incompressible/embedded3/step/mod.rs +++ b/crates/specialized/rtx-cfd/src/solvers/incompressible/embedded3/step/mod.rs @@ -139,6 +139,14 @@ pub struct Parameters { /// correction and the predictor's pressure force. The device path /// refuses it. `RTX_E3_PRESSURE_CENTROID=1`. pub pressure_centroid: bool, + /// HOST PROTOTYPE (A1-b, 2026-09-19): the pressure force, the momentum + /// source and the inertia of a face's control volume taken on the + /// TILED volume `½(V_− + V_+) h A` (the cell means the convective and + /// diffusive sides already use) instead of the aperture prism + /// `α h A`. On an oblique cut the two differ by O(1) in the cut + /// layer — the sharpened S2-7 hypothesis. The device path refuses it. + /// `RTX_E3_MOMENTUM_VOLUME=tiled`. + pub momentum_volume_tiled: bool, /// S2-5: the cross-direction diffusion between two faces over the /// distance between their OPEN-PART CENTROIDS (a cut face's velocity /// is its open part's mean, ½h(1 − α) off the face centre along the @@ -181,6 +189,8 @@ impl Default for Parameters { .is_ok_and(|v| v == "off"), wall_exchange_axis: std::env::var("RTX_E3_WALL_EXCHANGE").is_ok_and(|v| v == "axis"), pressure_centroid: std::env::var("RTX_E3_PRESSURE_CENTROID").is_ok_and(|v| v == "1"), + momentum_volume_tiled: std::env::var("RTX_E3_MOMENTUM_VOLUME") + .is_ok_and(|v| v == "tiled"), // ON by default since S2-5 (`=0` reproduces the records before it). diffusion_centroid: std::env::var("RTX_E3_DIFFUSION_CENTROID") .map_or(true, |v| v != "0"),