rtx-cfd: overset_cfd23 RTX_OVERSET_CFD23_PHASE=N — the momentum chain's stages sampled every N steps and averaged over the window; over whole shedding periods the unsteady terms vanish and the averaged chain is exact (the patch balance's unstored unsteady term included)
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
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 / 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
Performance Benchmarks / Run Benchmarks (push) Canceled after 0s
CI / Format Check (push) Canceled after 0s

Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_0116sg1Qz1gMv9hdcKP1XUam
This commit is contained in:
Omar Sobh
2026-09-07 04:14:55 -07:00
co-authored by Claude Fable 5.1
parent 1a8f4aaebe
commit cee9adef26
@@ -250,6 +250,30 @@ impl Composite {
) )
} }
/// The chain's stage values at one instant (x components, N/m).
fn chain_values(&self) -> ChainValues {
let dt = self.dt;
let wall = self.wall();
let mr = self.solver.momentum_residual(&self.field, dt);
let ring = mr.fringe_fringe.fx + mr.fringe_hole.fx;
let bx = self.solver.solver_metric_force(&self.field, dt, self.cv).0;
let pb = self
.solver
.patch()
.momentum_balance(&self.field.patch, self.solver.time());
ChainValues {
t: self.solver.time(),
box_force: bx,
ring,
interface: pb.flux_force()[0],
wall_scheme: pb.wall_force()[0],
wall: wall[0],
pressure_defect: pb.pressure_defect()[0],
balance_residual: pb.balance()[0],
lift: wall[1],
}
}
/// The solver-metric momentum chain at the current state (§5.11): /// The solver-metric momentum chain at the current state (§5.11):
/// residual buckets, the box in the solver's flux form, the patch's /// residual buckets, the box in the solver's flux form, the patch's
/// balance. Returns the solved-far Σ|r| (the pin). /// balance. Returns the solved-far Σ|r| (the pin).
@@ -386,6 +410,46 @@ impl Composite {
} }
} }
/// The chain's stages at one instant (x components, N/m).
#[derive(Debug, Clone, Copy, Default)]
struct ChainValues {
t: f64,
box_force: f64,
ring: f64,
interface: f64,
wall_scheme: f64,
wall: f64,
pressure_defect: f64,
balance_residual: f64,
lift: f64,
}
impl ChainValues {
fn add(&mut self, o: &ChainValues) {
self.box_force += o.box_force;
self.ring += o.ring;
self.interface += o.interface;
self.wall_scheme += o.wall_scheme;
self.wall += o.wall;
self.pressure_defect += o.pressure_defect;
self.balance_residual += o.balance_residual;
self.lift += o.lift;
}
fn scaled(&self, k: f64) -> ChainValues {
ChainValues {
t: self.t,
box_force: self.box_force * k,
ring: self.ring * k,
interface: self.interface * k,
wall_scheme: self.wall_scheme * k,
wall: self.wall * k,
pressure_defect: self.pressure_defect * k,
balance_residual: self.balance_residual * k,
lift: self.lift * k,
}
}
}
/// One sampled series of the three load routes. /// One sampled series of the three load routes.
struct Series { struct Series {
times: Vec<f64>, times: Vec<f64>,
@@ -447,6 +511,14 @@ async fn run_sampled(
pin: 0.0, pin: 0.0,
}; };
let (mut rounds_total, mut correctors_total) = (0usize, 0usize); let (mut rounds_total, mut correctors_total) = (0usize, 0usize);
// `RTX_OVERSET_CFD23_PHASE=N`: the chain's stages every N steps once
// `t >= t_start`, averaged — over whole shedding periods the unsteady
// terms (the residual's, the box's, and the one the patch balance
// cannot store) vanish and the averaged chain is exact.
let phase_every = env_usize("RTX_OVERSET_CFD23_PHASE", 0);
let mut phase_sum = ChainValues::default();
let mut phase_n = 0usize;
let (mut phase_t0, mut phase_t1) = (f64::NAN, f64::NAN);
while !loaded && c.solver.time() < t_end { while !loaded && c.solver.time() < t_end {
let r = c.solver.advance(&mut c.field, c.dt).await?; let r = c.solver.advance(&mut c.field, c.dt).await?;
s.steps += 1; s.steps += 1;
@@ -474,6 +546,15 @@ async fn run_sampled(
s.box_lift.push(b.1); s.box_lift.push(b.1);
s.cv_drag.push(cvx); s.cv_drag.push(cvx);
} }
if phase_every > 0 && s.steps % phase_every == 0 && c.solver.time() >= t_start {
let v = c.chain_values();
if phase_n == 0 {
phase_t0 = v.t;
}
phase_t1 = v.t;
phase_sum.add(&v);
phase_n += 1;
}
if s.steps % 2000 == 0 { if s.steps % 2000 == 0 {
let w = c.wall(); let w = c.wall();
println!( println!(
@@ -489,6 +570,26 @@ async fn run_sampled(
} }
s.seconds = start.elapsed().as_secs_f64(); s.seconds = start.elapsed().as_secs_f64();
s.rounds_mean = rounds_total as f64 / correctors_total.max(1) as f64; s.rounds_mean = rounds_total as f64 / correctors_total.max(1) as f64;
if phase_n > 0 {
let m = phase_sum.scaled(1.0 / phase_n as f64);
println!(
" {case} PHASE-AVERAGED chain ny = {ny} over t = {phase_t0:.4}{phase_t1:.4} s ({phase_n} samples every {phase_every} steps) [N/m]: box {:.3} → ring {:+.3} → hole flux {:.3} → band {:+.3} → patch interface {:.3} → interior {:+.3} (δP {:+.3}, balance residual {:+.3} — the unsteady term's period average) → wall, scheme fluxes {:.3} → wall formula {:+.3} → wall {:.3}; total wall box {:+.3} ({:+.2} %); mean lift {:+.3}",
m.box_force,
m.ring,
m.box_force + m.ring,
m.interface - (m.box_force + m.ring),
m.interface,
m.wall_scheme - m.interface,
m.pressure_defect,
m.balance_residual,
m.wall_scheme,
m.wall - m.wall_scheme,
m.wall,
m.wall - m.box_force,
100.0 * (m.wall - m.box_force) / m.wall,
m.lift
);
}
s.pin = c.chain(case); s.pin = c.chain(case);
c.save(case)?; c.save(case)?;
Ok(s) Ok(s)