P6-b design 2: the Robin wall on the patch's Inner side — RobinWall { alpha, datum } with the wall velocity u_s + (t_f − datum)/α (explicit, damped by the wall's own viscous gain μ/(α d)) and the compliant-wall pressure term |S| p'/α implicit in the projection (the added-mass operator in the fluid's own response); Dirichlet bit for bit when off; MMS pin on the skewed annulus (orders 2.46/2.13 at α = 10 and 100 μ/h, the Dirichlet values; a 1e12 wall reproduces Dirichlet to 2e-6); OversetPisoSolver::patch_mut; harness knob RTX_FSI2O_ROBIN_ALPHA with the previous pass's tractions as the datum, printed in the header
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 7s
CI / Format Check (push) Failing after 17s
Documentation / Build User Guide (push) Successful in 19s
Documentation / Build API Documentation (push) Failing after 1m51s
CI / Build CPU-Only (Explicit) (push) Failing after 1m58s
CI / Clippy Check (push) Failing after 2m13s
Performance Benchmarks / Run Benchmarks (push) Successful in 2m54s
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 7s
CI / Format Check (push) Failing after 17s
Documentation / Build User Guide (push) Successful in 19s
Documentation / Build API Documentation (push) Failing after 1m51s
CI / Build CPU-Only (Explicit) (push) Failing after 1m58s
CI / Clippy Check (push) Failing after 2m13s
Performance Benchmarks / Run Benchmarks (push) Successful in 2m54s
Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01YJPeT6WA2e7YvAnS875AHL
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
c144a5f733
commit
c3dbd5040a
@@ -6,10 +6,12 @@
|
||||
//! and ≈ 1 with upwind; every step is divergence-free to the solver's
|
||||
//! tolerance.
|
||||
|
||||
use rtx_cfd::mesh::PatchMesh;
|
||||
use rtx_cfd::mesh::patch_gen::{annulus_skewed, cartesian};
|
||||
use rtx_cfd::mesh::PatchMesh;
|
||||
use rtx_cfd::mesh::PatchSide;
|
||||
use rtx_cfd::solvers::incompressible::{
|
||||
CurvilinearParameters, CurvilinearPisoSolver, NormalDiffusion, PatchConvection, PatchField,
|
||||
RobinWall,
|
||||
};
|
||||
use rtx_cfd::{CfdConfig, CfdResult};
|
||||
use std::f64::consts::PI;
|
||||
@@ -54,11 +56,47 @@ fn min_spacing(mesh: &PatchMesh) -> f64 {
|
||||
h
|
||||
}
|
||||
|
||||
/// The manufactured traction on the body per Inner face (the
|
||||
/// `wall_tractions` convention: `S` into the fluid, `(−p S + μ (∇u + ∇uᵀ) S)/|S|`).
|
||||
fn robin_datum(mesh: &PatchMesh) -> Vec<[f64; 2]> {
|
||||
let mut out = Vec::new();
|
||||
for (f, face) in mesh.faces().iter().enumerate() {
|
||||
if mesh.side(f) != Some(PatchSide::Inner) {
|
||||
continue;
|
||||
}
|
||||
let [x, y] = face.centre;
|
||||
let sign = if face.neigh.is_some() { 1.0 } else { -1.0 };
|
||||
let s = [sign * face.s[0], sign * face.s[1]];
|
||||
let len = (s[0] * s[0] + s[1] * s[1]).sqrt();
|
||||
let p = (PI * x).sin() * (PI * y).sin();
|
||||
let ux = PI * (PI * x).cos() * (PI * y).cos();
|
||||
let uy = -PI * (PI * x).sin() * (PI * y).sin();
|
||||
let vx = PI * (PI * x).sin() * (PI * y).sin();
|
||||
let vy = -PI * (PI * x).cos() * (PI * y).cos();
|
||||
let tx = MU * (2.0 * ux * s[0] + (uy + vx) * s[1]);
|
||||
let ty = MU * ((uy + vx) * s[0] + 2.0 * vy * s[1]);
|
||||
out.push([(-p * s[0] + tx) / len, (-p * s[1] + ty) / len]);
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
async fn march(
|
||||
mesh: PatchMesh,
|
||||
convection: PatchConvection,
|
||||
diffusion: NormalDiffusion,
|
||||
steady_tol: f64,
|
||||
) -> CfdResult<Measurement> {
|
||||
march_with(mesh, convection, diffusion, steady_tol, None).await
|
||||
}
|
||||
|
||||
/// `robin_alpha`: put a Robin wall of that impedance on the Inner side with
|
||||
/// the manufactured traction as its datum (P6-b's MMS pin).
|
||||
async fn march_with(
|
||||
mesh: PatchMesh,
|
||||
convection: PatchConvection,
|
||||
diffusion: NormalDiffusion,
|
||||
steady_tol: f64,
|
||||
robin_alpha: Option<f64>,
|
||||
) -> CfdResult<Measurement> {
|
||||
let nu = MU / RHO;
|
||||
let h = min_spacing(&mesh);
|
||||
@@ -85,6 +123,10 @@ async fn march(
|
||||
let mut solver = CurvilinearPisoSolver::new(config, params, mesh)?;
|
||||
solver.set_boundary_velocity(|x, y, _t| (u_exact(x, y), v_exact(x, y)));
|
||||
solver.set_momentum_source(move |x, y, _t| source(x, y, convecting));
|
||||
if let Some(alpha) = robin_alpha {
|
||||
let datum = robin_datum(solver.mesh());
|
||||
solver.set_robin_wall(Some(RobinWall { alpha, datum }));
|
||||
}
|
||||
let mut field = PatchField::new(solver.mesh());
|
||||
solver.initialize(&mut field, |_, _| (0.0, 0.0));
|
||||
|
||||
@@ -320,3 +362,74 @@ async fn snapshot_restore_rerun_is_bit_identical() -> CfdResult<()> {
|
||||
assert!(max_diff == 0.0, "re-run differs by {max_diff:.3e}");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// P6-b's MMS pin: the annulus in the Stokes limit with a Robin wall of
|
||||
/// impedance `alpha` on the Inner side (datum = the manufactured traction)
|
||||
/// keeps the Dirichlet wall's order (≥ 1.8) at two impedances of the
|
||||
/// viscous scale, and an effectively rigid wall (`alpha` = 1e12)
|
||||
/// reproduces the Dirichlet march to rounding.
|
||||
#[tokio::test]
|
||||
async fn skewed_annulus_stokes_with_a_robin_inner_wall_keeps_second_order() -> CfdResult<()> {
|
||||
let steady_tol = 1e-4;
|
||||
let mut dirichlet = Vec::new();
|
||||
for ns in [24, 48, 96] {
|
||||
let m = march(
|
||||
annulus_skewed([0.0, 0.0], 0.5, 1.5, ns, ns / 4, 0.3, 3.0)?,
|
||||
PatchConvection::None,
|
||||
NormalDiffusion::LineImplicit,
|
||||
steady_tol,
|
||||
)
|
||||
.await?;
|
||||
dirichlet.push(m.l2_velocity);
|
||||
}
|
||||
println!("dirichlet L2 {dirichlet:?} orders {:?}", orders(&dirichlet));
|
||||
for &scale in &[10.0, 100.0] {
|
||||
let mut errs = Vec::new();
|
||||
for ns in [24, 48, 96] {
|
||||
let mesh = annulus_skewed([0.0, 0.0], 0.5, 1.5, ns, ns / 4, 0.3, 3.0)?;
|
||||
let h = min_spacing(&mesh);
|
||||
let alpha = scale * MU / h;
|
||||
let m = march_with(
|
||||
mesh,
|
||||
PatchConvection::None,
|
||||
NormalDiffusion::LineImplicit,
|
||||
steady_tol,
|
||||
Some(alpha),
|
||||
)
|
||||
.await?;
|
||||
println!(
|
||||
"robin alpha = {scale} μ/h ns={ns}: L2 {:.6e}, max div {:.2e}, {} steps",
|
||||
m.l2_velocity, m.max_div_rel, m.steps
|
||||
);
|
||||
errs.push(m.l2_velocity);
|
||||
}
|
||||
let o = orders(&errs);
|
||||
println!("robin alpha = {scale} μ/h orders {o:?}");
|
||||
assert!(
|
||||
o.iter().all(|&x| x > 1.8),
|
||||
"Robin ({scale} μ/h) orders {o:?} (gate >= 1.8)"
|
||||
);
|
||||
}
|
||||
let mesh = annulus_skewed([0.0, 0.0], 0.5, 1.5, 48, 12, 0.3, 3.0)?;
|
||||
let rigid = march_with(
|
||||
mesh,
|
||||
PatchConvection::None,
|
||||
NormalDiffusion::LineImplicit,
|
||||
steady_tol,
|
||||
Some(1e12),
|
||||
)
|
||||
.await?;
|
||||
let rel = (rigid.l2_velocity - dirichlet[1]).abs() / dirichlet[1];
|
||||
println!(
|
||||
"rigid Robin (1e12) vs Dirichlet at ns 48: L2 {:.6e} vs {:.6e} (rel {rel:.2e})",
|
||||
rigid.l2_velocity, dirichlet[1]
|
||||
);
|
||||
// 1e-5: the two marches stop at different steps under the 1e-4 steady
|
||||
// tolerance and the Robin path skips the closed-patch flux adjustment
|
||||
// (measured 1.9e-6 at ns 48).
|
||||
assert!(
|
||||
rel < 1e-5,
|
||||
"an effectively rigid Robin wall differs from Dirichlet by {rel:.2e}"
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user