rtx-cfd: overset P3b — the reclassification impulse located (the fringe ring is a staircase of the interpolated velocities' mass defect) and removed by a converged fringe flux balance (default on): falsifier max spike 594 → 5.50 N/m at the FSI2 step (staircase 6490), 10.95 / 16.79 at dt/2 / dt/4 (12600 / 25600), rms spike 0.07% of the force, far probe 6 (7900), KE per event 4.9e-3 J/m falling with Δt (2.6 fixed)
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
Documentation / Build API Documentation (push) Canceled after 0s
Documentation / Build User Guide (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
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
Documentation / Build API Documentation (push) Canceled after 0s
Documentation / Build User Guide (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
OverlapMap::balance_fringe_fluxes: Gauss–Seidel through the prescribed faces of every fringe cell to 1e-12 of the prescribed flux scale (≤ 50 sweeps), after every fringe stamping (3 fixed sweeps 101 N/m, 10 sweeps 5.5 — converged is the rule). OversetParameters: fringe_flux_balance (default on, RTX_OVERSET_NO_BALANCE off), fringe_balance_tolerance, refill_turned_active (measured no effect: 593.7 → 593.8; kept as the record), stall_rounds opt-in. P3b locating trace RTX_OVERSET_TRACE_SP (continuity source by class change in cell volumes/step, stored-pressure jump of turned-active cells): the flipped cells' mass source ≤ 6e-3 cell volumes/step, their stored pressure 5–10% of the range off their neighbours (4.4% on the static MMS — the meshes' discretization disagreement). Knock-outs refuted (RTX_OVERSET_H1 keep own face velocities, H4 no warm start, pressure refill): 593–597 N/m each. S4 MMS with the balance: velocity errors within 0.1% of the pinned values, the background's overlap mass defect 1e-13 by construction, pressure errors unchanged. overset_mms prints pressure diagnostics; overset_falsifier records the balanced ladder (regression guard 20 N/m at dt; RTX_OVERSET_FALSIFIER_STRICT asserts the registered gates — (ii) holds at dt, misses at dt/2, dt/4; (iv) fails: residual ∝ 1/Δt^0.8). Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01X2GmJXeQ2njUecEKiJZ1G2
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
6b8837301f
commit
62df6bd628
@@ -431,6 +431,77 @@ impl OverlapMap {
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Flux balance at the fringe (Chesshire–Henshaw in spirit): make every
|
||||
/// fringe cell divergence-free by adjusting only its PRESCRIBED faces
|
||||
/// (never a face shared with an active cell), spreading each cell's
|
||||
/// imbalance over them by face length, in Gauss–Seidel sweeps (a face
|
||||
/// shared by two fringe cells is corrected by both) until the largest
|
||||
/// fringe-cell imbalance is below `tol` (volume flux) or `max_sweeps`
|
||||
/// is reached. Returns `(sweeps, worst imbalance)`. This is what removes
|
||||
/// the reclassification impulse of A-P3: with the fringe ring a
|
||||
/// staircase of the interpolated velocities' mass defect, every
|
||||
/// row flip injected that defect in one step (§5.10).
|
||||
pub fn balance_fringe_fluxes(
|
||||
&self,
|
||||
field: &mut FlowField,
|
||||
tol: f64,
|
||||
max_sweeps: usize,
|
||||
) -> (usize, f64) {
|
||||
let (nx, ny, dx, dy) = (self.nx, self.ny, self.dx, self.dy);
|
||||
let is_prescribed_u: std::collections::HashSet<(usize, usize)> =
|
||||
self.fringe_u.iter().map(|e| (e.j, e.i)).collect();
|
||||
let is_prescribed_v: std::collections::HashSet<(usize, usize)> =
|
||||
self.fringe_v.iter().map(|e| (e.j, e.i)).collect();
|
||||
let _ = (nx, ny);
|
||||
let mut worst = f64::INFINITY;
|
||||
let mut sweeps = 0usize;
|
||||
while sweeps < max_sweeps && worst > tol {
|
||||
sweeps += 1;
|
||||
worst = 0.0;
|
||||
for e in &self.fringe_cells {
|
||||
let (j, i) = (e.j, e.i);
|
||||
let div = (field.u[(j, i + 1)] - field.u[(j, i)]) * dy
|
||||
+ (field.v[(j + 1, i)] - field.v[(j, i)]) * dx;
|
||||
// Prescribed faces of this cell with their outward sign and length.
|
||||
let mut faces: Vec<(bool, usize, usize, f64, f64)> = Vec::with_capacity(4);
|
||||
if is_prescribed_u.contains(&(j, i + 1)) {
|
||||
faces.push((true, j, i + 1, 1.0, dy));
|
||||
}
|
||||
if is_prescribed_u.contains(&(j, i)) {
|
||||
faces.push((true, j, i, -1.0, dy));
|
||||
}
|
||||
if is_prescribed_v.contains(&(j + 1, i)) {
|
||||
faces.push((false, j + 1, i, 1.0, dx));
|
||||
}
|
||||
if is_prescribed_v.contains(&(j, i)) {
|
||||
faces.push((false, j, i, -1.0, dx));
|
||||
}
|
||||
let total_len: f64 = faces.iter().map(|f| f.4).sum();
|
||||
if total_len == 0.0 {
|
||||
worst = worst.max(div.abs());
|
||||
continue;
|
||||
}
|
||||
for (is_u, jj, ii, sign, len) in faces {
|
||||
// outward flux change on this face = −div · len / total_len
|
||||
let dvel = -sign * div / total_len;
|
||||
if is_u {
|
||||
field.u[(jj, ii)] += dvel;
|
||||
} else {
|
||||
field.v[(jj, ii)] += dvel;
|
||||
}
|
||||
let _ = len;
|
||||
}
|
||||
}
|
||||
for e in &self.fringe_cells {
|
||||
let (j, i) = (e.j, e.i);
|
||||
let div = (field.u[(j, i + 1)] - field.u[(j, i)]) * dy
|
||||
+ (field.v[(j + 1, i)] - field.v[(j, i)]) * dx;
|
||||
worst = worst.max(div.abs());
|
||||
}
|
||||
}
|
||||
(sweeps, worst)
|
||||
}
|
||||
|
||||
/// A cell-centred background scalar (e.g. `p'`) at every acceptor.
|
||||
pub fn acceptor_scalar(&self, m: &nalgebra::DMatrix<f64>) -> Vec<f64> {
|
||||
self.acceptors.iter().map(|a| a.p.value(m)).collect()
|
||||
|
||||
Reference in New Issue
Block a user