rtx-fsi: spike-clamp the force-measurement probe
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

measure_force integrated raw traction samples while the coupling loads
carried the 20x-median clamp — so the s = 1 benchmark run's REPORTED
drag/lift were +-4,000-scale garbage against a +-78 reference while its
displacements matched the benchmark to 0.1%. Collect, clamp, then
integrate, both probes. Reporting only: the committed FSI2 default
reproduces its trajectory to every printed digit (uy 3.7732 / 3.7920
mm) and its rigid-phase drag (121.4) with the clamp in.

Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Lnyrw33Lu6rUhW42E9KHwq
This commit is contained in:
Omar Sobh
2026-08-24 05:59:18 -05:00
co-authored by Claude Fable 5
parent c1bcdc408e
commit 555a72cbc0
@@ -449,8 +449,13 @@ impl Fsi2Harness {
let mask = solver.mask().unwrap();
let body = solver.body().unwrap();
let vertices = self.shared.read().unwrap().0.clone();
let mut drag = 0.0;
let mut lift = 0.0;
// Collect, then clamp, then integrate: the same 20x-median spike
// clamp the coupling loads carry. Without it the REPORTED
// drag/lift at large deformation are dominated by the rare wild
// reconstructions (the s = 1 benchmark run printed +-4,000-scale
// load swings against a +-78 reference while its displacements
// matched the benchmark to 0.1%).
let mut samples: Vec<(f64, f64, f64)> = Vec::new();
let poly_probe = EmbeddedBody::polygon(vertices.clone());
for s in poly_probe.surface_samples(0.5 * self.h) {
if circle_sdf(s.x, s.y) < 1e-9 {
@@ -459,8 +464,7 @@ impl Fsi2Harness {
if let Some((tx, ty)) = mask.traction_at(
body, &field.u, &field.v, &field.p, self.mu, 0.0, s.x, s.y, s.nx, s.ny,
) {
drag += tx * s.ds;
lift += ty * s.ds;
samples.push((tx, ty, s.ds));
}
}
let circle_probe = EmbeddedBody::circle(0.2, 0.2, 0.05);
@@ -471,10 +475,28 @@ impl Fsi2Harness {
if let Some((tx, ty)) = mask.traction_at(
body, &field.u, &field.v, &field.p, self.mu, 0.0, s.x, s.y, s.nx, s.ny,
) {
drag += tx * s.ds;
lift += ty * s.ds;
samples.push((tx, ty, s.ds));
}
}
let mut magnitudes: Vec<f64> = samples
.iter()
.map(|(tx, ty, _)| (tx * tx + ty * ty).sqrt())
.collect();
magnitudes.sort_by(|a, b| a.partial_cmp(b).unwrap());
let median = magnitudes.get(magnitudes.len() / 2).copied().unwrap_or(0.0);
let cap = 20.0 * median;
let mut drag = 0.0;
let mut lift = 0.0;
for (tx, ty, ds) in samples {
let norm = (tx * tx + ty * ty).sqrt();
let scale = if median > 0.0 && norm > cap {
cap / norm
} else {
1.0
};
drag += tx * scale * ds;
lift += ty * scale * ds;
}
(drag, lift)
}