rtx-cfd + rtx-fsi: ECSW campaign phase 1 — snapshot dump + FlowField save/load
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
FlowField::save/load serialize the complete field state bit-exact
(all twelve matrices including *_old, predictors and sources, so a
load is a true restart state), with a roundtrip test asserting
to_bits equality on every value and rejection of truncated/corrupt
files.
The march gains an ECSW snapshot knob (RTX_FSI{2,3}_SNAP path,
SNAPEVERY, default off): every N committed steps it appends an FSNP
record — t, full-DOF displacement/velocity/acceleration (what
rtx_fea::mor's pod_basis/train_ecsw consume, plus what the phase-4
dynamic reduction will need) and the committed sparse nodal load for
the offline full-vs-reduced replay. Reporting-only: reads committed
state after acceptance, no float ops on the solver path. Verified:
smoke run's FSNP parsed by an independent reader (570 DOFs, correct
record count, physical values); FSI2 committed default
digit-identical with the knob off.
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
366a46b471
commit
9fe9d7f74a
@@ -70,6 +70,16 @@ pub struct MarchConfig {
|
||||
/// measured to change nothing that matters — see the probe).
|
||||
pub smooth_in_h: f64,
|
||||
pub csv_path: Option<String>,
|
||||
/// ECSW snapshot dump (0 = off): every `snap_every` committed steps,
|
||||
/// append the committed flag state — full-DOF displacement, velocity
|
||||
/// and acceleration — plus the committed sparse nodal load to
|
||||
/// `snap_path` (binary, magic `FSNP`; see `write_snapshot`). The POD
|
||||
/// basis and ECSW training (`rtx_fea::mor`) consume the displacement
|
||||
/// snapshots; the load records drive the offline full-vs-reduced
|
||||
/// replay. Reporting-only: reads the committed state after
|
||||
/// acceptance, no float ops on the solver path.
|
||||
pub snap_path: Option<String>,
|
||||
pub snap_every: usize,
|
||||
/// IQN's relaxation on the very first pass, before any secant
|
||||
/// information exists. Must CONTRACT a repulsive added-mass map: for
|
||||
/// a per-pass gain `-g` the first update multiplies the residual by
|
||||
@@ -105,7 +115,8 @@ pub struct MarchConfig {
|
||||
|
||||
impl MarchConfig {
|
||||
/// Read `RTX_{prefix}_{NY,T_RELEASE,T_END,SUBCYCLE,TOL,RTOL,STALLX,
|
||||
/// HYST,MAXSUB,FLAG_NX,SMOOTH,COUPLER,REUSE,CSV}` over `defaults`.
|
||||
/// HYST,MAXSUB,FLAG_NX,SMOOTH,COUPLER,REUSE,CSV,SNAP,SNAPEVERY}`
|
||||
/// 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);
|
||||
@@ -124,6 +135,8 @@ impl MarchConfig {
|
||||
reuse: num("REUSE", defaults.reuse as f64) as usize,
|
||||
smooth_in_h: num("SMOOTH", defaults.smooth_in_h),
|
||||
csv_path: std::env::var(key("CSV")).ok().or(defaults.csv_path),
|
||||
snap_path: std::env::var(key("SNAP")).ok().or(defaults.snap_path),
|
||||
snap_every: num("SNAPEVERY", defaults.snap_every as f64) as usize,
|
||||
initial_relaxation: num("OMEGA0", defaults.initial_relaxation),
|
||||
trace_steps: num("TRACE", defaults.trace_steps as f64) as usize,
|
||||
c1_interface: num("C1", f64::from(u8::from(defaults.c1_interface))) != 0.0,
|
||||
@@ -263,6 +276,8 @@ pub fn run_march(case: BenchmarkCase, config: &MarchConfig) -> MarchResult {
|
||||
reuse,
|
||||
smooth_in_h,
|
||||
ref csv_path,
|
||||
ref snap_path,
|
||||
snap_every,
|
||||
initial_relaxation,
|
||||
trace_steps,
|
||||
c1_interface,
|
||||
@@ -399,6 +414,14 @@ pub fn run_march(case: BenchmarkCase, config: &MarchConfig) -> MarchResult {
|
||||
let mut csv = csv_path
|
||||
.as_ref()
|
||||
.map(|p| std::fs::File::create(p).expect("csv path"));
|
||||
let mut snap = snap_path.as_ref().map(|p| {
|
||||
let mut w = std::io::BufWriter::new(std::fs::File::create(p).expect("snap path"));
|
||||
w.write_all(b"FSNP").unwrap();
|
||||
w.write_all(&1u32.to_le_bytes()).unwrap();
|
||||
w.write_all(&(flag_state.displacement.len() as u64).to_le_bytes())
|
||||
.unwrap();
|
||||
w
|
||||
});
|
||||
|
||||
let phase_start = std::time::Instant::now();
|
||||
for step in 0..coupled_steps {
|
||||
@@ -578,6 +601,11 @@ pub fn run_march(case: BenchmarkCase, config: &MarchConfig) -> MarchResult {
|
||||
times.push(t);
|
||||
ux_series.push(ux);
|
||||
uy_series.push(uy);
|
||||
if snap_every > 0 && (step + 1) % snap_every == 0 {
|
||||
if let Some(w) = snap.as_mut() {
|
||||
write_snapshot(w, t, &flag_state, &committed_nodal);
|
||||
}
|
||||
}
|
||||
let (drag_now, lift_now) = harness.measure_force(&solver.borrow(), &field.borrow());
|
||||
interval_drag.push(drag_now);
|
||||
interval_lift.push(lift_now);
|
||||
@@ -631,3 +659,29 @@ pub fn run_march(case: BenchmarkCase, config: &MarchConfig) -> MarchResult {
|
||||
elapsed: start.elapsed().as_secs_f64(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Append one `FSNP` record: `t`, the committed full-DOF displacement,
|
||||
/// velocity and acceleration, then the committed sparse nodal load as
|
||||
/// `(node id, fx, fy, fz)` tuples — everything the ECSW phase needs
|
||||
/// (POD/ECSW train on the displacement snapshots; the load records
|
||||
/// drive the offline full-vs-reduced replay).
|
||||
fn write_snapshot(
|
||||
w: &mut std::io::BufWriter<std::fs::File>,
|
||||
t: f64,
|
||||
state: &DynamicState,
|
||||
nodal: &[(NodeId, Vector3<f64>)],
|
||||
) {
|
||||
w.write_all(&t.to_le_bytes()).unwrap();
|
||||
for series in [&state.displacement, &state.velocity, &state.acceleration] {
|
||||
for v in series.iter() {
|
||||
w.write_all(&v.to_le_bytes()).unwrap();
|
||||
}
|
||||
}
|
||||
w.write_all(&(nodal.len() as u64).to_le_bytes()).unwrap();
|
||||
for (node, f) in nodal {
|
||||
w.write_all(&(node.0 as u64).to_le_bytes()).unwrap();
|
||||
for c in 0..3 {
|
||||
w.write_all(&f[c].to_le_bytes()).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user