R8-e: the coupled 3D FSI's small-dt instability located in the load/kinematics transfer, not a lagged fluid response
- tests/fsi2_embedded3_impulse.rs (new, ignored, cuda): the added-mass timing instrument — the R8-a fluid with a prescribed centreline velocity step (a one-step acceleration delta), baseline and impulse passes from one device snapshot, the load difference per step. Pitch shape on the slab ny 62: generalized added mass 39.11 / 39.09 / 39.07 kg/m at dt x 1 / 0.75 / 0.5, the step after carries 1.7 / 1.3 / 0.9 % above the steady tail — the cut-cell fluid answers in the same step (the lagged-added-mass hypothesis refuted). - fsi2_embedded3.rs: `RTX_E3FSI_LINE_CSV` (per-step centreline + mid/bottom/top y per station) showed the growing 2dt mode is a through-thickness mode — the edge nodes move against the mid-plane nodes (~4:1): the fluid reads the mid-plane, the loads land on the edges, so the added mass acts on that mode with the wrong sign (a negative effective added mass; the scalar Newmark model is unstable for it at omega dt below ~1-2 with gamma 0.7, stable at larger dt or gamma 0.9 — R8-a's observed pattern). `RTX_E3FSI_KIN=surface` (default off, byte-identical when unset): the fluid's centreline = the mean of the bottom and top edge nodes (the nodes the loads land on) — dt x 0.75 / 0.5 / 0.25 stable at gamma 0.7. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
171da41ed1
commit
74fac32a91
@@ -18,7 +18,11 @@
|
||||
//! `RTOL` (1e-3), `FLOOR` (1.5e-7 on the centreline vector), `MAX_SUBIT`
|
||||
//! (12), `STALL_ACCEPT` (5), `GAMMA` (0.7), `SPEED` (3.0 m/s, the band's
|
||||
//! surface-speed bound), `CSV` (per-step series), `TRACE` (steps whose
|
||||
//! passes are printed).
|
||||
//! passes are printed); R8-e: `KIN` (`centre` = the mid-plane nodes drive
|
||||
//! the fluid's centreline; `surface` = the mean of the bottom and top edge
|
||||
//! nodes, the nodes the loads land on — the transfer-consistent pair that
|
||||
//! removes the small-dt instability), `LINE_CSV` (the committed centreline
|
||||
//! and the per-station mid/bottom/top y displacement per step).
|
||||
//!
|
||||
//! `RTX_E3FSI_NY=62 RTX_E3FSI_CSV=<path> RTX_CUDA_ARCH=sm_120 cargo test --release -p rtx-fsi \
|
||||
//! --features cuda --test fsi2_embedded3 -- --ignored --nocapture`
|
||||
@@ -67,6 +71,9 @@ struct Flag {
|
||||
/// (reference y, wetted index), ascending (corners included).
|
||||
tip: Vec<(f64, usize)>,
|
||||
a_node: NodeId,
|
||||
/// R8-e: the bottom and top edge nodes at each station's x (the wetted
|
||||
/// surface the loads land on).
|
||||
surface: Vec<[NodeId; 2]>,
|
||||
}
|
||||
|
||||
impl Flag {
|
||||
@@ -107,6 +114,12 @@ impl Flag {
|
||||
top,
|
||||
tip,
|
||||
a_node: find(0.6, 0.2),
|
||||
surface: (0..STATIONS)
|
||||
.map(|k| {
|
||||
let x = X0 + 0.01 * k as f64;
|
||||
[find(x, 0.19), find(x, 0.21)]
|
||||
})
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -287,6 +300,11 @@ fn fsi2_on_embedded3() {
|
||||
.unwrap();
|
||||
f
|
||||
});
|
||||
// R8-e `RTX_E3FSI_LINE_CSV`: the committed centreline per coupled step
|
||||
// (t, then x, y displacement per station) — the unstable mode's shape.
|
||||
let mut line_csv = std::env::var("RTX_E3FSI_LINE_CSV")
|
||||
.ok()
|
||||
.map(|p| std::fs::File::create(p).expect("line csv"));
|
||||
let start = std::time::Instant::now();
|
||||
|
||||
// Phase 1: the rigid flag (target 1: the rest state vs CFD2 136.7 / 10.53).
|
||||
@@ -380,8 +398,27 @@ fn fsi2_on_embedded3() {
|
||||
})
|
||||
.collect();
|
||||
let a_dofs = flag.borrow().node_dofs(flag_geo.a_node);
|
||||
// R8-e `RTX_E3FSI_KIN=surface`: the fluid's centreline is the mean of the
|
||||
// bottom and top edge nodes at each station (the nodes the loads land on)
|
||||
// instead of the mid-plane nodes (default `centre`).
|
||||
let kin_surface = std::env::var("RTX_E3FSI_KIN").is_ok_and(|v| v == "surface");
|
||||
let surface_dofs: Vec<[usize; 4]> = flag_geo
|
||||
.surface
|
||||
.iter()
|
||||
.map(|[b, t]| {
|
||||
let (db, dt_) = (flag.borrow().node_dofs(*b), flag.borrow().node_dofs(*t));
|
||||
[db[0], db[1], dt_[0], dt_[1]]
|
||||
})
|
||||
.collect();
|
||||
let extract = |s: &DynamicState| -> Vec<f64> {
|
||||
let mut c = vec![0.0; 2 * STATIONS];
|
||||
if kin_surface {
|
||||
for (k, d) in surface_dofs.iter().enumerate() {
|
||||
c[2 * k] = 0.5 * (s.displacement[d[0]] + s.displacement[d[2]]);
|
||||
c[2 * k + 1] = 0.5 * (s.displacement[d[1]] + s.displacement[d[3]]);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
for (k, d) in centre_dofs.iter().enumerate() {
|
||||
c[2 * k] = s.displacement[d[0]];
|
||||
c[2 * k + 1] = s.displacement[d[1]];
|
||||
@@ -603,6 +640,16 @@ fn fsi2_on_embedded3() {
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
if let Some(f) = line_csv.as_mut() {
|
||||
// Then the y displacement of the mid-plane, bottom and top nodes per station.
|
||||
let mut c: Vec<String> = c_fluid_n.iter().map(|v| format!("{v:.6e}")).collect();
|
||||
for (k, d) in surface_dofs.iter().enumerate() {
|
||||
let yc = flag_state.displacement[centre_dofs[k][1]];
|
||||
let (yb, yt) = (flag_state.displacement[d[1]], flag_state.displacement[d[3]]);
|
||||
c.extend([yc, yb, yt].map(|v| format!("{v:.6e}")));
|
||||
}
|
||||
writeln!(f, "{t_new:.6},{}", c.join(",")).unwrap();
|
||||
}
|
||||
let at_end = step + 1 == coupled_steps;
|
||||
if let Some(dir) = save_dir
|
||||
.as_ref()
|
||||
|
||||
Reference in New Issue
Block a user