embedded3 S2-2b-ii: DeviceCg::refresh (fine operator per step, hierarchy every K steps, z zeroed before the scatter) + gate test; the flag driver's correctors 3 / inner 1e-3 (residual remedy measured 7.6e-9 on the moving circle); residual study test
CI / Test (ubuntu-latest) (push) Blocked by required conditions
CI / Test (macos-latest) (push) Blocked by required conditions
CI / Build (macos-latest) (push) Waiting to run
CI / Python Bindings (maturin) (macos-latest) (push) Blocked by required conditions
CI / Python Bindings (maturin) (ubuntu-latest) (push) Blocked by required conditions
CI / CI Success (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 / Format Check (push) Failing after 4s
CI / Build (ubuntu-latest) (push) Failing after 4s
Documentation / Build API Documentation (push) Failing after 6s
Performance Benchmarks / Run Benchmarks (push) Failing after 7s
Documentation / Build User Guide (push) Successful in 6s
CI / Build CPU-Only (Explicit) (push) Failing after 1m8s
CI / Clippy Check (push) Failing after 1m30s

Co-Authored-By: Claude Fable 5.1 <[email protected]>
This commit is contained in:
Omar Sobh
2026-09-17 21:15:02 -05:00
co-authored by Claude Fable 5.1
parent fc8d69af29
commit 33de5d0080
6 changed files with 178 additions and 3 deletions
@@ -99,7 +99,9 @@ fn march(tight: bool, steps: usize, bound: Option<f64>) {
let sp = scale(&fh.p).max(1000.0);
let t = device.timers().expect("timers");
println!(
" moving circle 152²×4 CutCell (tight {tight}): {steps} steps; host vs device max |Δu| {du:.3e} on {su:.3e}, max |Δp| {dp:.3e} on {sp:.3e}; fresh cells host {fresh_h} device {fresh_d}; corrector counts differ on {differ} steps; {:.1} ms per step (host + device), device step {:.1} ms of which rebuild {:.1} ms",
" moving circle 152²×4 CutCell (tight {tight}, hierarchy every {} steps): {steps} steps; host vs device max |Δu| {du:.3e} on {su:.3e}, max |Δp| {dp:.3e} on {sp:.3e}; fresh cells host {fresh_h} device {fresh_d}; corrector counts differ on {differ} steps; device CG {:.1}/step; {:.1} ms per step (host + device), device step {:.1} ms of which rebuild {:.1} ms",
std::env::var("RTX_E3_PRECOND_REFRESH").unwrap_or_else(|_| "10".into()),
t.cg_iterations as f64 / steps as f64,
1e3 * seconds / steps as f64,
1e-6 * (t.predictor_ns + t.poisson_ns + t.apply_ns + t.transfer_ns) as f64 / steps as f64,
1e-6 * t.transfer_ns as f64 / steps as f64
@@ -116,3 +118,16 @@ fn moving_circle_host_equals_device() {
march(false, 100, None);
march(true, 100, Some(1e-9));
}
/// S2-2b-ii: the hierarchy kept for ten steps (the fine operator exact
/// every step) — the tight identity to the host still holds (the CG
/// converges to the tolerance under any preconditioner), the CG count
/// and the step time recorded against the every-step rebuild.
#[test]
fn stale_hierarchy_keeps_the_identity() {
unsafe { std::env::set_var("RTX_E3_PRECOND_REFRESH", "1") };
march(true, 60, Some(1e-9));
unsafe { std::env::set_var("RTX_E3_PRECOND_REFRESH", "10") };
march(true, 60, Some(1e-9));
unsafe { std::env::remove_var("RTX_E3_PRECOND_REFRESH") };
}
@@ -249,3 +249,68 @@ fn operator_export_profile_at_ny_62() {
problem.links.len()
);
}
/// The residual clause's remedy, measured on the moving circle (host):
/// the worst mass residual over 100 steps with two correctors at the
/// 1e-2 inner stop against three correctors at 1e-3.
#[test]
#[ignore = "residual study on the moving circle (host, a minute)"]
fn moving_circle_residual_remedy() {
use rtx_cfd::solvers::incompressible::ConvectionScheme;
use rtx_cfd::solvers::incompressible::embedded3::{
Field, Fluid, Parameters, Solver, WallScheme,
};
let n = 152;
let h = 1.0 / n as f64;
let dt = 3.24e-4;
for (correctors, inner) in [(2usize, 1e-2), (3, 1e-3), (4, 1e-4)] {
let mut solver = Solver::new(
Fluid {
density: 1000.0,
viscosity: 1.0,
reference_velocity: 1.0,
reference_length: 0.1,
},
Parameters {
corrector_steps: correctors,
tolerance: 1e-8,
inner_stop_factor: inner,
convection_scheme: ConvectionScheme::Upwind,
wall_scheme: WallScheme::CutCell,
boundaries: Boundaries {
z0: Side::Periodic,
z1: Side::Periodic,
..Boundaries::default()
},
max_surface_speed: Some(1.0),
..Parameters::default()
},
);
solver.set_boundary_velocity(|_, _, _, _| (0.0, 0.0, 0.0));
let yc = |t: f64| 0.5 + 0.08 * (t / 0.08).sin();
let vc = |t: f64| (t / 0.08).cos();
solver.set_moving_body(
Body::from_sdf(move |x, y, _z, t| {
((x - 0.5_f64).powi(2) + (y - yc(t)).powi(2)).sqrt() - 0.05
})
.with_surface_velocity(move |_, _, _, t| (0.0, vc(t), 0.0)),
);
let g = Grid::cubic(n, n, 4, h);
let mut f = Field::new(g);
solver.initialize(&mut f);
let (mut worst, mut sum_cg, mut sum_corr) = (0.0_f64, 0usize, 0usize);
let start = std::time::Instant::now();
for _ in 0..100 {
let r = solver.advance(&mut f, dt);
worst = worst.max(r.final_residual);
sum_cg += r.poisson_iterations;
sum_corr += r.corrector_steps_performed;
}
println!(
" correctors {correctors} inner {inner:.0e}: worst residual {worst:.2e}, CG {:.1}/step, correctors {:.2}/step, {:.2} s",
sum_cg as f64 / 100.0,
sum_corr as f64 / 100.0,
start.elapsed().as_secs_f64()
);
}
}
@@ -141,7 +141,12 @@ fn flag_wake_on_the_device() {
reference_length: 2.0 * R_CYL,
},
Parameters {
corrector_steps: 2,
// Three correctors at a 1e-3 inner stop hold the moving cut
// wall's mass residual under 1e-8 (the moving circle: 7.6e-9
// against 1.5e-6 with two at 1e-2); `RTX_E3_FLAG_CORRECTORS`
// overrides for the comparison runs.
corrector_steps: env_f("RTX_E3_FLAG_CORRECTORS", 3.0) as usize,
inner_stop_factor: env_f("RTX_E3_FLAG_INNER", 1e-3),
tolerance: 1e-8,
convection_scheme: ConvectionScheme::TvdVanAlbada,
wall_scheme: WallScheme::CutCell,