rtx-cfd: fix the cell-centre velocity interpolation, which was half a cell out
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 / Distributed Training Tests (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
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 / Distributed Training Tests (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
`get_velocity_at` averaged u-faces `i - 1` and `i` to report the velocity
at cell `i`. On this staggered layout `u` is `(ny, nx + 1)` and cell `i`
is bounded by faces `i` and `i + 1` -- which is the convention
`compute_mass_source` uses to form the divergence, and therefore the one
that defines the grid. The two disagreed by one index.
Consequences: every profile read through this function was shifted half a
cell west of the field the solver actually computed, the first and last
cells were special-cased to a single face, and the outermost face was
never read at all.
It is a diagnostic path rather than a solve path -- the residuals are
byte-identical before and after -- but the cavity comparison against Ghia
is taken through it, so the reported vortex position was affected. The
corrected grid study, unchanged in the solve:
n u_min y
17^2 -0.1257 0.4375
33^2 -0.1550 0.4688
65^2 -0.1743 0.4844
97^2 -0.1825 0.5000
Ghia -0.2109 0.4531
The shift matters most where the grid is coarse and washes out under
refinement, which is what a half-cell offset should do.
Found while establishing where each staggered variable physically sits, a
prerequisite for applying the method of manufactured solutions to this
solver.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 5
parent
8615fc5783
commit
10e5f9cb90
@@ -110,22 +110,20 @@ impl FlowField {
|
||||
return Err(CfdError::invalid_parameter("Grid indices out of bounds"));
|
||||
}
|
||||
|
||||
// Interpolate velocities to cell center
|
||||
let u_center = if i == 0 {
|
||||
self.u[(j, 0)]
|
||||
} else if i == self.nx - 1 {
|
||||
self.u[(j, self.nx - 1)]
|
||||
} else {
|
||||
0.5 * (self.u[(j, i - 1)] + self.u[(j, i)])
|
||||
};
|
||||
|
||||
let v_center = if j == 0 {
|
||||
self.v[(0, i)]
|
||||
} else if j == self.ny - 1 {
|
||||
self.v[(self.ny - 1, i)]
|
||||
} else {
|
||||
0.5 * (self.v[(j - 1, i)] + self.v[(j, i)])
|
||||
};
|
||||
// Interpolate to the cell centre from the cell's own two faces.
|
||||
//
|
||||
// On this staggered layout `u` is `(ny, nx + 1)` and `v` is
|
||||
// `(ny + 1, nx)`, and cell `i` is bounded by u-faces `i` and `i + 1` —
|
||||
// which is the convention `compute_mass_source` uses to form the
|
||||
// divergence, and therefore the one that defines the grid.
|
||||
//
|
||||
// This previously averaged faces `i - 1` and `i`, half a cell to the
|
||||
// west of the cell it claimed to be reporting, with the first and last
|
||||
// cells special-cased to a single face and the outermost face never
|
||||
// read at all. Every profile taken through this function was shifted
|
||||
// by half a cell against the field the solver actually computed.
|
||||
let u_center = 0.5 * (self.u[(j, i)] + self.u[(j, i + 1)]);
|
||||
let v_center = 0.5 * (self.v[(j, i)] + self.v[(j + 1, i)]);
|
||||
|
||||
Ok((u_center, v_center))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user