rtx-cfd/rtx-fsi: the FlowField->clawview exporter — real FSI fields through the viewer, end to end
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
Performance Benchmarks / Run Benchmarks (push) Canceled after 0s
CI / Format Check (push) Canceled after 0s
The march gains RTX_FSI{2,3}_FFLD (MarchConfig::ffld_dir, off by
default): every snap_every committed steps (10 when SNAPEVERY is 0),
dump the committed FlowField (FlowField::save, bit-exact), the solver's
own fluid-cell mask and the interface polygon as text sidecars, plus an
index.csv. Reporting-only after acceptance; the FSI2 default with the
knob off reproduces every printed digit of the warm-start baseline
(uy 3.4921 +- 3.5109, conservation 8.25e-12).
The exporter (rtx-cfd examples/ffld_to_vtk): a dump directory ->
clawview-readable legacy VTK — per-snapshot 2D triangle meshes (one
selectable point scalar --field p|umag|vort, the 0/1 fluid mask as
integer CELL_DATA) and an optional space-time volume (--spacetime:
frames stacked along z = time, prisms split to tets, POINT_DATA phi),
which clawview's slice-plane animation plays as a transient movie.
Verified end to end with REAL fields, not synthetic: a 29-frame FSI3
release-transient dump (t 4.0 -> 4.1, 64 MB) exported to 29 snapshots
+ a 984,312-tet space-time volume; the clawview server loaded both
(176,320 nodes volume; 23,877-node snapshots) and served live
cross-sections (time slices), vorticity isosurfaces and contours from
them. The check caught two real viewer-contract constraints now
encoded in the exporter: clawview's legacy-VTK path parses CELL_DATA
scalars as INTEGER markers only, and supports exactly ONE point scalar
(all POINT_DATA blocks append into `phi`) — multi-field snapshots need
the .clwv route, out of scope here.
Closes the top open thread of the fifteenth-session handoff (the
FSNP/FlowField->viewer exporter); the mesh-repo push and the claw-gds
cargo feature remain clawview-side items.
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
4d2cede7bc
commit
4aec4e589d
@@ -80,6 +80,17 @@ pub struct MarchConfig {
|
||||
/// acceptance, no float ops on the solver path.
|
||||
pub snap_path: Option<String>,
|
||||
pub snap_every: usize,
|
||||
/// Fluid-field dump directory (`RTX_{prefix}_FFLD`, off by default):
|
||||
/// every `snap_every` committed steps (10 if `snap_every` is 0),
|
||||
/// write the committed `FlowField` (`f_STEP.ffld`,
|
||||
/// `FlowField::save` — bit-exact), the mask's fluid-cell map
|
||||
/// (`mask_STEP.txt`, `ny` rows of `nx` `0`/`1` chars, row 0 first)
|
||||
/// and the interface polygon (`poly_STEP.txt`, `x y` per line), and
|
||||
/// append `step,t,f_STEP.ffld` to `index.csv` in the directory.
|
||||
/// Reporting-only: reads committed state after acceptance, no float
|
||||
/// ops on the solver path. The `ffld_to_vtk` example in `rtx-cfd`
|
||||
/// turns a dump directory into clawview-readable VTK.
|
||||
pub ffld_dir: Option<String>,
|
||||
/// 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
|
||||
@@ -115,8 +126,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,SNAP,SNAPEVERY}`
|
||||
/// over `defaults`.
|
||||
/// HYST,MAXSUB,FLAG_NX,SMOOTH,COUPLER,REUSE,CSV,SNAP,SNAPEVERY,
|
||||
/// FFLD}` 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);
|
||||
@@ -137,6 +148,7 @@ impl MarchConfig {
|
||||
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,
|
||||
ffld_dir: std::env::var(key("FFLD")).ok().or(defaults.ffld_dir),
|
||||
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,
|
||||
@@ -278,6 +290,7 @@ pub fn run_march(case: BenchmarkCase, config: &MarchConfig) -> MarchResult {
|
||||
ref csv_path,
|
||||
ref snap_path,
|
||||
snap_every,
|
||||
ref ffld_dir,
|
||||
initial_relaxation,
|
||||
trace_steps,
|
||||
c1_interface,
|
||||
@@ -622,6 +635,19 @@ pub fn run_march(case: BenchmarkCase, config: &MarchConfig) -> MarchResult {
|
||||
write_snapshot(w, t, &flag_state, &committed_nodal);
|
||||
}
|
||||
}
|
||||
if let Some(dir) = ffld_dir {
|
||||
let cadence = if snap_every > 0 { snap_every } else { 10 };
|
||||
if (step + 1) % cadence == 0 {
|
||||
write_ffld_dump(
|
||||
dir,
|
||||
step + 1,
|
||||
t,
|
||||
&solver.borrow(),
|
||||
&field.borrow(),
|
||||
&harness,
|
||||
);
|
||||
}
|
||||
}
|
||||
let (drag_now, lift_now) = harness.measure_force(&solver.borrow(), &field.borrow());
|
||||
interval_drag.push(drag_now);
|
||||
interval_lift.push(lift_now);
|
||||
@@ -696,6 +722,49 @@ pub fn run_march(case: BenchmarkCase, config: &MarchConfig) -> MarchResult {
|
||||
/// `(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).
|
||||
/// One fluid-field dump (see `MarchConfig::ffld_dir`): the committed
|
||||
/// `FlowField` bit-exact, the mask's fluid-cell map and the interface
|
||||
/// polygon as text sidecars, and an `index.csv` line. Reporting-only.
|
||||
fn write_ffld_dump(
|
||||
dir: &str,
|
||||
step: usize,
|
||||
t: f64,
|
||||
solver: &rtx_cfd::solvers::incompressible::EmbeddedPisoSolver,
|
||||
field: &rtx_cfd::solvers::incompressible::FlowField,
|
||||
harness: &super::Fsi2Harness,
|
||||
) {
|
||||
use std::io::Write as _;
|
||||
let dir = std::path::Path::new(dir);
|
||||
std::fs::create_dir_all(dir).expect("ffld dir");
|
||||
let ffld_name = format!("f_{step:06}.ffld");
|
||||
field.save(&dir.join(&ffld_name)).expect("ffld save");
|
||||
|
||||
let (nx, ny, _, _) = field.grid_info();
|
||||
let mask = solver.mask().expect("mask");
|
||||
let mut mask_text = String::with_capacity((nx + 1) * ny);
|
||||
for j in 0..ny {
|
||||
for i in 0..nx {
|
||||
mask_text.push(if mask.is_fluid_cell(j, i) { '1' } else { '0' });
|
||||
}
|
||||
mask_text.push('\n');
|
||||
}
|
||||
std::fs::write(dir.join(format!("mask_{step:06}.txt")), mask_text).expect("mask sidecar");
|
||||
|
||||
let mut poly_text = String::new();
|
||||
for &(x, y) in harness.shared.read().unwrap().0.vertices() {
|
||||
use std::fmt::Write as _;
|
||||
writeln!(poly_text, "{x:.9e} {y:.9e}").unwrap();
|
||||
}
|
||||
std::fs::write(dir.join(format!("poly_{step:06}.txt")), poly_text).expect("poly sidecar");
|
||||
|
||||
let mut index = std::fs::OpenOptions::new()
|
||||
.create(true)
|
||||
.append(true)
|
||||
.open(dir.join("index.csv"))
|
||||
.expect("index.csv");
|
||||
writeln!(index, "{step},{t:.9},{ffld_name}").expect("index line");
|
||||
}
|
||||
|
||||
fn write_snapshot(
|
||||
w: &mut std::io::BufWriter<std::fs::File>,
|
||||
t: f64,
|
||||
|
||||
Reference in New Issue
Block a user