Files
rustytorch/crates/specialized/rtx-cfd/tests/embedded3_embedded_mms.rs
T
Omar SobhandClaude Fable 5.1 b34f6ad966
CI / Python Bindings (maturin) (ubuntu-latest) (push) Blocked by required conditions
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 / Build (macos-latest) (push) Waiting to run
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
Performance Benchmarks / Run Benchmarks (push) Failing after 4s
CI / Build (ubuntu-latest) (push) Failing after 3s
CI / Format Check (push) Failing after 3s
CI / Clippy Check (push) Failing after 3s
Documentation / Build User Guide (push) Successful in 5s
Documentation / Build API Documentation (push) Failing after 14s
CI / Build CPU-Only (Explicit) (push) Failing after 54s
embedded3 S2-1 remedy: the reconstructed wall route on the cut geometry's polygons (two probes along the interpolant normal) — sphere MMS 11.3/8.3 % (the best route); wired into the DFG and flag drivers
Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-17 23:18:21 -05:00

159 lines
5.4 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! embedded3 gates 9a and 10: the manufactured solution with an embedded
//! sphere (centre (0.6, 0.45, 0.5), r 0.2, off-centre so the exact force is
//! not zero by symmetry) carrying the exact field as its surface velocity,
//! on the binary ghost wall (item 9) and the apertured cut-cell wall (item
//! 10). The velocity error falls at the scheme's order, every fluid cell
//! is divergence-free (apertured, with the porous surface's flux, on the
//! cut wall), the compatibility correction shrinks, and both load routes
//! converge to the exact surface integral of the manufactured stress (the
//! control-volume route measures F M with M the momentum flux through
//! the porous manufactured surface). Item 10's gate: the cut wall's errors
//! are at most the binary wall's at every n, its loads within 10 % at the
//! finest rung.
mod embedded3_sphere;
use embedded3_sphere::{C, Measurement, exact_force_and_flux, measure};
use rtx_cfd::solvers::incompressible::embedded3::WallScheme;
fn norm(a: [f64; 3]) -> f64 {
(a[0] * a[0] + a[1] * a[1] + a[2] * a[2]).sqrt()
}
/// The velocity errors and the two routes' relative force errors per rung.
struct Ladder {
errors: Vec<f64>,
surface: Vec<f64>,
cv: Vec<f64>,
}
fn ladder(resolutions: &[usize], scheme: WallScheme) -> Ladder {
let (fe, m) = exact_force_and_flux(C);
let f_scale = norm(fe);
let fcv = [fe[0] - m[0], fe[1] - m[1], fe[2] - m[2]];
println!(
" {scheme:?}: exact force {fe:.5?}; momentum flux {m:.5?}; the control-volume route measures {fcv:.5?}"
);
let ms: Vec<Measurement> = resolutions.iter().map(|&n| measure(n, scheme, C)).collect();
let errors: Vec<f64> = ms.iter().map(|x| x.l2_velocity).collect();
let mut se = Vec::new();
let mut ce = Vec::new();
for (k, (mm, &n)) in ms.iter().zip(resolutions).enumerate() {
let rate = if k == 0 {
" -".to_string()
} else {
format!("{:5.2}", (errors[k - 1] / errors[k]).log2())
};
let s = norm([
mm.force_surface[0] - fe[0],
mm.force_surface[1] - fe[1],
mm.force_surface[2] - fe[2],
]) / f_scale;
let c = norm([
mm.force_cv[0] - fcv[0],
mm.force_cv[1] - fcv[1],
mm.force_cv[2] - fcv[2],
]) / f_scale;
let a = norm([
mm.force_sampler[0] - fe[0],
mm.force_sampler[1] - fe[1],
mm.force_sampler[2] - fe[2],
]) / f_scale;
println!(
" n = {n:3} L2 u {:.4e} (order {rate}) max div {:.2e} ghost corr {:.2e} F_surface {:.4?} rel {s:.3e} (skipped {}) F_cv {:.4?} rel {c:.3e} F_reconstructed rel {a:.3e}",
mm.l2_velocity,
mm.max_div,
mm.ghost_correction,
mm.force_surface,
mm.skipped,
mm.force_cv
);
if scheme == WallScheme::CutCell {
// S2-1 (reported, not gated): the traction sampler on the cut
// field against the box route — 1.15× at n 24 on the first read
// (`docs/embedded3_campaign.md`, S2-1).
println!(
" sampler / box error ratio at n = {n}: {:.3}",
a / c.max(1e-300)
);
}
se.push(s);
ce.push(c);
}
assert!(
errors.windows(2).all(|w| w[1] < w[0]),
"errors not monotone {errors:?}"
);
for w in errors.windows(2) {
let rate = (w[0] / w[1]).log2();
assert!(
rate > 0.75 && rate < 2.3,
"order {rate:.3} outside [0.75, 2.3]"
);
}
for mm in &ms {
assert!(mm.max_div < 1e-5, "max div {:.3e}", mm.max_div);
}
assert!(
se.windows(2).all(|w| w[1] < w[0]),
"surface-route error not falling {se:?}"
);
assert!(
ce.windows(2).all(|w| w[1] < w[0]),
"control-volume-route error not falling {ce:?}"
);
Ladder {
errors,
surface: se,
cv: ce,
}
}
/// Item 10's comparison: the cut wall's velocity error at most the binary
/// wall's at every rung; both routes within `load_bound` at the finest.
fn compare(resolutions: &[usize], load_bound: f64) {
let ghost = ladder(resolutions, WallScheme::GhostBinary);
let cut = ladder(resolutions, WallScheme::CutCell);
for (k, &n) in resolutions.iter().enumerate() {
println!(
" n = {n:3} L2 u ghost {:.4e} cut {:.4e} (ratio {:.3})",
ghost.errors[k],
cut.errors[k],
cut.errors[k] / ghost.errors[k]
);
assert!(
cut.errors[k] <= ghost.errors[k],
"cut-cell error above the binary wall's at n = {n}"
);
}
let last = resolutions.len() - 1;
assert!(
cut.surface[last] < load_bound && cut.cv[last] < load_bound,
"cut-cell loads at the finest rung: surface {:.3e}, control volume {:.3e} (bound {load_bound})",
cut.surface[last],
cut.cv[last]
);
}
#[test]
fn embedded_sphere_recovers_the_manufactured_solution() {
ladder(&[12, 24], WallScheme::GhostBinary);
}
#[test]
fn cut_cell_wall_recovers_the_manufactured_solution() {
compare(&[12, 24], 0.2);
}
#[test]
#[ignore = "the three-rung ladder to n = 48 (minutes on the host)"]
fn embedded_sphere_three_rungs() {
ladder(&[12, 24, 48], WallScheme::GhostBinary);
}
#[test]
#[ignore = "item 10's finest rung: the cut wall's loads within 10 % at n = 48"]
fn cut_cell_three_rungs() {
compare(&[12, 24, 48], 0.1);
}