S2-9: the flag on the recorded FSI2 kinematics — fsi2_overset_dump_centreline (rtx-fsi) exports the saved instants' wetted-node d/ḋ as a centreline per knot (clamp + 70 columns); tests/embedded3_flag_kinematics (cubic Hermite through the knots with their velocities, t = 0 at RTX_E3_FLAG_KIN_T0, a 0.5 s ramp from the undeflected line, arc-length interpolation along the stations) drives both the 3D/slab flag (RTX_E3_FLAG_KINEMATICS, both in-plane surface-velocity components, the record's period) and the 2D reference; the analytic paths unchanged (same tuples)
CI / Build (macos-latest) (push) Waiting to run
CI / Test (macos-latest) (push) Blocked by required conditions
CI / Test (ubuntu-latest) (push) Blocked by required conditions
CI / Python Bindings (maturin) (macos-latest) (push) Blocked by required conditions
CI / Python Bindings (maturin) (ubuntu-latest) (push) Blocked by required conditions
CI / WASM Build + Size Check (push) Blocked by required conditions
CI / Distributed Training Tests (push) Blocked by required conditions
CI / CI Success (push) Blocked by required conditions
CI / Build CPU-Only (Explicit) (push) Failing after 4s
Documentation / Build API Documentation (push) Failing after 4s
Documentation / Build User Guide (push) Successful in 6s
CI / Format Check (push) Failing after 13s
CI / Build (ubuntu-latest) (push) Failing after 1m56s
CI / Clippy Check (push) Failing after 2m12s
Performance Benchmarks / Run Benchmarks (push) Successful in 2m54s

Co-Authored-By: Claude Fable 5.1 <[email protected]>
This commit is contained in:
Omar Sobh
2026-09-20 21:27:26 -05:00
co-authored by Claude Fable 5.1
parent 03a9c9686e
commit f1714fb926
4 changed files with 268 additions and 22 deletions
@@ -8,6 +8,8 @@
//! median-filtered lift swing.
//!
//! `cargo test --release -p rtx-cfd --test embedded3_flag_reference_2d -- --ignored --nocapture`
mod embedded3_flag_kinematics;
use embedded3_flag_kinematics::{Recorded, recorded};
use rtx_cfd::solvers::incompressible::{
AleBoundaries, ConvectionScheme, EmbeddedBody, EmbeddedParameters, EmbeddedPisoSolver,
FlowField, MgPrecision, MgSmoother, PoissonSolverKind, SideBoundary,
@@ -48,26 +50,42 @@ fn deflection(s: f64, t: f64) -> (f64, f64) {
)
}
fn centreline(m: usize, n: usize, t: f64) -> (f64, f64, f64) {
/// The centreline point `m` of `n` at `t`: (x, y, vx, vy) — the first
/// mode, or (S2-9) the recorded FSI2 kinematics along its stations.
fn centreline(m: usize, n: usize, t: f64) -> (f64, f64, f64, f64) {
let s = m as f64 / n as f64;
if let Some(rec) = recorded() {
thread_local! {
static PTS: std::cell::RefCell<(f64, Vec<(f64, f64, f64, f64)>)> =
const { std::cell::RefCell::new((f64::NAN, Vec::new())) };
}
return PTS.with(|cell| {
let mut c = cell.borrow_mut();
if c.0.to_bits() != t.to_bits() {
c.1 = rec.at(t, CY);
c.0 = t;
}
rec.along(&c.1, s)
});
}
let (d, v) = deflection(s, t);
(FLAG_X0 + s * FLAG_LEN, CY + d, v)
(FLAG_X0 + s * FLAG_LEN, CY + d, 0.0, v)
}
/// Distance to the capsule flag and the centreline velocity at the foot.
fn flag_sdf(x: f64, y: f64, t: f64) -> (f64, f64) {
fn flag_sdf(x: f64, y: f64, t: f64) -> (f64, (f64, f64)) {
let n = 40;
let mut best = f64::INFINITY;
let mut v_best = 0.0;
let mut v_best = (0.0, 0.0);
for m in 0..n {
let (ax, ay, av) = centreline(m, n, t);
let (bx, by, bv) = centreline(m + 1, n, t);
let (ax, ay, avx, avy) = centreline(m, n, t);
let (bx, by, bvx, bvy) = centreline(m + 1, n, t);
let (ex, ey) = (bx - ax, by - ay);
let u = (((x - ax) * ex + (y - ay) * ey) / (ex * ex + ey * ey)).clamp(0.0, 1.0);
let d = ((x - ax - u * ex).powi(2) + (y - ay - u * ey).powi(2)).sqrt();
if d < best {
best = d;
v_best = av + u * (bv - av);
v_best = (avx + u * (bvx - avx), avy + u * (bvy - avy));
}
}
(best - FLAG_HALF, v_best)
@@ -84,8 +102,8 @@ fn samples(t: f64, ds: f64) -> Vec<(f64, f64, f64, f64, f64)> {
let mut out = Vec::new();
let n = ((FLAG_LEN / ds).ceil() as usize).max(8);
for m in 0..n {
let (ax, ay, _) = centreline(m, n, t);
let (bx, by, _) = centreline(m + 1, n, t);
let (ax, ay, _, _) = centreline(m, n, t);
let (bx, by, _, _) = centreline(m + 1, n, t);
let (ex, ey) = (bx - ax, by - ay);
let len = (ex * ex + ey * ey).sqrt();
let (tx, ty) = (ex / len, ey / len);
@@ -99,8 +117,8 @@ fn samples(t: f64, ds: f64) -> Vec<(f64, f64, f64, f64, f64)> {
}
}
// The tip: a semicircle around the last centreline point.
let (tx0, ty0, _) = centreline(n, n, t);
let (px, py, _) = centreline(n - 1, n, t);
let (tx0, ty0, _, _) = centreline(n, n, t);
let (px, py, _, _) = centreline(n - 1, n, t);
let ang0 = (ty0 - py).atan2(tx0 - px);
let n_arc = ((std::f64::consts::PI * FLAG_HALF / ds).ceil() as usize).max(4);
for k in 0..n_arc {
@@ -144,8 +162,10 @@ async fn flag_wake_2d_reference() -> CfdResult<()> {
let h = H / ny as f64;
let nx = (L / h).round() as usize;
let dt = 8.817e-4;
let period = 1.0 / FREQ;
let t_end = 2.0 * period;
// S2-9: the record's period and the requested number of periods.
let env_f = |k: &str, d: f64| std::env::var(k).ok().and_then(|v| v.parse().ok()).unwrap_or(d);
let period = if recorded().is_some() { env_f("RTX_E3_FLAG_KIN_PERIOD", 0.5225) } else { 1.0 / FREQ };
let t_end = env_f("RTX_E3_FLAG_PERIODS", 2.0) * period;
let config = CfdConfig::new()
.with_density(RHO)
.with_viscosity(RHO * NU)
@@ -174,7 +194,7 @@ async fn flag_wake_2d_reference() -> CfdResult<()> {
}
});
let flag = EmbeddedBody::from_sdf(|x, y, t| flag_sdf(x, y, t).0)
.with_surface_velocity(|x, y, t| (0.0, flag_sdf(x, y, t).1));
.with_surface_velocity(|x, y, t| flag_sdf(x, y, t).1);
solver.set_moving_body(EmbeddedBody::union(
EmbeddedBody::circle(CX, CY, R_CYL),
flag,