R8-f: the operator-route wall load on the device (RTX_E3_LOADS_DEVICE=1, default off)

e3_loads.cu evaluates every summand of Mask::cut_wall_force (cells' p W,
fluid faces' implicit wall shear) and cut_wall_exchange_parts (diffusive and
convective exchange with prescribed neighbours) on the device from the
predictor set of the device mask, in the host's expression order with FMA
off; the summands are appended as records, downloaded once per pass, sorted
into the host's loop order and summed sequentially on the host, so the
totals are the host route's to the bit. Records replay into the R8-a load
sink (exact zeros dropped; sum-invariant) and into R8-c's WallLoad shape.

DeviceStep::cut_wall_loads_device / cut_wall_force_device /
check_wall_loads (the G1 instrument, RTX_E3_LOADS_CHECK=1 in the flag test).
The flag test and the R8-a harness take the device route under the knob;
default paths untouched. cut.rs: E3CutPtrs and DeviceCut::ptrs pub(super).

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
Omar Sobh
2026-09-25 22:00:42 -05:00
co-authored by Claude Opus 5.5
parent 171da41ed1
commit 2d5ffbabdd
6 changed files with 959 additions and 10 deletions
@@ -20,7 +20,9 @@ use std::sync::{Arc, Mutex, RwLock};
use rtx_cfd::solvers::incompressible::ConvectionScheme;
use rtx_cfd::solvers::incompressible::embedded3::exchange::set_load_sink;
use rtx_cfd::solvers::incompressible::embedded3::step::device::{DeviceSnapshot, DeviceStep};
use rtx_cfd::solvers::incompressible::embedded3::step::device::{
DeviceSnapshot, DeviceStep, loads_device_enabled,
};
use rtx_cfd::solvers::incompressible::embedded3::{
Body, Boundaries, DeviceSdf, Field, Fluid, Grid, Parameters, Side, Solver, StepResult,
WallScheme,
@@ -334,7 +336,24 @@ impl E3Fluid {
// flow; the whole span by default.
let nz = self.grid.nz;
let n = (super::env_f("RTX_E3FSI_LOAD_PLANES", 0.0) as usize).min(nz);
let f = if n > 0 && n < nz {
// R8-f (`RTX_E3_LOADS_DEVICE=1`): the same route on the device, its
// summands replayed into the sink in the host's order (exact zeros
// dropped); the host route when the device cannot serve it.
let planes = (n > 0 && n < nz).then(|| ((nz - n) / 2, (nz - n) / 2 + n));
let on_device = if loads_device_enabled() {
self.device.cut_wall_force_device(RHO * NU, planes)
} else {
None
};
let f = if let Some(f) = on_device {
if let Some((k0, k1)) = planes {
self.load_width = (k1 - k0) as f64 * self.h;
f
} else {
self.load_width = self.width;
[f[0] / self.width, f[1] / self.width, f[2] / self.width]
}
} else if n > 0 && n < nz {
let k0 = (nz - n) / 2;
self.load_width = n as f64 * self.h;
mask.cut_wall_force_per_span(body, &self.field, RHO * NU, t, (k0, k0 + n))