embedded3 S2-8 instrument: the load routes in PARTS per body — exchange split into diffusive / convective (cut_wall_exchange_parts), a diagnostic x window on every load route (exchange::set_load_window), the flag test as a 2D periodic slab (RTX_E3_FLAG_NZ) with an amplitude knob (RTX_E3_FLAG_AMP, 0 = frozen) and a PARTS summary; cut_wall_force / _per_span moved to exchange.rs (cutwall.rs line cap)
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 (ubuntu-latest) (push) Failing after 4s
Documentation / Build API Documentation (push) Failing after 4s
CI / Build CPU-Only (Explicit) (push) Failing after 5s
Documentation / Build User Guide (push) Successful in 4s
CI / Format Check (push) Failing after 17s
CI / Clippy Check (push) Failing after 38s
Performance Benchmarks / Run Benchmarks (push) Successful in 1m47s

Co-Authored-By: Claude Fable 5.1 <[email protected]>
This commit is contained in:
Omar Sobh
2026-09-19 08:53:40 -05:00
co-authored by Claude Fable 5.1
parent 3f0bd0650a
commit 5e1b3e0731
4 changed files with 169 additions and 55 deletions
@@ -65,12 +65,19 @@ fn mode(s: f64) -> f64 {
/// Centreline deflection and its velocity at arc parameter `s`, time `t`.
fn deflection(s: f64, t: f64) -> (f64, f64) {
let w = 2.0 * std::f64::consts::PI * FREQ;
let amp = amplitude();
(
AMP * mode(s) * (w * t).sin(),
AMP * mode(s) * w * (w * t).cos(),
amp * mode(s) * (w * t).sin(),
amp * mode(s) * w * (w * t).cos(),
)
}
/// The tip amplitude: `RTX_E3_FLAG_AMP` (default 0.084; 0 freezes the flag —
/// the static control of the load routes).
fn amplitude() -> f64 {
env_f("RTX_E3_FLAG_AMP", AMP)
}
/// Signed distance to the deflected flag's cross-section (a capsule
/// around the centreline polyline of `n` segments) and the centreline's
/// transverse velocity at the closest point.
@@ -131,7 +138,10 @@ fn flag_wake_on_the_device() {
let periods = env_f("RTX_E3_FLAG_PERIODS", 2.0);
let h = H / ny as f64;
let nx = (L / h).round() as usize;
let nz = ny;
// `RTX_E3_FLAG_NZ=4`: a thin slab periodic in z with the 2D inflow (Ū = 1) — the
// flag as a 2D problem, minutes per rung: the instrument for the load routes' parts.
let slab_nz = env_f("RTX_E3_FLAG_NZ", 0.0) as usize;
let nz = if slab_nz > 0 { slab_nz } else { ny };
let r_edge = h;
let dt_cfl = 0.3 * h / (U_M.max(2.0 * std::f64::consts::PI * FREQ * AMP));
// `RTX_E3_FLAG_DT_SCALE` scales the step (the dt ladder of the loads).
@@ -155,18 +165,36 @@ fn flag_wake_on_the_device() {
tolerance: 1e-8,
convection_scheme: ConvectionScheme::TvdVanAlbada,
wall_scheme: WallScheme::CutCell,
boundaries: Boundaries {
x1: Side::PressureOutlet,
..Boundaries::default()
boundaries: if slab_nz > 0 {
Boundaries {
x1: Side::PressureOutlet,
z0: Side::Periodic,
z1: Side::Periodic,
..Boundaries::default()
}
} else {
Boundaries {
x1: Side::PressureOutlet,
..Boundaries::default()
}
},
// The narrow band: the flag's tip speed bounds the surface motion.
max_surface_speed: Some(2.0 * std::f64::consts::PI * FREQ * AMP * 1.05),
max_surface_speed: Some(
(2.0 * std::f64::consts::PI * FREQ * amplitude() * 1.05).max(1e-3),
),
..Parameters::default()
},
);
solver.set_boundary_velocity(|x, y, z, _t| {
let inflow_at = move |y: f64, z: f64| {
if slab_nz > 0 {
6.0 * y * (H - y) / (H * H)
} else {
inflow(y, z)
}
};
solver.set_boundary_velocity(move |x, y, z, _t| {
if x <= 0.0 {
(inflow(y, z), 0.0, 0.0)
(inflow_at(y, z), 0.0, 0.0)
} else {
(0.0, 0.0, 0.0)
}
@@ -186,7 +214,7 @@ fn flag_wake_on_the_device() {
let mut field = Field::new(g);
for k in 0..nz {
for j in 0..ny {
let u0 = inflow((j as f64 + 0.5) * h, (k as f64 + 0.5) * h);
let u0 = inflow_at((j as f64 + 0.5) * h, (k as f64 + 0.5) * h);
for i in 0..=nx {
field.u[g.uface(k, j, i)] = u0;
}
@@ -217,9 +245,17 @@ fn flag_wake_on_the_device() {
let last_period_start = t_end - period;
let mut next_phase = 0;
let mid = nz / 2;
let slab = (mid - 2, mid + 2);
let slab = if slab_nz > 0 {
(0, nz)
} else {
(mid - 2, mid + 2)
};
let width = nz as f64 * h;
let start = std::time::Instant::now();
let mut drag_rec_sum = 0.0;
// The routes' PARTS over the whole body (x, per unit width): operator
// pressure / shear / exchange, reconstructed pressure / shear.
let mut parts = [[0.0_f64; 6]; 3];
let (mut drag_sum, mut lift_min, mut lift_max, mut samples) =
(0.0, f64::INFINITY, f64::NEG_INFINITY, 0usize);
let mut worst_residual = 0.0_f64;
@@ -283,6 +319,30 @@ fn flag_wake_on_the_device() {
.unwrap();
}
if t >= last_period_start {
use rtx_cfd::solvers::incompressible::embedded3::exchange::set_load_window;
// whole body, the cylinder (x < 0.252), the flag
for (w, window) in [None, Some((0.0, 0.252)), Some((0.252, 10.0))]
.into_iter()
.enumerate()
{
set_load_window(window);
let (po, so) = mask
.cut_wall_force_parts(body, &field, RHO * NU, t)
.expect("parts");
let (xd, xc) = mask
.cut_wall_exchange_parts(body, &field, RHO * NU, RHO, t, None)
.expect("exchange");
let (pr, sr) = mask
.cut_wall_force_reconstructed_parts(body, &field, RHO * NU, t, None)
.expect("reconstructed parts");
for (acc, v) in parts[w]
.iter_mut()
.zip([po[0], so[0], xd[0], xc[0], pr[0], sr[0]])
{
*acc += v / width;
}
}
set_load_window(None);
drag_sum += fs[0];
drag_rec_sum += fr[0];
lift_min = lift_min.min(fs[1]);
@@ -306,6 +366,21 @@ fn flag_wake_on_the_device() {
next_phase,
start.elapsed().as_secs_f64()
);
let n = samples.max(1) as f64;
for (name, q) in ["whole body", "cylinder", "flag"].iter().zip(parts) {
println!(
" PARTS ny {ny} amp {:.3} {name} (x, N/m of width): operator pressure {:.2} + shear {:.2} + exchange diffusive {:.2} + convective {:.2} = {:.2}; reconstructed pressure {:.2} + shear {:.2} = {:.2}",
amplitude(),
q[0] / n,
q[1] / n,
q[2] / n,
q[3] / n,
(q[0] + q[1] + q[2] + q[3]) / n,
q[4] / n,
q[5] / n,
(q[4] + q[5]) / n
);
}
if let Some(t) = device.timers() {
println!(" timers: {t:?}");
}