embedded3 S2-5: the cut wall sat ½(1−α)h inside the body — cross diffusion over the open-part centroid spacing (RTX_E3_DIFFUSION_CENTROID; host + e3_cut.cu, shift tables, point-implicit excess); flat-wall effective-position instrument; DFG 2D-1 ladder tests (device + host); knobs tried and refuted along the way (oblique distance, axis exchange, centroid pressure gradient)
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 / Python Bindings (maturin) (ubuntu-latest) (push) Blocked by required conditions
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
CI / Build (macos-latest) (push) Waiting to run
Documentation / Build API Documentation (push) Failing after 4s
Documentation / Build User Guide (push) Successful in 4s
CI / Clippy Check (push) Failing after 2m24s
CI / Build CPU-Only (Explicit) (push) Failing after 3s
CI / Format Check (push) Failing after 11s
CI / Build (ubuntu-latest) (push) Failing after 1m58s
Performance Benchmarks / Run Benchmarks (push) Successful in 2m44s
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 / Python Bindings (maturin) (ubuntu-latest) (push) Blocked by required conditions
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
CI / Build (macos-latest) (push) Waiting to run
Documentation / Build API Documentation (push) Failing after 4s
Documentation / Build User Guide (push) Successful in 4s
CI / Clippy Check (push) Failing after 2m24s
CI / Build CPU-Only (Explicit) (push) Failing after 3s
CI / Format Check (push) Failing after 11s
CI / Build (ubuntu-latest) (push) Failing after 1m58s
Performance Benchmarks / Run Benchmarks (push) Successful in 2m44s
Co-Authored-By: Claude Fable 5.1 <[email protected]>
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
4c3e58fa27
commit
fdfb6da769
+49
-3
@@ -117,6 +117,9 @@ impl Solver {
|
||||
let mut mass_out = 0.0;
|
||||
let mut conv = 0.0;
|
||||
let mut diff = 0.0;
|
||||
let shift0 = mask.face_shift(c, p);
|
||||
// The implicit exchange with solid neighbour faces (S2-5).
|
||||
let (mut wall_implicit, mut wall_rhs) = (0.0, 0.0);
|
||||
for d in 0..3 {
|
||||
let ed = e(d);
|
||||
let a_d = area[d];
|
||||
@@ -187,7 +190,32 @@ impl Solver {
|
||||
conv += m_plus * (u_plus + delta_plus) - m_minus * (u_minus + delta_minus);
|
||||
// Diffusion through the plus / minus faces.
|
||||
let (g_minus, g_plus) = (cv.ap[d][0], cv.ap[d][1]);
|
||||
// The cross-direction spacing between open-part centroids (S2-5).
|
||||
let centroid = d != c && mask.diffusion_centroid;
|
||||
let spacing = |q: [i64; 3], sign: f64| -> f64 {
|
||||
let delta = h[d] + sign * (mask.face_shift(c, q)[d] - shift0[d]);
|
||||
delta.clamp(0.25 * h[d], 2.0 * h[d])
|
||||
};
|
||||
let solid = |q: [i64; 3]| mask.wall_exchange_axis && ap(c, q) == Some(0.0);
|
||||
diff += match up1 {
|
||||
Some(un) if solid(add(p, ed, 1)) => {
|
||||
let k = mu * g_plus * a_d / mask.exchange_delta(&cv, d);
|
||||
wall_implicit += k;
|
||||
wall_rhs += k * un;
|
||||
0.0
|
||||
}
|
||||
Some(un) if centroid => {
|
||||
// The part of the centroid coupling beyond `1/h` is taken
|
||||
// point-implicitly (the explicit limit is `h`'s).
|
||||
let k = mu * g_plus * a_d * (1.0 / spacing(add(p, ed, 1), 1.0) - 1.0 / h[d]);
|
||||
if k > 0.0 {
|
||||
wall_implicit += k;
|
||||
wall_rhs += k * un;
|
||||
mu * g_plus * a_d * (un - u0) / h[d]
|
||||
} else {
|
||||
(mu * g_plus * a_d / h[d] + k) * (un - u0)
|
||||
}
|
||||
}
|
||||
Some(un) => mu * g_plus * a_d * (un - u0) / h[d],
|
||||
None => {
|
||||
if sides[d][1] == Side::Velocity {
|
||||
@@ -198,6 +226,22 @@ impl Solver {
|
||||
}
|
||||
};
|
||||
diff -= match dn1 {
|
||||
Some(ud) if solid(add(p, ed, -1)) => {
|
||||
let k = mu * g_minus * a_d / mask.exchange_delta(&cv, d);
|
||||
wall_implicit += k;
|
||||
wall_rhs += k * ud;
|
||||
0.0
|
||||
}
|
||||
Some(ud) if centroid => {
|
||||
let k = mu * g_minus * a_d * (1.0 / spacing(add(p, ed, -1), -1.0) - 1.0 / h[d]);
|
||||
if k > 0.0 {
|
||||
wall_implicit += k;
|
||||
wall_rhs += k * ud;
|
||||
mu * g_minus * a_d * (u0 - ud) / h[d]
|
||||
} else {
|
||||
(mu * g_minus * a_d / h[d] + k) * (u0 - ud)
|
||||
}
|
||||
}
|
||||
Some(ud) => mu * g_minus * a_d * (u0 - ud) / h[d],
|
||||
None => {
|
||||
if sides[d][0] == Side::Velocity {
|
||||
@@ -218,7 +262,8 @@ impl Solver {
|
||||
let conv = rho * (conv - mass_out * u0);
|
||||
let p_plus = lat.cell(cell_plus).map_or(0.0, |ci| field.p[ci]);
|
||||
let p_minus = lat.cell(cell_minus).map_or(0.0, |ci| field.p[ci]);
|
||||
let pressure = -(p_plus - p_minus) * cv.alpha * area[c];
|
||||
let idx_f = lat.face(c, p).expect("the face");
|
||||
let pressure = -(p_plus - p_minus) * cv.alpha * area[c] * mask.grad_weight(c, idx_f);
|
||||
let v_u = cv.alpha * h[c] * area[c];
|
||||
let source = self.momentum_source.as_ref().map_or(0.0, |f| {
|
||||
let s = f(x[0], x[1], x[2], t_old);
|
||||
@@ -238,8 +283,9 @@ impl Solver {
|
||||
};
|
||||
let v_eff = fraction.max(INERTIA_FLOOR) * h[c] * area[c];
|
||||
let inertia = rho * v_eff / dt;
|
||||
let u_star = (inertia * u0 - conv + diff + pressure + source + shear * ub - shear_explicit)
|
||||
/ (inertia + shear);
|
||||
let u_star = (inertia * u0 - conv + diff + pressure + source + shear * ub - shear_explicit
|
||||
+ wall_rhs)
|
||||
/ (inertia + shear + wall_implicit);
|
||||
let v_alpha = fraction * h[c] * area[c];
|
||||
(u_star, rho * (v_eff - v_alpha) * (u_star - u0) / dt)
|
||||
}
|
||||
|
||||
@@ -163,6 +163,10 @@ impl DeviceStep {
|
||||
/// Allocates the device fields for `grid`; the momentum source is
|
||||
/// tabulated at `t = 0` (steady sources only in Stage 1).
|
||||
pub fn new(solver: Solver, grid: Grid) -> Self {
|
||||
assert!(
|
||||
!solver.params.pressure_centroid,
|
||||
"the centroid pressure gradient (S2-5) is a host prototype: the device kernels do not carry it"
|
||||
);
|
||||
let rt = runtime();
|
||||
let nu = (grid.nx + 1) * grid.ny * grid.nz;
|
||||
let nv = grid.nx * (grid.ny + 1) * grid.nz;
|
||||
@@ -284,7 +288,11 @@ impl DeviceStep {
|
||||
bz0: side_code(b.z0),
|
||||
bz1: side_code(b.z1),
|
||||
scheme: scheme_code(self.solver.params.convection_scheme),
|
||||
wall_order: i32::from(self.solver.params.wall_order),
|
||||
// Low 4 bits: the shear closure's order; bit 4: the oblique wall distance.
|
||||
wall_order: i32::from(self.solver.params.wall_order)
|
||||
+ 16 * i32::from(self.solver.params.wall_distance_oblique)
|
||||
+ 32 * i32::from(self.solver.params.wall_exchange_axis)
|
||||
+ 64 * i32::from(self.solver.params.diffusion_centroid),
|
||||
dx: g.dx,
|
||||
dy: g.dy,
|
||||
dz: g.dz,
|
||||
|
||||
@@ -51,11 +51,11 @@ fn cut_kernels() -> &'static CutKernels {
|
||||
})
|
||||
}
|
||||
|
||||
/// `struct E3Cut` in e3_cut.cu: 18 device pointers.
|
||||
/// `struct E3Cut` in e3_cut.cu: 21 device pointers.
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy)]
|
||||
struct E3CutPtrs {
|
||||
ptrs: [u64; 18],
|
||||
ptrs: [u64; 21],
|
||||
}
|
||||
unsafe impl DeviceRepr for E3CutPtrs {}
|
||||
unsafe impl ValidAsZeroBits for E3CutPtrs {}
|
||||
@@ -74,6 +74,8 @@ pub(super) struct DeviceCut {
|
||||
fold_ptr: CudaSlice<u32>,
|
||||
fold_idx: CudaSlice<u32>,
|
||||
cell_flux: CudaSlice<f64>,
|
||||
/// The open-part centroid shifts per face (S2-5; one dummy entry when off).
|
||||
shift: [CudaSlice<f64>; 3],
|
||||
pub(super) merged: usize,
|
||||
}
|
||||
|
||||
@@ -228,6 +230,10 @@ impl DeviceCut {
|
||||
fold_ptr: up_u(&fold_ptr),
|
||||
fold_idx: up_u(&fold_idx),
|
||||
cell_flux: rt.stream.alloc_zeros::<f64>(nc).expect("alloc"),
|
||||
shift: match mask.face_shift_tables() {
|
||||
Some(t) => [up_f(&t[0]), up_f(&t[1]), up_f(&t[2])],
|
||||
None => [up_f(&[0.0]), up_f(&[0.0]), up_f(&[0.0])],
|
||||
},
|
||||
merged,
|
||||
})
|
||||
}
|
||||
@@ -258,6 +264,9 @@ impl DeviceCut {
|
||||
pu(&self.fold_ptr),
|
||||
pu(&self.fold_idx),
|
||||
pf(&self.cell_flux),
|
||||
pf(&self.shift[0]),
|
||||
pf(&self.shift[1]),
|
||||
pf(&self.shift[2]),
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,6 +96,33 @@ pub struct Parameters {
|
||||
/// next open face along the wall normal's dominant axis (S2-4).
|
||||
/// `Parameters::default()` reads `RTX_E3_WALL_ORDER` (default 1).
|
||||
pub wall_order: u8,
|
||||
/// The cut face's wall distance with the wall's obliquity: `φ + ½h(1 −
|
||||
/// α)|n_t|` (`n_t` the wall normal's part in the face plane) instead
|
||||
/// of `φ + ½h(1 − α)`, which over-reads the distance on oblique walls
|
||||
/// by `½h(1 − α)(1 − |n_t|)` at every h (S2-5). `Parameters::default()`
|
||||
/// reads `RTX_E3_WALL_DISTANCE=oblique` (default: the recorded form).
|
||||
pub wall_distance_oblique: bool,
|
||||
/// The diffusive exchange of a fluid face with a SOLID neighbour face
|
||||
/// over the axis distance to the wall, `δ = min(h, d_f/|n_d|)`, and
|
||||
/// implicit — instead of the full `h`, which places the no-slip value
|
||||
/// deeper in the body than the wall (an effective radius ≈ 0.28 h short
|
||||
/// on the DFG 2D-1 ladder; S2-5). `RTX_E3_WALL_EXCHANGE=axis`.
|
||||
pub wall_exchange_axis: bool,
|
||||
/// HOST PROTOTYPE (S2-5): the pressure gradient across cut cells over
|
||||
/// the distance between the cells' FLUID CENTROIDS (estimated from the
|
||||
/// volume fraction and the wall normal) instead of `h` — a symmetric
|
||||
/// per-face weight `ω = h/δ` in the Poisson coefficient, the velocity
|
||||
/// correction and the predictor's pressure force. The device path
|
||||
/// refuses it. `RTX_E3_PRESSURE_CENTROID=1`.
|
||||
pub pressure_centroid: bool,
|
||||
/// S2-5: the cross-direction diffusion between two faces over the
|
||||
/// distance between their OPEN-PART CENTROIDS (a cut face's velocity
|
||||
/// is its open part's mean, ½h(1 − α) off the face centre along the
|
||||
/// wall's in-plane normal) instead of `h`. With `h` the coupling of a
|
||||
/// cut face to its neighbour is weak by `(1 + α)/2` and the no-slip
|
||||
/// surface sits `½(1 − α) h` inside the body (the flat-wall instrument).
|
||||
/// `RTX_E3_DIFFUSION_CENTROID=1`.
|
||||
pub diffusion_centroid: bool,
|
||||
}
|
||||
|
||||
impl Default for Parameters {
|
||||
@@ -116,6 +143,11 @@ impl Default for Parameters {
|
||||
.ok()
|
||||
.and_then(|v| v.parse().ok())
|
||||
.unwrap_or(1),
|
||||
wall_distance_oblique: std::env::var("RTX_E3_WALL_DISTANCE")
|
||||
.is_ok_and(|v| v == "oblique"),
|
||||
wall_exchange_axis: std::env::var("RTX_E3_WALL_EXCHANGE").is_ok_and(|v| v == "axis"),
|
||||
pressure_centroid: std::env::var("RTX_E3_PRESSURE_CENTROID").is_ok_and(|v| v == "1"),
|
||||
diffusion_centroid: std::env::var("RTX_E3_DIFFUSION_CENTROID").is_ok_and(|v| v == "1"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -241,6 +273,15 @@ impl Solver {
|
||||
m.scheme = self.params.convection_scheme;
|
||||
m.density = self.fluid.density;
|
||||
m.wall_order = self.params.wall_order;
|
||||
m.wall_distance_oblique = self.params.wall_distance_oblique;
|
||||
m.wall_exchange_axis = self.params.wall_exchange_axis;
|
||||
m.diffusion_centroid = self.params.diffusion_centroid;
|
||||
if self.params.diffusion_centroid {
|
||||
m.compute_face_shifts();
|
||||
}
|
||||
if self.params.pressure_centroid {
|
||||
m.compute_gradient_weights();
|
||||
}
|
||||
m
|
||||
})
|
||||
.expect("embedded mask")
|
||||
@@ -360,6 +401,24 @@ impl Solver {
|
||||
// The projection's apertures: step-averaged on a moving cut wall
|
||||
// (1 without a cut geometry).
|
||||
#[inline]
|
||||
/// The pressure-gradient weight `ω = h/δ` of a u / v / w face (1
|
||||
/// without the centroid prototype).
|
||||
pub(super) fn gu(&self, k: usize, j: usize, i: usize) -> f64 {
|
||||
self.mask
|
||||
.as_ref()
|
||||
.map_or(1.0, |m| m.grad_weight(0, m.grid().uface(k, j, i)))
|
||||
}
|
||||
pub(super) fn gv(&self, k: usize, j: usize, i: usize) -> f64 {
|
||||
self.mask
|
||||
.as_ref()
|
||||
.map_or(1.0, |m| m.grad_weight(1, m.grid().vface(k, j, i)))
|
||||
}
|
||||
pub(super) fn gw(&self, k: usize, j: usize, i: usize) -> f64 {
|
||||
self.mask
|
||||
.as_ref()
|
||||
.map_or(1.0, |m| m.grad_weight(2, m.grid().wface(k, j, i)))
|
||||
}
|
||||
|
||||
pub(super) fn au(&self, k: usize, j: usize, i: usize) -> f64 {
|
||||
self.mask
|
||||
.as_ref()
|
||||
|
||||
@@ -39,28 +39,28 @@ impl Solver {
|
||||
extra += ae_outlet;
|
||||
}
|
||||
} else if self.u_is_unknown(k, j, i + 1) {
|
||||
problem.ae[idx] = ae_interior * self.au(k, j, i + 1);
|
||||
problem.ae[idx] = ae_interior * self.au(k, j, i + 1) * self.gu(k, j, i + 1);
|
||||
}
|
||||
if i == 0 {
|
||||
if b.x0 == outlet {
|
||||
extra += ae_outlet;
|
||||
}
|
||||
} else if self.u_is_unknown(k, j, i) {
|
||||
problem.aw[idx] = ae_interior * self.au(k, j, i);
|
||||
problem.aw[idx] = ae_interior * self.au(k, j, i) * self.gu(k, j, i);
|
||||
}
|
||||
if j + 1 == ny {
|
||||
if b.y1 == outlet {
|
||||
extra += an_outlet;
|
||||
}
|
||||
} else if self.v_is_unknown(k, j + 1, i) {
|
||||
problem.an[idx] = an_interior * self.av(k, j + 1, i);
|
||||
problem.an[idx] = an_interior * self.av(k, j + 1, i) * self.gv(k, j + 1, i);
|
||||
}
|
||||
if j == 0 {
|
||||
if b.y0 == outlet {
|
||||
extra += an_outlet;
|
||||
}
|
||||
} else if self.v_is_unknown(k, j, i) {
|
||||
problem.as_[idx] = an_interior * self.av(k, j, i);
|
||||
problem.as_[idx] = an_interior * self.av(k, j, i) * self.gv(k, j, i);
|
||||
}
|
||||
if k + 1 == nz && !periodic {
|
||||
if b.z1 == outlet {
|
||||
@@ -434,7 +434,8 @@ impl Solver {
|
||||
for j in 0..ny {
|
||||
for i in 1..nx {
|
||||
if self.u_is_unknown(k, j, i) {
|
||||
let dp_dx = (pp[g.cell(k, j, i)] - pp[g.cell(k, j, i - 1)]) / dx;
|
||||
let dp_dx =
|
||||
self.gu(k, j, i) * (pp[g.cell(k, j, i)] - pp[g.cell(k, j, i - 1)]) / dx;
|
||||
let f = g.uface(k, j, i);
|
||||
field.u[f] = field.u_star[f] - (dt / rho) * dp_dx;
|
||||
}
|
||||
@@ -453,7 +454,8 @@ impl Solver {
|
||||
for i in 0..nx {
|
||||
for j in 1..ny {
|
||||
if self.v_is_unknown(k, j, i) {
|
||||
let dp_dy = (pp[g.cell(k, j, i)] - pp[g.cell(k, j - 1, i)]) / dy;
|
||||
let dp_dy =
|
||||
self.gv(k, j, i) * (pp[g.cell(k, j, i)] - pp[g.cell(k, j - 1, i)]) / dy;
|
||||
let f = g.vface(k, j, i);
|
||||
field.v[f] = field.v_star[f] - (dt / rho) * dp_dy;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user