test(rtx-cfd): fresh-cell falsifier extended (circle body, far-field probe, kinetic energy, speed knob) + print-only divergence trace; two candidate fixes REFUTED on it (swept-volume source 40x worse at either sign; fresh-face field extension no effect), both kept default-off with their verdicts
Performance Benchmarks / Run Benchmarks (push) Canceled after 0s
CI / Format Check (push) Canceled after 0s
Documentation / Build API Documentation (push) Canceled after 0s
Documentation / Build User Guide (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
Performance Benchmarks / Run Benchmarks (push) Canceled after 0s
CI / Format Check (push) Canceled after 0s
Documentation / Build API Documentation (push) Canceled after 0s
Documentation / Build User Guide (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
Phase 1 of omni-cortex docs/fresh_cell_gcl_campaign.md. The mechanism of the moving-body force spikes is measured from four directions: per-flip force amplitude ∝ 1/dt, kinetic energy injected per flipped cell 0.048 J/m independent of dt and body shape (plate row vs circle), felt at a far-field pressure probe, and ∝ U^2 (2.60 / 0.64 / 0.15 J/m at U = 1 / 0.5 / 0.25). A binary mask's wall position jumps by one cell at every flip and the fluid answers with a fixed impulse. Neither the swept-volume source (the wall faces already carry the swept volume — the source double-counts it) nor the fresh-face velocity is where it lives. Next: the virtual cut cell in the projection (apertures + the wall-relative divergence), registered in the campaign doc. 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
23eb996a9f
commit
c9492d3e1e
@@ -696,6 +696,47 @@ impl EmbeddedMask {
|
||||
self.impose_from(body, &u_source, &v_source, u, v, t)
|
||||
}
|
||||
|
||||
/// Field extension for the faces that were ghosts in this (old) mask
|
||||
/// and are fluid in `new_mask`: overwrite their velocity AND history
|
||||
/// with the fluid-side reconstruction at their new distance from the
|
||||
/// wall ([`Ghost::extend`]), instead of the inherited ghost value (a
|
||||
/// plane fit extrapolated through the wall, or — where the fit has too
|
||||
/// few fluid nodes, at corners — the mirror formula with the
|
||||
/// deviation's sign flipped). Returns the number of faces extended.
|
||||
/// Knob-gated by the solver; off, nothing here runs.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn extend_fresh_faces(
|
||||
&self,
|
||||
new_mask: &EmbeddedMask,
|
||||
body: &EmbeddedBody,
|
||||
t: f64,
|
||||
u_source: &DMatrix<f64>,
|
||||
v_source: &DMatrix<f64>,
|
||||
u: &mut DMatrix<f64>,
|
||||
v: &mut DMatrix<f64>,
|
||||
u_old: &mut DMatrix<f64>,
|
||||
v_old: &mut DMatrix<f64>,
|
||||
) -> usize {
|
||||
let mut extended = 0usize;
|
||||
for g in &self.u_ghosts {
|
||||
if new_mask.u_kind(g.j, g.i) == FaceKind::Fluid {
|
||||
let val = g.extend(u_source, body.phi(g.x, g.y, t));
|
||||
u[(g.j, g.i)] = val;
|
||||
u_old[(g.j, g.i)] = val;
|
||||
extended += 1;
|
||||
}
|
||||
}
|
||||
for g in &self.v_ghosts {
|
||||
if new_mask.v_kind(g.j, g.i) == FaceKind::Fluid {
|
||||
let val = g.extend(v_source, body.phi(g.x, g.y, t));
|
||||
v[(g.j, g.i)] = val;
|
||||
v_old[(g.j, g.i)] = val;
|
||||
extended += 1;
|
||||
}
|
||||
}
|
||||
extended
|
||||
}
|
||||
|
||||
/// [`Self::impose`] with the fluid values read from a *different* field
|
||||
/// than the one written: the moving-body step reconstructs the new
|
||||
/// mask's ghost values from the previous step's corrected field (the
|
||||
@@ -1057,6 +1098,20 @@ impl Ghost {
|
||||
/// collinear), fall back to the linear profile along the normal with the
|
||||
/// non-fluid nodes replaced by the surface velocity at their own
|
||||
/// projections.
|
||||
/// The fluid-side value at signed distance `s_new >= 0` from the wall
|
||||
/// along this ghost's normal: the wall value plus the probe's
|
||||
/// deviation scaled by `s_new / s_probe` — never the mirror. This is
|
||||
/// the field extension for a face that has just turned fluid
|
||||
/// (Yang & Balaras 2006; Lee, Kim, Choi & Yang 2011's temporal
|
||||
/// velocity discontinuity is what it removes).
|
||||
fn extend(&self, values: &DMatrix<f64>, s_new: f64) -> f64 {
|
||||
let mut probe = 0.0;
|
||||
for n in &self.nodes {
|
||||
probe += n.weight * n.fallback.unwrap_or_else(|| values[(n.j, n.i)]);
|
||||
}
|
||||
self.u_surface + (probe - self.u_surface) * (s_new.max(0.0) / self.s_probe)
|
||||
}
|
||||
|
||||
fn reconstruct(&self, values: &DMatrix<f64>) -> f64 {
|
||||
let mut pts: Vec<(f64, f64, f64)> = self
|
||||
.nodes
|
||||
|
||||
Reference in New Issue
Block a user