rtx-cfd: F2 — the moving embedded body, and falsifier 3 measured
EmbeddedPisoSolver::set_moving_body: the mask is rebuilt at the end-of-step geometry every step, and the new mask's ghost values are reconstructed FROM THE PREVIOUS CORRECTED FIELD (EmbeddedMask:: impose_from — the boundary-history principle extended to a moving wall), so a stationary body run through the moving path is bit-identical to the static path, which is the first test. A velocity face that flips solid -> fluid enters the new interval holding exactly the ghost reconstruction the previous step left on it — a consistent near-wall value, not garbage; a fresh pressure cell is refilled from its fluid neighbours before the predictor's gradient can read the value it kept while inside the body. The body must move under a cell per step (the convective dt limit already enforces this for bodies slower than the local peak velocity). EmbeddedResult reports fresh_cells. tests/embedded_moving.rs: - a stationary body through the moving path: 0.0 difference over 100 steps (and zero fresh cells, identical ghost corrections); - a circle (r = 0.2) translating through the steady manufactured field with the exact field as its surface velocity — the solution must hold still while the mask sweeps 84 cells fresh over 300 steps at n = 32: max L2 velocity error 9.85e-3 = 1.16x the static steady level (8.489e-3), max L2 pressure error 4.67e-2 = 2.11x the static level (2.22e-2), bulk |div u| 1.6e-7, projection residual 5.9e-9 every step. That pressure ratio is the geometry decision's falsifier 3 (omni-cortex docs/turek_hron_geometry_decision.md): fresh-cell transients sit at ~2x the static discretisation error, not orders above it — the falsifier does not fire and no cut cells are needed. Measurement note, recorded in the test: the divergence of body-adjacent cells read after the end-of-step ghost re-imposition is a one-step lag by design (the next projection honours the re-imposed prescribed fluxes — the same lag the static path has); the continuity claims are the projection residual and the bulk divergence over all-fluid-faced cells. Deferred: an oscillating-cylinder benchmark against published force histories (Duetsch et al. 1998) when the FSI rungs need it. rtx-cfd 321 -> 323 green. Co-Authored-By: Claude Fable 5 <[email protected]>
This commit is contained in:
co-authored by
Claude Fable 5
parent
38645c7c74
commit
0ad31abb6b
@@ -525,6 +525,26 @@ impl EmbeddedMask {
|
||||
u: &mut DMatrix<f64>,
|
||||
v: &mut DMatrix<f64>,
|
||||
t: f64,
|
||||
) -> f64 {
|
||||
let (u_source, v_source) = (u.clone(), v.clone());
|
||||
self.impose_from(body, &u_source, &v_source, u, v, t)
|
||||
}
|
||||
|
||||
/// [`Self::impose`] with the fluid values read from a *different* field
|
||||
/// than the one written: the moving-body step reconstructs the new
|
||||
/// mask's ghost values from the previous step's corrected field (the
|
||||
/// boundary-history principle — ghost data, like domain-boundary data,
|
||||
/// is carried by what the previous step left, not by the uncorrected
|
||||
/// predictor state). With `source == target` values this is `impose`.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn impose_from(
|
||||
&self,
|
||||
body: &EmbeddedBody,
|
||||
u_source: &DMatrix<f64>,
|
||||
v_source: &DMatrix<f64>,
|
||||
u: &mut DMatrix<f64>,
|
||||
v: &mut DMatrix<f64>,
|
||||
t: f64,
|
||||
) -> f64 {
|
||||
let (nx, ny, dx, dy) = (self.nx, self.ny, self.dx, self.dy);
|
||||
|
||||
@@ -549,10 +569,18 @@ impl EmbeddedMask {
|
||||
}
|
||||
}
|
||||
|
||||
// Ghost values from the fluid field as it stands (reads only fluid
|
||||
// faces and fallbacks, so order does not matter).
|
||||
let u_vals: Vec<f64> = self.u_ghosts.iter().map(|g| g.reconstruct(u)).collect();
|
||||
let v_vals: Vec<f64> = self.v_ghosts.iter().map(|g| g.reconstruct(v)).collect();
|
||||
// Ghost values from the source fluid field (reads only fluid faces
|
||||
// and fallbacks, so order does not matter).
|
||||
let u_vals: Vec<f64> = self
|
||||
.u_ghosts
|
||||
.iter()
|
||||
.map(|g| g.reconstruct(u_source))
|
||||
.collect();
|
||||
let v_vals: Vec<f64> = self
|
||||
.v_ghosts
|
||||
.iter()
|
||||
.map(|g| g.reconstruct(v_source))
|
||||
.collect();
|
||||
|
||||
// Net outward (from fluid) flux through flux-carrying ghost faces.
|
||||
let mut net = 0.0;
|
||||
|
||||
Reference in New Issue
Block a user