rtx-cfd: curvilinear collocated PISO on a structured patch (overset A-P0, WIP) — PatchMesh (right-handed s,n; periodic seam with shift; face metrics), patch generators (TFI, skewed annulus, sheared/varying-skew channels), CSR + Jacobi-BiCGSTAB, the Zang–Street–Koseff incremental step with the node-based 9-point L_f, LSQ gradients, explicit and line-implicit-n predictors, adjustPhi; tests: mesh metrics (5 green), operators exact on linear fields incl. the seam (green), sparse (2 green), MMS ladder (Cartesian 16/32: 1.37–1.39x the staggered error, order 0.83; n=64 stalls at a |du/dt| floor 2e-4 — open, tolerance-scaling hypothesis), annulus/Poiseuille not yet run
CI / Distributed Training Tests (push) Canceled after 0s
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 / CI Success (push) Canceled after 0s
Documentation / Build API Documentation (push) Canceled after 0s
Documentation / Build User Guide (push) Canceled after 0s
CI / Distributed Training Tests (push) Canceled after 0s
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 / CI Success (push) Canceled after 0s
Documentation / Build API Documentation (push) Canceled after 0s
Documentation / Build User Guide (push) Canceled after 0s
Co-Authored-By: Claude Fable 5.1 <[email protected]> Claude-Session: https://claude.ai/code/session_01X2GmJXeQ2njUecEKiJZ1G2
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
1347bc6772
commit
52da75a3a9
@@ -0,0 +1,88 @@
|
||||
//! P0 step 2: the operators are exact on linear fields over a skewed,
|
||||
//! stretched, periodic annulus — nodal reconstruction at interior nodes,
|
||||
//! the least-squares cell gradient, and the face operator `L_f`
|
||||
//! (seam faces included).
|
||||
|
||||
use rtx_cfd::mesh::patch_gen::annulus_skewed;
|
||||
use rtx_cfd::solvers::incompressible::{Operators, PatchBoundaries};
|
||||
|
||||
#[test]
|
||||
fn nodes_gradients_and_face_operator_are_exact_on_linear_fields() {
|
||||
let mesh = annulus_skewed([0.2, -0.1], 0.5, 1.5, 32, 8, 0.3, 3.0).unwrap();
|
||||
let ops = Operators::new(&mesh, &PatchBoundaries::default());
|
||||
let (a, b, c) = (0.7, -1.3, 2.1);
|
||||
let phi = |x: f64, y: f64| a + b * x + c * y;
|
||||
let cell_vals: Vec<f64> = (0..mesh.cell_count())
|
||||
.map(|i| {
|
||||
let xy = mesh.centre(i);
|
||||
phi(xy[0], xy[1])
|
||||
})
|
||||
.collect();
|
||||
|
||||
// Interior nodes (rows 1..nn-1, every column including the seam).
|
||||
let nodes = ops.node_values(&mesh, &cell_vals, &|_, _| None);
|
||||
let mut worst_node = 0.0_f64;
|
||||
for k in 1..mesh.nn() {
|
||||
for i in 0..=mesh.ns() {
|
||||
let n = mesh.node(k, i);
|
||||
let xy = mesh.node_xy(n);
|
||||
worst_node = worst_node.max((nodes[n] - phi(xy[0], xy[1])).abs());
|
||||
}
|
||||
}
|
||||
assert!(
|
||||
worst_node < 1e-13,
|
||||
"interior node reconstruction error {worst_node:.3e}"
|
||||
);
|
||||
|
||||
// Cell gradients with the boundary faces at their exact values.
|
||||
let mut worst_grad = 0.0_f64;
|
||||
for cell in 0..mesh.cell_count() {
|
||||
let g = ops.gradient(&mesh, cell, &cell_vals, &|f| {
|
||||
let xy = mesh.faces()[f].centre;
|
||||
Some(phi(xy[0], xy[1]))
|
||||
});
|
||||
worst_grad = worst_grad.max((g[0] - b).abs()).max((g[1] - c).abs());
|
||||
}
|
||||
assert!(worst_grad < 1e-12, "gradient error {worst_grad:.3e}");
|
||||
|
||||
// L_f on interior faces with exact node values: (∇φ)·S = b S_x + c S_y.
|
||||
let exact_nodes: Vec<f64> = (0..ops.nodes().len())
|
||||
.map(|n| {
|
||||
let xy = mesh.node_xy(n);
|
||||
phi(xy[0], xy[1])
|
||||
})
|
||||
.collect();
|
||||
let mut worst_face = 0.0_f64;
|
||||
let mut seam_checked = 0;
|
||||
for (f, face) in mesh.faces().iter().enumerate() {
|
||||
if face.owner.is_none() || face.neigh.is_none() {
|
||||
continue;
|
||||
}
|
||||
let lf = ops.face_gradient_flux(&mesh, f, &cell_vals, &exact_nodes, None);
|
||||
let exact = b * face.s[0] + c * face.s[1];
|
||||
let scale = (face.s[0] * face.s[0] + face.s[1] * face.s[1]).sqrt() * (b.abs() + c.abs());
|
||||
worst_face = worst_face.max((lf - exact).abs() / scale);
|
||||
if mesh.is_sface(f) && f % mesh.sfaces_per_row() == 0 {
|
||||
seam_checked += 1;
|
||||
}
|
||||
}
|
||||
assert_eq!(seam_checked, mesh.nn());
|
||||
assert!(worst_face < 1e-13, "face operator error {worst_face:.3e}");
|
||||
|
||||
// With the reconstructed (not exact) node values the interior faces are
|
||||
// still exact, since the reconstruction is.
|
||||
let mut worst_face2 = 0.0_f64;
|
||||
for k in 1..mesh.nn() - 1 {
|
||||
for i in 0..mesh.ns() {
|
||||
let f = mesh.sface(k, i);
|
||||
let face = &mesh.faces()[f];
|
||||
let lf = ops.face_gradient_flux(&mesh, f, &cell_vals, &nodes, None);
|
||||
let exact = b * face.s[0] + c * face.s[1];
|
||||
worst_face2 = worst_face2.max((lf - exact).abs());
|
||||
}
|
||||
}
|
||||
assert!(
|
||||
worst_face2 < 1e-12,
|
||||
"face operator with reconstructed nodes {worst_face2:.3e}"
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user