Files
rustytorch/crates/specialized/rtx-cfd/tests/overset_mms.rs
T
Omar SobhandClaude Fable 5.1 afd1bff6ee
CI / Test (macos-latest) (push) Canceled after 0s
Performance Benchmarks / Run Benchmarks (push) Canceled after 0s
CI / Format Check (push) Canceled after 0s
CI / Clippy Check (push) Canceled after 0s
CI / Build (macos-latest) (push) Canceled after 0s
CI / Build (ubuntu-latest) (push) Canceled after 0s
CI / Test (ubuntu-latest) (push) Canceled after 0s
CI / Build CPU-Only (Explicit) (push) Canceled after 0s
Documentation / Build User Guide (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
Documentation / Build API Documentation (push) Canceled after 0s
rtx-cfd: overset A-P2 — the patch overlaps the background (OversetPisoSolver), gated S1–S5
Background = the embedded solver with a mask from the overlap classification
(embedded/{mod,projection}.rs: module split, projection's solve/apply halves,
set_overlap, fringe p' Dirichlet by elimination into extra_diag/rhs, anchor
dropped, set_inner_stop_factor, phase API begin_step/solve_correction/
apply_correction/end_step; advance rebuilt on the phases — every suite digit-
identical, FSI2 default line-for-line). Patch = the curvilinear solver with an
acceptor ring (set_side_velocity; set_acceptor_ring/stamp_acceptors/
set_acceptor_correction; acceptor Dirichlet by elimination into
PressureSystem.links so the BiCGSTAB stop stays in flux units — identity rows
measured unconverged at 2431 iterations; same phase API). overset/overlap.rs:
OverlapMap — hole/fringe/active from the patch's own indices (hole = body or
k <= nn-1-overlap_rows, DEFAULT_OVERLAP_ROWS = 4 from the 2.9 h depth budget),
dual-quad inverse-bilinear donors patch→fringe, lattice donors →acceptors,
both invariants asserted, mass-defect measures. overset/mod.rs:
OversetPisoSolver — advance (exchange rebuilt BEFORE the predictors from the
previous corrected field), alternating Schwarz on the acceptor p' vector with
Anderson(3) (plain Schwarz measured 0.82/round: floating patch, Neumann wall)
and the previous step's vector as warm start (1 round/corrector at steady
state), stop relative to the STEP's p' scale (the MG absolute stop is
1e-9/dt² in pressure — the whole second correction), set_patch_mesh,
snapshot/restore carrying the warm-start vector.

Gates: overlap linear-exact 1e-13, quadratic orders 1.96/1.99 (acceptors),
1.40/1.91 (fringe); half-couplings: patch with exact acceptors Stokes 2.07/1.98
+ 2.08/1.98, upwind 0.84/0.84, background with exact fringe 7.86e-3/2.90e-3/
1.09e-3 (1.44/1.41); two-mesh MMS n=32/64: background 8.717e-3/4.207e-3 (1.03x/
0.97x the embedded circle), patch 1.322e-2/6.904e-3 (1.5-1.6x), orders 1.05/
0.94, patch div <= 5e-13, overlap mass defect 3.6e-3 -> 8.2e-4 of the overlap
flux (under the registered 1e-3 from n=64; disclosed at 32); motion: stationary
patch through set_patch_mesh bit-identical, snapshot/restore with a pending mesh
bit-identical, translating phantom circle 1.22x/1.19x the static level over
4.5 cells. Inherited, disclosed: poisson_equivalence's no-body multigrid pin
fails by 3.9e-9 at d46fb0b (M1's commit; verified in a clean worktree).

Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01X2GmJXeQ2njUecEKiJZ1G2
2026-09-04 19:24:13 -07:00

89 lines
3.3 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! A-P2, step S4 (`docs/overset_metal_campaign.md` §5.9): the two-mesh
//! manufactured solution on the static overset.
//!
//! Unit square (closed box, exact Dirichlet velocity on every side, the
//! `mms_piso.rs` field and source, upwind) with the phantom circle at
//! (0.6, 0.45) r = 0.2 as the patch's inner wall (the exact velocity as its
//! Dirichlet), the skewed stretched annulus to r = 0.354 as the patch
//! (outer spacing = h), background n = 32 / 64 / 128. Gates: the
//! background's L2 on its fluid faces within 1.5× the embedded-circle
//! ladder (8.489e-3 / 4.341e-3 at n = 32 / 64) and first order to 128; the
//! patch's L2 on its interior cells ≤ 2× the background's at equal h and
//! first order; both divergence-free on their equation-carrying cells;
//! Schwarz rounds per corrector recorded; the overlap mass defect per step,
//! both sides, relative to the overlap flux scale — measured first.
mod overset_common;
use overset_common::{march, orders};
use rtx_cfd::CfdResult;
#[tokio::test]
async fn two_mesh_mms_recovers_the_single_mesh_levels_on_both_meshes() -> CfdResult<()> {
let reference = [8.489e-3, 4.341e-3];
let only: Option<usize> = std::env::var("RTX_OVERSET_N")
.ok()
.and_then(|v| v.parse().ok());
let tol: f64 = std::env::var("RTX_OVERSET_SCHWARZ")
.ok()
.and_then(|v| v.parse().ok())
.unwrap_or(1e-3);
let mut bg = Vec::new();
let mut patch = Vec::new();
for (idx, n) in [32usize, 64, 128].into_iter().enumerate() {
if only.is_some_and(|o| o != n) {
continue;
}
let m = march(n, tol).await?;
println!(
" overset n={n}: L2 background {:.6e} patch {:.6e} ({} steps); Schwarz rounds mean {:.2} max {} (failures {}); \
worst bg residual {:.2e}, patch div {:.2e}; overlap mass defect (of the overlap flux) at steady state bg {:.3e} patch {:.3e}, worst over the transient bg {:.3e} patch {:.3e}",
m.l2_background,
m.l2_patch,
m.steps,
m.mean_rounds,
m.max_rounds,
m.schwarz_failures,
m.worst_bg_residual,
m.worst_patch_div_rel,
m.final_defect_bg_rel,
m.final_defect_patch_rel,
m.worst_defect_bg_rel,
m.worst_defect_patch_rel
);
if let Some(r) = reference.get(idx) {
assert!(
m.l2_background < 1.5 * r,
"n = {n}: background L2 {:.3e} > 1.5x embedded circle {r:.3e}",
m.l2_background
);
}
assert!(
m.l2_patch < 2.0 * m.l2_background,
"n = {n}: patch L2 {:.3e} > 2x background {:.3e}",
m.l2_patch,
m.l2_background
);
assert!(
m.worst_patch_div_rel < 1e-9,
"patch divergence {:.3e}",
m.worst_patch_div_rel
);
bg.push(m.l2_background);
patch.push(m.l2_patch);
}
if only.is_none() {
let (ob, op) = (orders(&bg), orders(&patch));
println!(" overset orders: background {ob:?}, patch {op:?}");
assert!(
ob.iter().all(|&x| (0.75..1.3).contains(&x)),
"background orders {ob:?}"
);
assert!(
op.iter().all(|&x| (0.7..1.6).contains(&x)),
"patch orders {op:?}"
);
}
Ok(())
}