rtx-cfd: mask hysteresis — sticky cell classification against a reference mask
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 (macos-latest) (push) Canceled after 0s
CI / Test (ubuntu-latest) (push) Canceled after 0s
CI / Build CPU-Only (Explicit) (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
Documentation / Build User Guide (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 (macos-latest) (push) Canceled after 0s
CI / Test (ubuntu-latest) (push) Canceled after 0s
CI / Build CPU-Only (Explicit) (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
Documentation / Build User Guide (push) Canceled after 0s
The measured FSI3 killer is the bistable mask: one geometry (|d|
identical to 4 digits) samples two load branches (60 vs 120 kN), and
the traced s=1 death is the secant walking a 20x load cliff
(68,886 -> 1,307,938 N over a 1e-3 candidate change). A sticky band
makes the load map single-valued at the crossing: formerly-fluid
cells flip only at phi < -band, formerly-solid at phi > band,
classified against the mask held at rebuild time — in a coupling
loop, the restored committed step-start mask.
Band 0 is structurally bit-identical (phi > -0.0 <=> phi > 0.0) and
verified digit-for-digit on both committed defaults (FSI2 and FSI3,
every physics digit). Measured cost on the translating-circle MMS at
band 0.25h: +0.5% field error (u/p ratios 1.17/2.12 vs the
no-hysteresis moving levels 1.16/2.11); flip delay = band/(v dt),
deterministic. Exposed as RTX_FSI{2,3}_HYST in multiples of h_min.
Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01X2GmJXeQ2njUecEKiJZ1G2
This commit is contained in:
co-authored by
Claude Fable 5
parent
3207f2d4c6
commit
bff84ccdcc
@@ -49,6 +49,17 @@ pub struct MarchConfig {
|
||||
/// only the rare-event window (FSI2's s = 1 benchmark run accepted
|
||||
/// a worst stall of 5.4e-4 the same way and measured 0.1%).
|
||||
pub stall_accept: f64,
|
||||
/// Mask hysteresis band in multiples of the min cell size (0 = off,
|
||||
/// bit-identical). With a band, a cell within `band * h` of the
|
||||
/// interface keeps the classification of the committed step-start
|
||||
/// mask (the snapshot every pass restores), so candidate geometries
|
||||
/// within the band all see the SAME mask — the pass map stops
|
||||
/// flipping cells on sub-band candidate differences (the measured
|
||||
/// FSI3 killer: bistable load branches 60 vs 120 kN at one geometry,
|
||||
/// and a 20x load cliff over a 1e-3 candidate change). Cost: the
|
||||
/// effective wall lags the true surface by up to the band (measured
|
||||
/// on the translating-circle MMS at 0.25h: +0.5% field error).
|
||||
pub mask_hysteresis: f64,
|
||||
pub max_subiterations: usize,
|
||||
/// `"aitken"` (per-step scalar Aitken) or `"iqn"` (a persistent
|
||||
/// IQN-ILS whose secant history carries across steps).
|
||||
@@ -93,8 +104,8 @@ pub struct MarchConfig {
|
||||
}
|
||||
|
||||
impl MarchConfig {
|
||||
/// Read `RTX_{prefix}_{NY,T_RELEASE,T_END,SUBCYCLE,TOL,RTOL,MAXSUB,
|
||||
/// FLAG_NX,SMOOTH,COUPLER,REUSE,CSV}` over `defaults`.
|
||||
/// Read `RTX_{prefix}_{NY,T_RELEASE,T_END,SUBCYCLE,TOL,RTOL,STALLX,
|
||||
/// HYST,MAXSUB,FLAG_NX,SMOOTH,COUPLER,REUSE,CSV}` over `defaults`.
|
||||
pub fn from_env(prefix: &str, defaults: MarchConfig) -> MarchConfig {
|
||||
let key = |name: &str| format!("RTX_{prefix}_{name}");
|
||||
let num = |name: &str, default: f64| env_or(&key(name), default);
|
||||
@@ -107,6 +118,7 @@ impl MarchConfig {
|
||||
tol_floor: num("TOL", defaults.tol_floor),
|
||||
rtol: num("RTOL", defaults.rtol),
|
||||
stall_accept: num("STALLX", defaults.stall_accept),
|
||||
mask_hysteresis: num("HYST", defaults.mask_hysteresis),
|
||||
max_subiterations: num("MAXSUB", defaults.max_subiterations as f64) as usize,
|
||||
coupler: std::env::var(key("COUPLER")).unwrap_or(defaults.coupler),
|
||||
reuse: num("REUSE", defaults.reuse as f64) as usize,
|
||||
@@ -234,6 +246,7 @@ pub fn run_march(case: BenchmarkCase, config: &MarchConfig) -> MarchResult {
|
||||
tol_floor,
|
||||
rtol,
|
||||
stall_accept,
|
||||
mask_hysteresis,
|
||||
max_subiterations: max_subiterations_budget,
|
||||
ref coupler,
|
||||
reuse,
|
||||
@@ -247,6 +260,7 @@ pub fn run_march(case: BenchmarkCase, config: &MarchConfig) -> MarchResult {
|
||||
} = *config;
|
||||
|
||||
let (harness, mut solver, mut field) = Fsi2Harness::build_case(case, ny, flag_nx, smooth_in_h);
|
||||
solver.set_mask_hysteresis(mask_hysteresis);
|
||||
let dt_fluid = harness.dt_fluid;
|
||||
let dt = dt_fluid * subcycle as f64;
|
||||
let interface = &harness.interface;
|
||||
|
||||
Reference in New Issue
Block a user