overset mesh: the flat-tip outline (P5-3 option B) — cylinder_flag_outline_deformed_tip / cylinder_flag_patch_deformed{,_from}_tip take the tip's corner radius (t = the recorded semicircle, bit for bit; smaller = the benchmark's flat face between corner arcs sampled at the wall spacing, point count fixed across the deformation, the tip's corners kept off the hull source); pins: corner t reproduces the recorded outline, the 2.5 mm outline has the exact area excess with no collapsed spacing, the O-grid builds straight and at ± 80 mm cold and warm at ny 41/62; harness knob RTX_FSI2O_TIP_CORNER (default 0.01) on the cold and warm builds, printed in both headers; pinned-toolchain fmt on touched crates
Performance Benchmarks / Run Benchmarks (push) Failing after 10s
CI / Format Check (push) Failing after 10s
CI / Build (ubuntu-latest) (push) Failing after 10s
CI / Clippy Check (push) Failing after 10s
Documentation / Build User Guide (push) Successful in 9s
CI / Build CPU-Only (Explicit) (push) Failing after 1m13s
Documentation / Build API Documentation (push) Failing after 2m25s
CI / Build (macos-latest) (push) Canceled after 0s
CI / Test (macos-latest) (push) Canceled after 0s
CI / Test (ubuntu-latest) (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

Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01LzcjQX7tvgn87CQCyg9Cfr
This commit is contained in:
Omar Sobh
2026-09-13 22:22:08 -05:00
co-authored by Claude Fable 5.1
parent 7224521309
commit 415c32ab04
12 changed files with 519 additions and 71 deletions
+156 -13
View File
@@ -667,8 +667,83 @@ pub fn cylinder_flag_patch_deformed_from(
stretch: f64,
winslow_sweeps: usize,
) -> CfdResult<(PatchMesh, (usize, f64))> {
let (inner, tip) =
cylinder_flag_outline_deformed(centre, r, t, edges, x_tip_ref, fillet, 3, 16, h, 1.15);
cylinder_flag_patch_deformed_from_tip(
prev,
centre,
r,
t,
edges,
x_tip_ref,
h,
fillet,
offset,
nn,
stretch,
winslow_sweeps,
t,
)
}
/// [`cylinder_flag_patch_deformed`] with the flat tip of
/// [`cylinder_flag_outline_deformed_tip`] (`tip_corner = t` is the recorded
/// patch).
#[allow(clippy::too_many_arguments)]
pub fn cylinder_flag_patch_deformed_tip(
centre: [f64; 2],
r: f64,
t: f64,
edges: &FlagEdges,
x_tip_ref: f64,
h: f64,
fillet: f64,
offset: f64,
nn: usize,
stretch: f64,
winslow_sweeps: usize,
tip_corner: f64,
) -> CfdResult<(PatchMesh, (usize, f64))> {
cylinder_flag_patch_deformed_from_tip(
None,
centre,
r,
t,
edges,
x_tip_ref,
h,
fillet,
offset,
nn,
stretch,
winslow_sweeps,
tip_corner,
)
}
/// [`cylinder_flag_patch_deformed_from`] with the flat tip.
#[allow(clippy::too_many_arguments)]
pub fn cylinder_flag_patch_deformed_from_tip(
prev: Option<&PatchMesh>,
centre: [f64; 2],
r: f64,
t: f64,
edges: &FlagEdges,
x_tip_ref: f64,
h: f64,
fillet: f64,
offset: f64,
nn: usize,
stretch: f64,
winslow_sweeps: usize,
tip_corner: f64,
) -> CfdResult<(PatchMesh, (usize, f64))> {
let (inner, tip) = cylinder_flag_outline_deformed_tip(
centre, r, t, edges, x_tip_ref, fillet, 3, 16, h, 1.15, tip_corner,
);
// With a flat tip the outline's corners would put 90° corners on the
// hull, whose normal offset folds the rays at the tip; the tip disc
// (already a hull source) covers the tip, so the inner points ahead of
// the disc's centre plane are left out of the hull source.
let flat = tip_corner < tip.radius - 1e-12;
// The hull source carries the deformed outline itself: for a straight
// flag the cylinder and the tip disc alone give the flag's surfaces as
// the hull's tangents, for a BENT flag they give the chord under the
@@ -676,7 +751,9 @@ pub fn cylinder_flag_patch_deformed_from(
// where 6 h was asked, the acceptors' donors reaching into the fringe
// (P5-2's first death).
let mut hull_src = hull_source(centre, r, tip.centre, tip.radius, tip.axis);
hull_src.extend(inner.iter().copied());
hull_src.extend(inner.iter().copied().filter(|p| {
!flat || (p[0] - tip.centre[0]) * tip.axis[0] + (p[1] - tip.centre[1]) * tip.axis[1] < 0.0
}));
let start = prev.and_then(|m| {
(m.ns() == inner.len() && m.nn() == nn).then(|| {
(0..=nn)
@@ -767,11 +844,7 @@ fn arclength_at_x(pts: &[[f64; 2]], cum: &[f64], x0: f64, from_end: bool) -> f64
return cum[i] + f * (cum[i + 1] - cum[i]);
}
}
if from_end {
cum[n - 1]
} else {
0.0
}
if from_end { cum[n - 1] } else { 0.0 }
}
/// Points at the arclength fractions `fr` (`0 ..= 1`) along an open
@@ -810,6 +883,33 @@ pub fn cylinder_flag_outline_deformed(
k_tip: usize,
d_straight: f64,
grade: f64,
) -> (Vec<[f64; 2]>, TipArc) {
cylinder_flag_outline_deformed_tip(
centre, r, t, edges, x_tip_ref, fillet, k_fillet, k_tip, d_straight, grade, t,
)
}
/// [`cylinder_flag_outline_deformed`] with the tip as the benchmark's FLAT
/// face between two corner arcs of radius `tip_corner` (P5-3 option B):
/// `tip_corner = t` is the recorded semicircle, bit for bit; smaller
/// corners keep the along-wall spacing `d0` (the corner arcs and the flat
/// face are sampled at it, so the fluid step does not collapse) and a
/// point count fixed across the deformation (the flat face's count comes
/// from the UNDEFORMED thickness). The returned [`TipArc`] is still the
/// tip disc of radius half the tip edge — the hull source's shape.
#[allow(clippy::too_many_arguments)]
pub fn cylinder_flag_outline_deformed_tip(
centre: [f64; 2],
r: f64,
t: f64,
edges: &FlagEdges,
x_tip_ref: f64,
fillet: f64,
k_fillet: usize,
k_tip: usize,
d_straight: f64,
grade: f64,
tip_corner: f64,
) -> (Vec<[f64; 2]>, TipArc) {
let (cx, cy) = (centre[0], centre[1]);
// Root junction, as the rigid outline.
@@ -852,9 +952,31 @@ pub fn cylinder_flag_outline_deformed(
let mut pts: Vec<[f64; 2]> = Vec::new();
// 1. Top edge: from the arc's top end toward the root, along the top
// polyline (given tip → root) from arclength `radius` to x = x_f.
// The corner radius: `radius` (the semicircle) or the flat tip's.
let rc = tip_corner.min(radius).max(0.0);
let flat = rc < radius - 1e-12;
// Corner arc centres and the tangent points on the faces.
let c_top = [
tc[0] - rc * axis[0] - rc * normal[0],
tc[1] - rc * axis[1] - rc * normal[1],
];
let c_bot = [
b[0] - rc * axis[0] + rc * normal[0],
b[1] - rc * axis[1] + rc * normal[1],
];
let (start_top, start_bot, s_top0, s_bot1) = if flat {
(
[c_top[0] + rc * normal[0], c_top[1] + rc * normal[1]],
[c_bot[0] - rc * normal[0], c_bot[1] - rc * normal[1]],
rc,
rc,
)
} else {
(start_top, start_bot, radius, radius)
};
let cum_t = open_cum(top);
let s_root_t = arclength_at_x(top, &cum_t, x_f, true);
let mut top_run = open_slice(top, &cum_t, radius, s_root_t);
let mut top_run = open_slice(top, &cum_t, s_top0, s_root_t);
top_run[0] = start_top;
pts.extend(along_fractions(&top_run, &fr));
// 2. Top fillet, 3. cylinder arc, 4. bottom fillet — the rigid construction.
@@ -885,13 +1007,34 @@ pub fn cylinder_flag_outline_deformed(
// to `radius` short of the corner, ending at the arc's bottom end.
let cum_b = open_cum(bottom);
let s_root_b = arclength_at_x(bottom, &cum_b, x_f, false);
let mut bot_run = open_slice(bottom, &cum_b, s_root_b, cum_b[nb - 1] - radius);
let mut bot_run = open_slice(bottom, &cum_b, s_root_b, cum_b[nb - 1] - s_bot1);
let last = bot_run.len() - 1;
bot_run[last] = start_bot;
pts.extend(along_fractions(&bot_run, &fr));
// 6. Tip semicircle from the bottom end through the apex to the top end.
let a0 = (start_bot[1] - tip_c[1]).atan2(start_bot[0] - tip_c[0]);
pts.extend(arc_points(tip_c, radius, a0, a0 + PI, k_tip));
if flat {
// 6'. Bottom corner arc (from the bottom face to the tip face), the
// flat tip face, the top corner arc (to the top face, whose start
// point opens the outline — excluded here). Counts from the
// UNDEFORMED thickness so the topology is fixed.
let k_c = ((PI * rc / 2.0) / d0).round().max(2.0) as usize;
let k_f = ((2.0 * (t - rc)) / d0).round().max(1.0) as usize;
let a_b0 = (-normal[1]).atan2(-normal[0]);
pts.extend(arc_points(c_bot, rc, a_b0, a_b0 + PI / 2.0, k_c));
let (p0, p1) = (
[c_bot[0] + rc * axis[0], c_bot[1] + rc * axis[1]],
[c_top[0] + rc * axis[0], c_top[1] + rc * axis[1]],
);
for m in 0..k_f {
let f = m as f64 / k_f as f64;
pts.push([p0[0] + f * (p1[0] - p0[0]), p0[1] + f * (p1[1] - p0[1])]);
}
let a_t0 = axis[1].atan2(axis[0]);
pts.extend(arc_points(c_top, rc, a_t0, a_t0 + PI / 2.0, k_c));
} else {
// 6. Tip semicircle from the bottom end through the apex to the top end.
let a0 = (start_bot[1] - tip_c[1]).atan2(start_bot[0] - tip_c[0]);
pts.extend(arc_points(tip_c, radius, a0, a0 + PI, k_tip));
}
(
pts,
TipArc {
@@ -41,7 +41,7 @@ mod projection;
use super::ale::{AleBoundaries, SideBoundary};
use super::embedded_body::{EmbeddedBody, EmbeddedMask, FaceKind};
use super::poisson::{
solve_multigrid_pcg, MgPrecision, MultigridParameters, PoissonProblem, PoissonSolverKind,
MgPrecision, MultigridParameters, PoissonProblem, PoissonSolverKind, solve_multigrid_pcg,
};
use super::simple::ConvectionScheme;
use super::{FlowField, SolverResult};
@@ -53,8 +53,8 @@ pub use curvilinear::{
};
pub use embedded::{EmbeddedParameters, EmbeddedPisoSolver, EmbeddedResult, EmbeddedSolverState};
pub use embedded_body::{
polygon_interface_velocity, polygon_signed_distance, EmbeddedBody, EmbeddedMask, FaceKind,
SurfaceForce, SurfaceSample,
EmbeddedBody, EmbeddedMask, FaceKind, SurfaceForce, SurfaceSample, polygon_interface_velocity,
polygon_signed_distance,
};
pub use flow_field::FlowField;
pub use overset::{