rtx-cfd embedded3 item 9: wall.rs (binary ghost mask: three face families, trilinear stencil with periodic-z wrap, z-weighted least-squares ghost fit, flux compatibility correction; slip walls allowed as touched sides) + loads.rs (surface-stress route with probes, control-volume route with full-span z faces skipped); the step wired (body/mask, predicates, anchor, ghost re-imposition). Gates HELD: CFD1 ny 41 nz 1 CV 15.6156 / surface 15.7126 both to 1e-6 of the 2D record; nz 4 periodic CV 1.1e-6 / surface 4.2e-4; sphere MMS order 0.89, div 1e-8, ghost correction 1.0e-4 → 2.0e-5, both load routes' errors falling (0.153 → 0.115 surface, 0.214 → 0.139 CV)
CI / Build CPU-Only (Explicit) (push) Failing after 4s
Documentation / Build API Documentation (push) Failing after 4s
Documentation / Build User Guide (push) Successful in 4s
CI / Format Check (push) Failing after 12s
CI / Build (ubuntu-latest) (push) Failing after 1m57s
CI / Clippy Check (push) Failing after 2m20s
Performance Benchmarks / Run Benchmarks (push) Successful in 2m50s
CI / Build (macos-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
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 / Build CPU-Only (Explicit) (push) Failing after 4s
Documentation / Build API Documentation (push) Failing after 4s
Documentation / Build User Guide (push) Successful in 4s
CI / Format Check (push) Failing after 12s
CI / Build (ubuntu-latest) (push) Failing after 1m57s
CI / Clippy Check (push) Failing after 2m20s
Performance Benchmarks / Run Benchmarks (push) Successful in 2m50s
CI / Build (macos-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
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
Co-Authored-By: Claude Fable 5.1 <[email protected]>
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
d4ffac9ac7
commit
d337afa8f9
@@ -10,8 +10,10 @@ mod predictor;
|
||||
mod projection;
|
||||
|
||||
use super::Grid;
|
||||
use super::body::Body;
|
||||
use super::field::Field;
|
||||
use super::poisson::{PcgCache, Problem, solve_pcg_cached};
|
||||
use super::wall::{FaceKind, Mask, WallScheme};
|
||||
use crate::solvers::incompressible::poisson::{
|
||||
MgPrecision, MgSmoother, MultigridParameters, PoissonSolution,
|
||||
};
|
||||
@@ -40,7 +42,7 @@ pub struct Boundaries {
|
||||
}
|
||||
|
||||
impl Boundaries {
|
||||
fn any_outlet(self) -> bool {
|
||||
pub(crate) fn any_outlet(self) -> bool {
|
||||
[self.x0, self.x1, self.y0, self.y1, self.z0, self.z1].contains(&Side::PressureOutlet)
|
||||
}
|
||||
|
||||
@@ -69,6 +71,8 @@ pub struct Parameters {
|
||||
pub convection_scheme: ConvectionScheme,
|
||||
/// Relative part of the pressure solve's inner stop (the 2D 1e-2).
|
||||
pub inner_stop_factor: f64,
|
||||
/// The wall treatment of an embedded body.
|
||||
pub wall_scheme: WallScheme,
|
||||
}
|
||||
|
||||
impl Default for Parameters {
|
||||
@@ -81,6 +85,7 @@ impl Default for Parameters {
|
||||
poisson_precision: MgPrecision::F64,
|
||||
convection_scheme: ConvectionScheme::Upwind,
|
||||
inner_stop_factor: 1e-2,
|
||||
wall_scheme: WallScheme::GhostBinary,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -102,6 +107,9 @@ pub struct Solver {
|
||||
pub params: Parameters,
|
||||
pub(super) momentum_source: Option<Vec3Fn>,
|
||||
boundary_velocity: Option<Vec3Fn>,
|
||||
body: Option<Body>,
|
||||
mask: Option<Mask>,
|
||||
last_ghost_correction: f64,
|
||||
pcg_cache: PcgCache,
|
||||
time: f64,
|
||||
initialized: bool,
|
||||
@@ -126,6 +134,9 @@ impl Solver {
|
||||
params,
|
||||
momentum_source: None,
|
||||
boundary_velocity: None,
|
||||
body: None,
|
||||
mask: None,
|
||||
last_ghost_correction: 0.0,
|
||||
pcg_cache: PcgCache::default(),
|
||||
time: 0.0,
|
||||
initialized: false,
|
||||
@@ -147,6 +158,28 @@ impl Solver {
|
||||
self.boundary_velocity = Some(Box::new(f));
|
||||
}
|
||||
|
||||
/// A static embedded body (the mask is built at initialisation).
|
||||
pub fn set_body(&mut self, body: Body) {
|
||||
self.body = Some(body);
|
||||
self.mask = None;
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn body(&self) -> Option<&Body> {
|
||||
self.body.as_ref()
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn mask(&self) -> Option<&Mask> {
|
||||
self.mask.as_ref()
|
||||
}
|
||||
|
||||
/// The last step's ghost compatibility correction.
|
||||
#[must_use]
|
||||
pub fn ghost_correction(&self) -> f64 {
|
||||
self.last_ghost_correction
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn time(&self) -> f64 {
|
||||
self.time
|
||||
@@ -167,22 +200,30 @@ impl Solver {
|
||||
.map_or((0.0, 0.0, 0.0), |f| f(x, y, z, t))
|
||||
}
|
||||
|
||||
// The fluid predicates: everything is fluid until the wall arrives.
|
||||
// The fluid predicates (everything is fluid without a body).
|
||||
#[inline]
|
||||
fn u_is_fluid(&self, _k: usize, _j: usize, _i: usize) -> bool {
|
||||
true
|
||||
pub(super) fn u_is_fluid(&self, k: usize, j: usize, i: usize) -> bool {
|
||||
self.mask
|
||||
.as_ref()
|
||||
.is_none_or(|m| m.u_kind(m.grid().uface(k, j, i)) == FaceKind::Fluid)
|
||||
}
|
||||
#[inline]
|
||||
fn v_is_fluid(&self, _k: usize, _j: usize, _i: usize) -> bool {
|
||||
true
|
||||
pub(super) fn v_is_fluid(&self, k: usize, j: usize, i: usize) -> bool {
|
||||
self.mask
|
||||
.as_ref()
|
||||
.is_none_or(|m| m.v_kind(m.grid().vface(k, j, i)) == FaceKind::Fluid)
|
||||
}
|
||||
#[inline]
|
||||
fn w_is_fluid(&self, _k: usize, _j: usize, _i: usize) -> bool {
|
||||
true
|
||||
pub(super) fn w_is_fluid(&self, k: usize, j: usize, i: usize) -> bool {
|
||||
self.mask
|
||||
.as_ref()
|
||||
.is_none_or(|m| m.w_kind(m.grid().wface(k, j, i)) == FaceKind::Fluid)
|
||||
}
|
||||
#[inline]
|
||||
fn cell_is_fluid(&self, _k: usize, _j: usize, _i: usize) -> bool {
|
||||
true
|
||||
pub(super) fn cell_is_fluid(&self, k: usize, j: usize, i: usize) -> bool {
|
||||
self.mask
|
||||
.as_ref()
|
||||
.is_none_or(|m| m.is_fluid_cell(m.grid().cell(k, j, i)))
|
||||
}
|
||||
|
||||
pub(super) fn upwind(face_velocity: f64, upstream: f64, downstream: f64) -> f64 {
|
||||
@@ -318,10 +359,27 @@ impl Solver {
|
||||
field.copy_to_starred();
|
||||
}
|
||||
|
||||
/// Stamp the `t = time` boundary data (lazily called by the first step).
|
||||
/// Build the mask (with a body) and stamp the `t = time` boundary data
|
||||
/// and ghost values (lazily called by the first step).
|
||||
pub fn initialize(&mut self, field: &mut Field) {
|
||||
let t = self.time;
|
||||
if let Some(body) = &self.body {
|
||||
if self.mask.is_none() {
|
||||
assert_eq!(
|
||||
self.params.wall_scheme,
|
||||
WallScheme::GhostBinary,
|
||||
"item 10 brings CutCell"
|
||||
);
|
||||
self.mask = Some(
|
||||
Mask::build(body, field.grid, t, self.params.boundaries)
|
||||
.expect("embedded mask"),
|
||||
);
|
||||
}
|
||||
}
|
||||
self.apply_boundary_normals(field, t);
|
||||
if let (Some(body), Some(mask)) = (&self.body, &self.mask) {
|
||||
mask.impose(body, &mut field.u, &mut field.v, &mut field.w, t);
|
||||
}
|
||||
self.initialized = true;
|
||||
}
|
||||
|
||||
@@ -354,6 +412,11 @@ impl Solver {
|
||||
}
|
||||
field.copy_to_starred();
|
||||
}
|
||||
// Ghost faces follow the corrected field (the next step's stencil data).
|
||||
if let (Some(body), Some(mask)) = (&self.body, &self.mask) {
|
||||
self.last_ghost_correction =
|
||||
mask.impose(body, &mut field.u, &mut field.v, &mut field.w, t_new);
|
||||
}
|
||||
self.time = t_new;
|
||||
StepResult {
|
||||
converged: final_residual < self.params.tolerance,
|
||||
|
||||
@@ -93,7 +93,11 @@ impl Solver {
|
||||
/// The anchor cell of a pure-Neumann projection (the first fluid cell,
|
||||
/// the 2D `(1, 1)` at `k = 0`), or `None` with an outlet.
|
||||
pub(crate) fn anchor_cell(&self, g: Grid) -> Option<usize> {
|
||||
(!self.params.boundaries.any_outlet()).then_some(g.cell(0, 1, 1))
|
||||
(!self.params.boundaries.any_outlet()).then(|| {
|
||||
self.mask
|
||||
.as_ref()
|
||||
.map_or(g.cell(0, 1, 1), super::super::wall::Mask::anchor)
|
||||
})
|
||||
}
|
||||
|
||||
/// The inner stop of a projection from the source scale (the 2D rule).
|
||||
|
||||
Reference in New Issue
Block a user