embedded3 R6-2 step 2: the band's surface velocity at the foot (ub) and the rebuild's imposition on the device (RTX_E3_UB_DEVICE=1, default off)
e3_geom_ub / e3_geom_impose / e3_geom_seam: the flag body's surface velocity (DeviceSdf::vel, the centreline's velocity per point) at the foot from the trilinear interpolant of the device corner phi, and at the face centre for the band's solid faces of the uploaded field (a cut mask has no ghosts); the host mirror of ub by a packed download. RTX_E3_BAND_CHECK=1 compares the device ub with Mask::surface_velocity_at and the imposed u, v, w with impose_from, bit for bit; check_against now compares ub on every band face (zeros included). Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
6ae5312b6f
commit
2c054ccc91
@@ -33,6 +33,13 @@ pub struct DeviceSdf {
|
||||
pub fillet: f64,
|
||||
/// The centreline polyline's points (x, y) at `t`.
|
||||
pub poly: Vec<[f64; 2]>,
|
||||
/// R6-2 step 2: the centreline's velocity (vx, vy) per point at `t`, the
|
||||
/// device form of the surface velocity (`e3_geom.cu` `e3_geom_ub`): the
|
||||
/// velocity interpolated at the capsule's closest point where the
|
||||
/// capsule (span-cut) is not farther than the circle, zero on the circle,
|
||||
/// no z component — the flag test's host closure. Empty: the surface
|
||||
/// velocity stays on the host.
|
||||
pub vel: Vec<[f64; 2]>,
|
||||
}
|
||||
|
||||
/// One surface quadrature point: position, unit normal out of the solid,
|
||||
|
||||
@@ -48,6 +48,9 @@ pub struct MaskUpdate {
|
||||
pub correction: f64,
|
||||
/// The merged cells.
|
||||
pub merged: usize,
|
||||
/// R6-2 step 2 (`RTX_E3_UB_DEVICE=1`): the device imposes the wall on
|
||||
/// the uploaded field (the host rebuild skips `impose_from`).
|
||||
pub impose_on_device: bool,
|
||||
}
|
||||
|
||||
/// The instantaneous arrays of a retired mask, kept for the mask two steps later.
|
||||
|
||||
@@ -675,8 +675,11 @@ impl DeviceCut {
|
||||
(&mut self.a_pred, &mut self.d)
|
||||
}
|
||||
|
||||
/// `RTX_E3_BAND_CHECK=1`: every device table against a fresh full build.
|
||||
pub(super) fn check_against(&self, full: &Self) {
|
||||
/// `RTX_E3_BAND_CHECK=1`: every device table against a fresh full build
|
||||
/// (the surface velocity on every face within `band` of the wall, the
|
||||
/// imposition band — zeros included; beyond it the full build writes 0
|
||||
/// and ours keeps stale values, never read).
|
||||
pub(super) fn check_against(&self, full: &Self, band: f64) {
|
||||
let rt = runtime();
|
||||
let same_f = |name: &str, a: &CudaSlice<f64>, b: &CudaSlice<f64>| {
|
||||
let (x, y) = (
|
||||
@@ -709,16 +712,22 @@ impl DeviceCut {
|
||||
same_i("open", &self.open[c], &full.open[c]);
|
||||
same_i("open_pred", &self.open_pred[c], &full.open_pred[c]);
|
||||
same_f("shift", &self.shift[c], &full.shift[c]);
|
||||
// ub: the band's faces only (beyond it the full build writes 0, ours keeps stale — never read).
|
||||
// ub: the band's faces (|d| ≤ band, by the full build's distances).
|
||||
let (x, y) = (
|
||||
rt.stream.memcpy_dtov(&self.ub[c]).expect("dl"),
|
||||
rt.stream.memcpy_dtov(&full.ub[c]).expect("dl"),
|
||||
);
|
||||
let d: Vec<f64> = rt.stream.memcpy_dtov(&full.d[c]).expect("dl");
|
||||
let bad = x
|
||||
.iter()
|
||||
.zip(&y)
|
||||
.filter(|(p, q)| **q != 0.0 && p.to_bits() != q.to_bits())
|
||||
.zip(&d)
|
||||
.filter(|((p, q), dd)| dd.abs() <= band && p.to_bits() != q.to_bits())
|
||||
.count();
|
||||
assert!(
|
||||
x.len() == y.len() && y.len() == d.len(),
|
||||
"band tables: ub[{c}] lengths differ"
|
||||
);
|
||||
assert!(bad == 0, "band tables: ub[{c}] differs on {bad} band faces");
|
||||
}
|
||||
same_f("wall_flux", &self.wall_flux, &full.wall_flux);
|
||||
@@ -774,6 +783,51 @@ impl DeviceCut {
|
||||
}
|
||||
|
||||
impl DeviceStep {
|
||||
/// R6-2 step 2 under `RTX_E3_BAND_CHECK=1`: the device-imposed velocities
|
||||
/// against the host's `impose_from` on the uploaded (un-imposed) mirror,
|
||||
/// bit for bit on every face.
|
||||
fn check_device_impose(&mut self, t: f64) {
|
||||
let rt = runtime();
|
||||
let field = self.host_field.as_ref().expect("the host mirror");
|
||||
let mask = self.solver.mask().expect("mask");
|
||||
let body = self.solver.body().expect("body");
|
||||
let (mut u, mut v, mut w) = (field.u.clone(), field.v.clone(), field.w.clone());
|
||||
mask.impose_from(
|
||||
body,
|
||||
&field.u_old,
|
||||
&field.v_old,
|
||||
&field.w_old,
|
||||
&mut u,
|
||||
&mut v,
|
||||
&mut w,
|
||||
t,
|
||||
);
|
||||
let mut bad = [0usize; 3];
|
||||
for (c, (host, dev)) in [(&u, &self.u), (&v, &self.v), (&w, &self.w)]
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
{
|
||||
let d: Vec<f64> = rt.stream.memcpy_dtov(dev).expect("dl");
|
||||
bad[c] = host
|
||||
.iter()
|
||||
.zip(&d)
|
||||
.filter(|(a, b)| a.to_bits() != b.to_bits())
|
||||
.count()
|
||||
+ host.len().abs_diff(d.len());
|
||||
}
|
||||
if bad == [0; 3] {
|
||||
eprintln!(" impose check t {t:.6}: device imposition IDENTICAL to the host (u, v, w every face)");
|
||||
} else {
|
||||
eprintln!(
|
||||
" impose check t {t:.6}: DIFFERS — {} / {} / {} faces",
|
||||
bad[0], bad[1], bad[2]
|
||||
);
|
||||
if std::env::var("RTX_E3_GEOM_CHECK_SOFT").is_err() {
|
||||
panic!("impose check: the device imposition differs from the host");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// One step on the cut-cell mask (the host `advance` with the cut
|
||||
/// predictor, the apertured merged continuity and the owner-read
|
||||
/// corrections).
|
||||
@@ -858,6 +912,7 @@ impl DeviceStep {
|
||||
None => true,
|
||||
};
|
||||
let mut device_mask: Option<usize> = None;
|
||||
let mut ub_body: Option<super::geom::UbBody> = None;
|
||||
if geom_on {
|
||||
if let Some(dc) = self.cut.as_mut() {
|
||||
let fresh_geom = self.geom.is_none();
|
||||
@@ -900,6 +955,11 @@ impl DeviceStep {
|
||||
upd.merged
|
||||
);
|
||||
}
|
||||
// R6-2 step 2 (`RTX_E3_UB_DEVICE=1`): the band's surface
|
||||
// velocity and the imposition on the device.
|
||||
ub_body = super::geom::UbBody::new(&self.solver, t_new);
|
||||
let mut upd = upd;
|
||||
upd.impose_on_device = ub_body.is_some();
|
||||
self.solver.pending_mask = Some(upd);
|
||||
}
|
||||
}
|
||||
@@ -923,17 +983,38 @@ impl DeviceStep {
|
||||
// reference `RTX_E3_TABLES_REBUILD=1`).
|
||||
let rebuild_all = std::env::var("RTX_E3_TABLES_REBUILD").is_ok();
|
||||
let check = std::env::var("RTX_E3_BAND_CHECK").is_ok();
|
||||
let ub_band = self
|
||||
.solver
|
||||
.mask()
|
||||
.and_then(|m| m.impose_band())
|
||||
.unwrap_or(f64::INFINITY);
|
||||
self.cut = match self.cut.take() {
|
||||
// R6-2: the device classification wrote the tables; the
|
||||
// surface velocities remain.
|
||||
Some(mut prev) if device_mask.is_some() => {
|
||||
let dm = self.dmask.as_mut().expect("device mask");
|
||||
let merged = device_mask.expect("merged");
|
||||
assert!(prev.update_after_device_mask(&self.solver, g, t_new, dm, merged));
|
||||
let device_side = match (self.geom.as_ref(), ub_body.as_ref()) {
|
||||
(Some(gm), Some(ubb)) => {
|
||||
Some((gm, ubb, [&mut self.u, &mut self.v, &mut self.w]))
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
assert!(prev.update_after_device_mask(
|
||||
&self.solver,
|
||||
g,
|
||||
t_new,
|
||||
dm,
|
||||
device_side,
|
||||
merged
|
||||
));
|
||||
if check && ub_body.is_some() {
|
||||
self.check_device_impose(t_new);
|
||||
}
|
||||
if check {
|
||||
let full = DeviceCut::build(&self.solver, g, Phase::Projection, t_new)
|
||||
.expect("full");
|
||||
prev.check_against(&full);
|
||||
prev.check_against(&full, ub_band);
|
||||
eprintln!(" table check t {t_new:.6}: device tables IDENTICAL to the full build");
|
||||
}
|
||||
Some(prev)
|
||||
@@ -943,7 +1024,7 @@ impl DeviceStep {
|
||||
if check {
|
||||
let full = DeviceCut::build(&self.solver, g, Phase::Projection, t_new)
|
||||
.expect("full");
|
||||
prev.check_against(&full);
|
||||
prev.check_against(&full, ub_band);
|
||||
}
|
||||
Some(prev)
|
||||
} else {
|
||||
|
||||
+186
-30
@@ -12,6 +12,7 @@
|
||||
//! down.
|
||||
|
||||
use super::cut::DeviceCut;
|
||||
use crate::solvers::incompressible::embedded3::body::DeviceSdf;
|
||||
use crate::solvers::incompressible::embedded3::cut::{CutGeometry, GeomPool};
|
||||
use crate::solvers::incompressible::embedded3::poisson::device::{cfg, load_module, runtime};
|
||||
use crate::solvers::incompressible::embedded3::step::Solver;
|
||||
@@ -32,6 +33,9 @@ struct GeomKernels {
|
||||
faces: CudaFunction,
|
||||
cells: CudaFunction,
|
||||
gather: CudaFunction,
|
||||
ub: CudaFunction,
|
||||
impose: CudaFunction,
|
||||
seam: CudaFunction,
|
||||
}
|
||||
|
||||
static GEOM_ONCE: OnceLock<GeomKernels> = OnceLock::new();
|
||||
@@ -46,6 +50,9 @@ fn kernels() -> &'static GeomKernels {
|
||||
faces: f("e3_geom_faces"),
|
||||
cells: f("e3_geom_cells"),
|
||||
gather: f("e3_geom_gather"),
|
||||
ub: f("e3_geom_ub"),
|
||||
impose: f("e3_geom_impose"),
|
||||
seam: f("e3_geom_seam"),
|
||||
_module: module,
|
||||
}
|
||||
})
|
||||
@@ -116,6 +123,80 @@ pub(super) fn log_fallback(reason: &str) {
|
||||
});
|
||||
}
|
||||
|
||||
fn geom_grid(g: Grid) -> GeomGrid {
|
||||
GeomGrid {
|
||||
nx: g.nx as i32,
|
||||
ny: g.ny as i32,
|
||||
nz: g.nz as i32,
|
||||
dx: g.dx,
|
||||
dy: g.dy,
|
||||
dz: g.dz,
|
||||
}
|
||||
}
|
||||
|
||||
fn geom_sdf(sdf: &DeviceSdf) -> GeomSdf {
|
||||
GeomSdf {
|
||||
cx: sdf.cyl[0],
|
||||
cy: sdf.cyl[1],
|
||||
rc: sdf.cyl[2],
|
||||
zc: sdf.zc,
|
||||
span: sdf.span,
|
||||
r_edge: sdf.r_edge,
|
||||
half: sdf.half,
|
||||
fillet: sdf.fillet,
|
||||
cyl_cut: i32::from(sdf.cyl_cut),
|
||||
flag_cut: i32::from(sdf.flag_cut),
|
||||
npts: sdf.poly.len() as i32,
|
||||
}
|
||||
}
|
||||
|
||||
/// Interleaved (x, y) pairs on the device (one dummy entry when empty).
|
||||
fn upload_pairs(v: &[[f64; 2]]) -> CudaSlice<f64> {
|
||||
let flat: Vec<f64> = v.iter().flat_map(|p| [p[0], p[1]]).collect();
|
||||
runtime()
|
||||
.stream
|
||||
.memcpy_stod(if flat.is_empty() {
|
||||
&[0.0f64][..]
|
||||
} else {
|
||||
&flat
|
||||
})
|
||||
.expect("upload pairs")
|
||||
}
|
||||
|
||||
/// R6-2 step 2 (`RTX_E3_UB_DEVICE=1`, default off): the band's surface
|
||||
/// velocity at the foot and the end-of-rebuild imposition on the device.
|
||||
pub(crate) fn ub_device_enabled() -> bool {
|
||||
std::env::var("RTX_E3_UB_DEVICE").is_ok_and(|v| v == "1")
|
||||
}
|
||||
|
||||
/// R6-2 step 2: the body's surface velocity in the device's form at one
|
||||
/// time (the polyline and its velocities uploaded once per step).
|
||||
pub(super) struct UbBody {
|
||||
gs: GeomSdf,
|
||||
poly: CudaSlice<f64>,
|
||||
vel: CudaSlice<f64>,
|
||||
}
|
||||
|
||||
impl UbBody {
|
||||
/// `None` unless `RTX_E3_UB_DEVICE=1` (default off until its
|
||||
/// two-period gate) and the body has a device form of its surface
|
||||
/// velocity (`DeviceSdf::vel`, one per polyline point).
|
||||
pub(super) fn new(solver: &Solver, t: f64) -> Option<Self> {
|
||||
if !ub_device_enabled() {
|
||||
return None;
|
||||
}
|
||||
let sdf = solver.body()?.device_sdf(t)?;
|
||||
if sdf.vel.is_empty() || sdf.vel.len() != sdf.poly.len() {
|
||||
return None;
|
||||
}
|
||||
Some(Self {
|
||||
gs: geom_sdf(&sdf),
|
||||
poly: upload_pairs(&sdf.poly),
|
||||
vel: upload_pairs(&sdf.vel),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// The persistent device geometry: corner φ and bound, the evaluated
|
||||
/// corners, the cell volumes and wall vectors (the face tables live in
|
||||
/// `DeviceCut`).
|
||||
@@ -147,6 +228,108 @@ fn par_copy<T: Copy + Send + Sync>(src: &[T]) -> Vec<T> {
|
||||
}
|
||||
|
||||
impl DeviceGeom {
|
||||
/// R6-2 step 2: the imposition on the `nf` band faces of component `c`
|
||||
/// listed in `faces` — the solid ones (`open` = the instantaneous kind
|
||||
/// is fluid) take the body's surface velocity at the face centre — and,
|
||||
/// for `c = 2` on a periodic mask, the seam.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(super) fn impose(
|
||||
body: &UbBody,
|
||||
g: Grid,
|
||||
c: usize,
|
||||
faces: &CudaSlice<u32>,
|
||||
nf: usize,
|
||||
open: &CudaSlice<i32>,
|
||||
field: &mut CudaSlice<f64>,
|
||||
) {
|
||||
if nf == 0 {
|
||||
return;
|
||||
}
|
||||
let rt = runtime();
|
||||
let k = kernels();
|
||||
let gg = geom_grid(g);
|
||||
let ci = c as i32;
|
||||
let nf64 = nf as i64;
|
||||
unsafe {
|
||||
rt.stream
|
||||
.launch_builder(&k.impose)
|
||||
.arg(&gg)
|
||||
.arg(&body.gs)
|
||||
.arg(&body.poly)
|
||||
.arg(&body.vel)
|
||||
.arg(&ci)
|
||||
.arg(faces)
|
||||
.arg(&nf64)
|
||||
.arg(open)
|
||||
.arg(field)
|
||||
.launch(cfg(nf))
|
||||
.expect("e3_geom_impose");
|
||||
}
|
||||
}
|
||||
|
||||
/// The periodic seam after the imposition (`Mask::impose_from`'s last loop).
|
||||
pub(super) fn seam(g: Grid, open_w: &CudaSlice<i32>, w: &mut CudaSlice<f64>) {
|
||||
let rt = runtime();
|
||||
let k = kernels();
|
||||
let gg = geom_grid(g);
|
||||
unsafe {
|
||||
rt.stream
|
||||
.launch_builder(&k.seam)
|
||||
.arg(&gg)
|
||||
.arg(open_w)
|
||||
.arg(w)
|
||||
.launch(cfg(g.nx * g.ny))
|
||||
.expect("e3_geom_seam");
|
||||
}
|
||||
}
|
||||
|
||||
/// R6-2 step 2: the surface velocity at the foot for the `nf` faces of
|
||||
/// component `c` listed in `faces` (`Mask::surface_velocity_at` on the
|
||||
/// device, from this state's corner φ — the mirror's): written into the
|
||||
/// dense table `ub` and packed into `packed` (the host mirror's copy).
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(super) fn surface_velocity(
|
||||
&self,
|
||||
body: &UbBody,
|
||||
g: Grid,
|
||||
c: usize,
|
||||
faces: &CudaSlice<u32>,
|
||||
nf: usize,
|
||||
shift: Option<&CudaSlice<f64>>,
|
||||
ub: &mut CudaSlice<f64>,
|
||||
packed: &mut CudaSlice<f64>,
|
||||
) {
|
||||
if nf == 0 {
|
||||
return;
|
||||
}
|
||||
let rt = runtime();
|
||||
let k = kernels();
|
||||
let gg = geom_grid(g);
|
||||
let ci = c as i32;
|
||||
let nf64 = nf as i64;
|
||||
let use_shift = i32::from(shift.is_some());
|
||||
// Without the shift the kernel never reads it: any f64 buffer stands in.
|
||||
let shift = shift.unwrap_or(&self.vol);
|
||||
unsafe {
|
||||
rt.stream
|
||||
.launch_builder(&k.ub)
|
||||
.arg(&gg)
|
||||
.arg(&body.gs)
|
||||
.arg(&body.poly)
|
||||
.arg(&body.vel)
|
||||
.arg(&self.phi)
|
||||
.arg(&ci)
|
||||
.arg(faces)
|
||||
.arg(&nf64)
|
||||
.arg(shift)
|
||||
.arg(&use_shift)
|
||||
.arg(ub)
|
||||
.arg(packed)
|
||||
.launch(cfg(nf))
|
||||
.expect("e3_geom_ub");
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn new(g: Grid) -> Self {
|
||||
let rt = runtime();
|
||||
let nn = (g.nx + 1) * (g.ny + 1) * (g.nz + 1);
|
||||
@@ -215,36 +398,9 @@ impl DeviceGeom {
|
||||
full = true;
|
||||
}
|
||||
}
|
||||
let gg = GeomGrid {
|
||||
nx: nx as i32,
|
||||
ny: ny as i32,
|
||||
nz: nz as i32,
|
||||
dx: g.dx,
|
||||
dy: g.dy,
|
||||
dz: g.dz,
|
||||
};
|
||||
let gs = GeomSdf {
|
||||
cx: sdf.cyl[0],
|
||||
cy: sdf.cyl[1],
|
||||
rc: sdf.cyl[2],
|
||||
zc: sdf.zc,
|
||||
span: sdf.span,
|
||||
r_edge: sdf.r_edge,
|
||||
half: sdf.half,
|
||||
fillet: sdf.fillet,
|
||||
cyl_cut: i32::from(sdf.cyl_cut),
|
||||
flag_cut: i32::from(sdf.flag_cut),
|
||||
npts: sdf.poly.len() as i32,
|
||||
};
|
||||
let poly: Vec<f64> = sdf.poly.iter().flat_map(|p| [p[0], p[1]]).collect();
|
||||
let d_poly = rt
|
||||
.stream
|
||||
.memcpy_stod(if poly.is_empty() {
|
||||
&[0.0f64][..]
|
||||
} else {
|
||||
&poly
|
||||
})
|
||||
.expect("poly");
|
||||
let gg = geom_grid(g);
|
||||
let gs = geom_sdf(&sdf);
|
||||
let d_poly = upload_pairs(&sdf.poly);
|
||||
let (has_prev, band, motion) = match prev {
|
||||
Some((_, band, motion)) => (1i32, band, motion),
|
||||
None => (0i32, 0.0, 0.0),
|
||||
|
||||
+117
-47
@@ -10,7 +10,7 @@
|
||||
//! kernels overwrite them.
|
||||
|
||||
use super::cut::{DeviceCut, Phase};
|
||||
use super::geom::DeviceGeom;
|
||||
use super::geom::{DeviceGeom, UbBody};
|
||||
use crate::solvers::incompressible::embedded3::cutwall::MERGE_FRACTION;
|
||||
use crate::solvers::incompressible::embedded3::maskupdate::MaskUpdate;
|
||||
use crate::solvers::incompressible::embedded3::poisson::device::{cfg, load_module, runtime};
|
||||
@@ -157,6 +157,8 @@ pub(super) struct DeviceMask {
|
||||
out_face_a: [Option<CudaSlice<f64>>; 3],
|
||||
out_face_shift: [Option<CudaSlice<f64>>; 3],
|
||||
out_wall_a: Option<CudaSlice<f64>>,
|
||||
/// Step 2: the band's surface velocities, packed (the host mirror's copy).
|
||||
out_ub: Option<CudaSlice<f64>>,
|
||||
/// The laps of the last run (ms): lists, cells + merging, faces, GCL, fold.
|
||||
pub(super) laps: [f64; 6],
|
||||
}
|
||||
@@ -190,6 +192,7 @@ impl DeviceMask {
|
||||
out_face_a: [None, None, None],
|
||||
out_face_shift: [None, None, None],
|
||||
out_wall_a: None,
|
||||
out_ub: None,
|
||||
laps: [0.0; 6],
|
||||
}
|
||||
}
|
||||
@@ -597,31 +600,31 @@ impl DeviceMask {
|
||||
face_shift,
|
||||
correction,
|
||||
merged,
|
||||
impose_on_device: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// The imposition band's faces per component (|d| <= band), ascending.
|
||||
pub(super) fn band_faces(&mut self, dc: &DeviceCut, g: Grid, band: f64) -> [Vec<u32>; 3] {
|
||||
/// The imposition band's faces of component `c` (|d| ≤ `band`),
|
||||
/// ascending, compacted into `list_tmp`; their count.
|
||||
fn band_list(&mut self, dc: &DeviceCut, g: Grid, band: f64, c: usize) -> usize {
|
||||
let rt = runtime();
|
||||
let k = kernels();
|
||||
let counts = [g.n_ufaces(), g.n_vfaces(), g.n_wfaces()];
|
||||
[0usize, 1, 2].map(|c| {
|
||||
let n64 = counts[c] as i64;
|
||||
unsafe {
|
||||
rt.stream
|
||||
.launch_builder(&k.band_flags)
|
||||
.arg(&n64)
|
||||
.arg(&dc.d[c])
|
||||
.arg(&band)
|
||||
.arg(&mut self.flags)
|
||||
.launch(cfg(counts[c]))
|
||||
.expect("e3_mask_band_flags");
|
||||
}
|
||||
let mut tmp = self.list_tmp.take();
|
||||
let n = self.compact(counts[c], &mut tmp);
|
||||
self.list_tmp = tmp;
|
||||
head(self.list_tmp.as_ref().expect("band"), n)
|
||||
})
|
||||
let n = [g.n_ufaces(), g.n_vfaces(), g.n_wfaces()][c];
|
||||
let n64 = n as i64;
|
||||
unsafe {
|
||||
rt.stream
|
||||
.launch_builder(&k.band_flags)
|
||||
.arg(&n64)
|
||||
.arg(&dc.d[c])
|
||||
.arg(&band)
|
||||
.arg(&mut self.flags)
|
||||
.launch(cfg(n))
|
||||
.expect("e3_mask_band_flags");
|
||||
}
|
||||
let mut tmp = self.list_tmp.take();
|
||||
let nb = self.compact(n, &mut tmp);
|
||||
self.list_tmp = tmp;
|
||||
nb
|
||||
}
|
||||
}
|
||||
|
||||
@@ -629,13 +632,19 @@ impl DeviceCut {
|
||||
/// R6-2: finish the projection tables after the device classification
|
||||
/// (`DeviceMask::run` wrote every table but the surface velocities): the
|
||||
/// surface velocity at the foot for the imposition band's faces (listed
|
||||
/// on the device), the merged count, the phase.
|
||||
/// on the device) — on the device from `geom`'s corner φ when the body
|
||||
/// has a device form of its surface velocity (step 2; the host mirror
|
||||
/// by a packed download), on the host otherwise — the merged count, the
|
||||
/// phase. `RTX_E3_BAND_CHECK=1`: the device values against the host's
|
||||
/// `surface_velocity_at`, bit for bit on every band face.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(super) fn update_after_device_mask(
|
||||
&mut self,
|
||||
solver: &Solver,
|
||||
g: Grid,
|
||||
t: f64,
|
||||
dm: &mut DeviceMask,
|
||||
device_side: Option<(&DeviceGeom, &UbBody, [&mut CudaSlice<f64>; 3])>,
|
||||
merged: usize,
|
||||
) -> bool {
|
||||
use crate::solvers::incompressible::embedded3::poisson::device_cg::scatter_f64;
|
||||
@@ -645,12 +654,24 @@ impl DeviceCut {
|
||||
};
|
||||
let lap = Instant::now();
|
||||
let band = mask.impose_band().unwrap_or(f64::INFINITY);
|
||||
let band_faces = dm.band_faces(self, g, band);
|
||||
let l_band = lap.elapsed();
|
||||
let h = [g.dx, g.dy, g.dz];
|
||||
let shifts = mask.face_shift_tables();
|
||||
let centroid_foot = shifts.is_some() && mask.wall_foot_centroid;
|
||||
let (nx, ny) = (g.nx, g.ny);
|
||||
for (c, bf) in band_faces.iter().enumerate() {
|
||||
let check = std::env::var("RTX_E3_BAND_CHECK").is_ok();
|
||||
let on_device = device_side.is_some();
|
||||
let (geom, ubody, mut fields) = match device_side {
|
||||
Some((gm, ubb, f)) => (Some(gm), Some(ubb), Some(f)),
|
||||
None => (None, None, None),
|
||||
};
|
||||
let mut l_band = std::time::Duration::ZERO;
|
||||
let mut nbf = [0usize; 3];
|
||||
let mut checked = 0usize;
|
||||
for c in 0..3 {
|
||||
let lb = Instant::now();
|
||||
let nb = dm.band_list(self, g, band, c);
|
||||
l_band += lb.elapsed();
|
||||
nbf[c] = nb;
|
||||
let (ni, nj) = (nx + usize::from(c == 0), ny + usize::from(c == 1));
|
||||
let pos = |idx: usize| -> [f64; 3] {
|
||||
let (k, j, i) = (idx / (nj * ni), (idx / ni) % nj, idx % ni);
|
||||
@@ -660,25 +681,73 @@ impl DeviceCut {
|
||||
(k as f64 + if c == 2 { 0.0 } else { 0.5 }) * h[2],
|
||||
]
|
||||
};
|
||||
let ubv: Vec<f64> = bf
|
||||
.par_iter()
|
||||
.map(|&f| {
|
||||
let x = pos(f as usize);
|
||||
let xf = match (shifts, mask.wall_foot_centroid) {
|
||||
(Some(sh), true) => [
|
||||
x[0] + sh[c][3 * f as usize],
|
||||
x[1] + sh[c][3 * f as usize + 1],
|
||||
x[2] + sh[c][3 * f as usize + 2],
|
||||
],
|
||||
_ => x,
|
||||
};
|
||||
mask.surface_velocity_at(body, xf, c, t)
|
||||
})
|
||||
.collect();
|
||||
for (&f, &v) in bf.iter().zip(&ubv) {
|
||||
self.ub_host[c][f as usize] = v;
|
||||
let host_ub = |bf: &[u32]| -> Vec<f64> {
|
||||
bf.par_iter()
|
||||
.map(|&f| {
|
||||
let x = pos(f as usize);
|
||||
let xf = match (shifts, mask.wall_foot_centroid) {
|
||||
(Some(sh), true) => [
|
||||
x[0] + sh[c][3 * f as usize],
|
||||
x[1] + sh[c][3 * f as usize + 1],
|
||||
x[2] + sh[c][3 * f as usize + 2],
|
||||
],
|
||||
_ => x,
|
||||
};
|
||||
mask.surface_velocity_at(body, xf, c, t)
|
||||
})
|
||||
.collect()
|
||||
};
|
||||
let list = dm.list_tmp.as_ref().expect("band list");
|
||||
let bf = head(list, nb);
|
||||
match (geom, ubody) {
|
||||
(Some(gm), Some(ubb)) => {
|
||||
ensure(&mut dm.out_ub, nb);
|
||||
let packed = dm.out_ub.as_mut().expect("packed ub");
|
||||
let shift = centroid_foot.then_some(&self.shift[c]);
|
||||
gm.surface_velocity(ubb, g, c, list, nb, shift, &mut self.ub[c], packed);
|
||||
let ubv = head(dm.out_ub.as_ref().expect("packed ub"), nb);
|
||||
for (&f, &v) in bf.iter().zip(&ubv) {
|
||||
self.ub_host[c][f as usize] = v;
|
||||
}
|
||||
if check {
|
||||
let reference = host_ub(&bf);
|
||||
let bad = reference
|
||||
.iter()
|
||||
.zip(&ubv)
|
||||
.filter(|(a, b)| a.to_bits() != b.to_bits())
|
||||
.count();
|
||||
if bad > 0 {
|
||||
eprintln!(
|
||||
" ub check t {t:.6}: DIFFERS — component {c}: {bad} of {nb} band faces"
|
||||
);
|
||||
if std::env::var("RTX_E3_GEOM_CHECK_SOFT").is_err() {
|
||||
panic!(
|
||||
"ub check: the device surface velocity differs from the host"
|
||||
);
|
||||
}
|
||||
}
|
||||
checked += nb;
|
||||
}
|
||||
// The imposition on the uploaded field: the same band list.
|
||||
let field = &mut *fields.as_mut().expect("fields")[c];
|
||||
DeviceGeom::impose(ubb, g, c, list, nb, &self.open_pred[c], field);
|
||||
if c == 2 {
|
||||
DeviceGeom::seam(g, &self.open_pred[2], field);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
let ubv = host_ub(&bf);
|
||||
for (&f, &v) in bf.iter().zip(&ubv) {
|
||||
self.ub_host[c][f as usize] = v;
|
||||
}
|
||||
scatter_f64(&bf, &ubv, &mut self.ub[c]);
|
||||
}
|
||||
}
|
||||
scatter_f64(bf, &ubv, &mut self.ub[c]);
|
||||
}
|
||||
if check && on_device {
|
||||
eprintln!(
|
||||
" ub check t {t:.6}: device surface velocity IDENTICAL to the host on {checked} band faces"
|
||||
);
|
||||
}
|
||||
self.merged = merged;
|
||||
self.phase = Phase::Projection;
|
||||
@@ -686,12 +755,13 @@ impl DeviceCut {
|
||||
runtime().stream.synchronize().expect("sync");
|
||||
if std::env::var("RTX_E3_MOVING_PROFILE").is_ok() {
|
||||
eprintln!(
|
||||
" table laps (device mask): band list {:.0} ms, ub {:.0} ms ({} / {} / {} band faces)",
|
||||
" table laps (device mask): band list {:.0} ms, ub {:.0} ms ({} / {} / {} band faces, {})",
|
||||
l_band.as_secs_f64() * 1e3,
|
||||
(lap.elapsed() - l_band).as_secs_f64() * 1e3,
|
||||
band_faces[0].len(),
|
||||
band_faces[1].len(),
|
||||
band_faces[2].len()
|
||||
nbf[0],
|
||||
nbf[1],
|
||||
nbf[2],
|
||||
if on_device { "device, imposed" } else { "host" }
|
||||
);
|
||||
}
|
||||
true
|
||||
|
||||
@@ -152,17 +152,25 @@ impl Solver {
|
||||
let fresh_cells = refill_fresh_in(&old_mask, &new_mask, &mut field.p, &upd.changed);
|
||||
let l_step = lap.elapsed();
|
||||
let sub = std::time::Instant::now();
|
||||
let body = self.body.as_ref().expect("body");
|
||||
new_mask.impose_from(
|
||||
body,
|
||||
&field.u_old,
|
||||
&field.v_old,
|
||||
&field.w_old,
|
||||
&mut field.u,
|
||||
&mut field.v,
|
||||
&mut field.w,
|
||||
t_new,
|
||||
// R6-2 step 2: the device imposes after the upload (a cut mask has
|
||||
// no ghosts: the imposition is the band's solid faces and the seam).
|
||||
assert!(
|
||||
!upd.impose_on_device || new_mask.ghost_faces() == 0,
|
||||
"R6-2 step 2: the device imposition has no ghost reconstruction"
|
||||
);
|
||||
if !upd.impose_on_device {
|
||||
let body = self.body.as_ref().expect("body");
|
||||
new_mask.impose_from(
|
||||
body,
|
||||
&field.u_old,
|
||||
&field.v_old,
|
||||
&field.w_old,
|
||||
&mut field.u,
|
||||
&mut field.v,
|
||||
&mut field.w,
|
||||
t_new,
|
||||
);
|
||||
}
|
||||
let s_impose = sub.elapsed();
|
||||
self.last_ghost_correction = upd.correction;
|
||||
if let Some((r, p_ref, fresh_ref, table, correction)) = reference {
|
||||
|
||||
Reference in New Issue
Block a user