core 0.5.0
Shared MPI block decomposition + asynchronous ghost-layer exchange (header-only C++20)
Loading...
Searching...
No Matches
ghost_projection.hpp
Go to the documentation of this file.
1// core — directional ghost-cell projection overlay on the AMR octree (collocated).
2//
3// *** PRODUCTION CANDIDATE (re-framed 2026-08-24; quarantined 2026-08-19..24) ***
4// flow's attractor campaign (flow/doc/collocated_invisible_subspace.md) reversed the 08-19
5// retirement calculus: the aperture projection possesses an attractor FAMILY of steady states
6// (its solid-centered pressure DOFs are constraint multipliers the gauge-exact gradient never
7// reads — support inconsistency, Prop. 2 of the note) plus a rotational-update instability at
8// fine resolution, both of which this fluid-only scheme is measured NOT to have (family-free,
9// no stabilizer at any dt, protocol-independent, clean-ladder convergent with a ~+0.2% bias).
10// The 08-19 COST statement stands (aperture MG-PCG is cheaper per step); the scheme choice is
11// now robustness/uniqueness. Default remains aperture until the suite-wide flip decision;
12// select with setGhostProjection(true) — the (2, 2) closure pair ONLY (the (1, 2) mixed form
13// is march-unstable above ~2000 spheres, flow/doc/ghost_hardening_findings_A.md).
14//
15// The AMR port of flow's collocated set_ghost_projection (flow/src/ghost_projection.hpp §9 of
16// flow/doc/collocated_second_order_open_problem.md): the aperture projection's two measured O(1)
17// cut-cell defects (tests/study_amr_ghost_apriori.cpp — the gauge-dependent O(1/h) ABC gradient
18// and the O(1) truncation of the pinned ½/½ aperture constraint) are removed by
19// * closing every solid/sliver face of the ½/½ face-AVERAGED cell field with the momentum
20// IBM's 1-D wall-anchored closure (peclet::core::scheme — the SAME pure per-face functions
21// flow uses), which after the substitution uf -= grad(phi) makes the pressure operator
22// A = L_bin + Delta: the symmetric BINARY-openness FV Laplacian (face open iff the face
23// sample and both adjacent centers are fluid — rides the UNCHANGED openness-MG rails as
24// the preconditioner) plus a compact nonsymmetric per-row overlay (this header), both
25// row-rescaled by rho = min(1, min_f D_f);
26// * the directional ghost cell-gradient (AmrFlow::setGhostGradient, already the step-1 hybrid)
27// for the -grad(p^n) predictor and the cell correction.
28//
29// THE STRUCTURAL INVARIANT that makes the octree port cheap: cut cells live in a uniformly-FINEST
30// band (the AmrCutCell same-level contract), so every overlay row's ±2-cell closure reach stays
31// inside a locally uniform region — no closure ever crosses a 2:1 level boundary. buildGhostOverlay
32// ENFORCES this (throws if a non-clean row's reach touches a different-level leaf): widen the
33// refineToSdf band rather than weaken the invariant. Level boundaries then carry only the smooth
34// binary operator, which the openness MG already handles conservatively.
35//
36// Sign conventions (differ from flow's kernels — DERIVED, do not pattern-match): the suite AMR
37// operator is the NEGATIVE-definite Laplacian L solved as L phi = div(u*). The ghost system is
38// [rho·(L_bin + Delta)] phi = rho·D_g(u*), Delta_i = invh² Σ_k sgn_k [w1(X(mn)-X(mn-1)) +
39// w2(X(mf)-X(mf-1))]
40// (flow's gpApplyDelta carries the OPPOSITE sign because its binary operator is -L). The
41// divergence delta keeps flow's orientation verbatim: D_g adds sgn·(w1·U(mn) + w2·U(mf)) per
42// closure face (EXPLICIT: sgn·U(mg)) with U(m) = ½(u_a(cell m-1) + u_a(cell m)), scaled invh.
43// Decoupled rows (no phi coupling at all) are zeroed in both matrix and RHS (phi = 0).
44//
45// Host-only-safe: the overlay build + host delta appliers compile without Kokkos (the oracle
46// path); the device mirror + kernels are guarded on KOKKOS_INLINE_FUNCTION (include this header
47// AFTER a Kokkos-carrying header in device TUs).
48#ifndef PECLET_CORE_AMR_GHOST_PROJECTION_HPP
49#define PECLET_CORE_AMR_GHOST_PROJECTION_HPP
50
51#ifdef PECLET_CORE_HAVE_MORTON
52
53#include <array>
54#include <cstdint>
55#include <stdexcept>
56#include <vector>
57
62
63namespace peclet::core::amr {
64
69 Index n = 0;
70 std::vector<Index> cell;
71 std::vector<float> rescale;
72 std::vector<int8_t> coupled;
73 std::vector<int8_t> state;
74 std::vector<float> th;
75 std::vector<float> w_bc, w_n1, w_n2;
76 std::vector<float> wm_n1, wm_n2;
77 std::vector<Index> nbr;
78 std::vector<double> invh;
79};
80
81namespace detail {
82// Reference adapter so scheme::gpFillRow (which writes ov.field(slot) = …) targets the host SoA.
85 Index& cell(int s) const { return g.cell[static_cast<std::size_t>(s)]; }
86 float& rescale(int s) const { return g.rescale[static_cast<std::size_t>(s)]; }
87 int8_t& coupled(int s) const { return g.coupled[static_cast<std::size_t>(s)]; }
88 int8_t& state(int s) const { return g.state[static_cast<std::size_t>(s)]; }
89 float& th(int s) const { return g.th[static_cast<std::size_t>(s)]; }
90 float& w_bc(int s) const { return g.w_bc[static_cast<std::size_t>(s)]; }
91 float& w_n1(int s) const { return g.w_n1[static_cast<std::size_t>(s)]; }
92 float& w_n2(int s) const { return g.w_n2[static_cast<std::size_t>(s)]; }
93 float& wm_n1(int s) const { return g.wm_n1[static_cast<std::size_t>(s)]; }
94 float& wm_n2(int s) const { return g.wm_n2[static_cast<std::size_t>(s)]; }
95};
96} // namespace detail
97
109template <unsigned Bits>
112 const std::vector<double>& sdfC, int matrixOrder,
113 int rhsOrder, bool* bandViolation = nullptr) {
114 GhostOverlay ov;
115 const Index n = t.numLeaves();
116 auto sf = [&](Index j) {
117 return j >= 0 ? static_cast<float>(sdfC[static_cast<std::size_t>(j)]) : -1.0f;
118 };
119 // scratch single row appended per accepted cell
120 for (Index i = 0; i < n; ++i) {
121 if (!(sdfC[static_cast<std::size_t>(i)] > 0.0))
122 continue; // solid-centered: decoupled row (phi = 0), not in the overlay
123 Index chain[3][5];
124 bool clean = true;
125 for (int a = 0; a < 3; ++a) {
126 chain[a][2] = i;
127 chain[a][3] = pres.periodicNeighbor(i, a, +1);
128 chain[a][1] = pres.periodicNeighbor(i, a, -1);
129 const float c1 = sf(chain[a][1]), c3 = sf(chain[a][3]), c2 = sf(i);
130 clean = clean && c1 >= 0.0f && c3 >= 0.0f && 0.5f * (c1 + c2) >= 0.0f &&
131 0.5f * (c2 + c3) >= 0.0f;
132 }
133 if (clean)
134 continue;
135 // Non-clean ⇒ cut band ⇒ the finest-band contract must hold: the whole ±2 reach same-level.
136 const unsigned Li = t.level(i);
137 bool bad = false;
138 for (int a = 0; a < 3; ++a) {
139 chain[a][4] =
140 chain[a][3] >= 0 ? pres.periodicNeighbor(chain[a][3], a, +1) : chain[a][3];
141 chain[a][0] =
142 chain[a][1] >= 0 ? pres.periodicNeighbor(chain[a][1], a, -1) : chain[a][1];
143 for (int q = 0; q < 5; ++q)
144 if (chain[a][q] < 0 || pres.levelOf(chain[a][q]) != Li)
145 bad = true;
146 }
147 if (bad) {
148 if (!bandViolation)
149 throw std::runtime_error(
150 "amr ghost projection: an overlay row's ±2 closure reach crosses a 2:1 level "
151 "boundary — the cut band is too thin; widen the refineToSdf band margin");
152 *bandViolation = true;
153 continue;
154 }
155 float F[3][4], Cq[3][5];
156 for (int a = 0; a < 3; ++a) {
157 for (int q = 0; q < 5; ++q)
158 Cq[a][q] = sf(chain[a][q]);
159 for (int m = 0; m < 4; ++m) // face i+m-1 = mean of centers q=m-1, q=m (slots m, m+1)
160 F[a][m] = 0.5f * (Cq[a][m] + Cq[a][m + 1]);
161 }
162 const int slot = static_cast<int>(ov.n);
163 ov.cell.resize(static_cast<std::size_t>(slot) + 1);
164 ov.rescale.resize(static_cast<std::size_t>(slot) + 1);
165 ov.coupled.resize(static_cast<std::size_t>(slot) + 1);
166 ov.state.resize(static_cast<std::size_t>(slot + 1) * 6);
167 ov.th.resize(static_cast<std::size_t>(slot + 1) * 6);
168 ov.w_bc.resize(static_cast<std::size_t>(slot + 1) * 6);
169 ov.w_n1.resize(static_cast<std::size_t>(slot + 1) * 6);
170 ov.w_n2.resize(static_cast<std::size_t>(slot + 1) * 6);
171 ov.wm_n1.resize(static_cast<std::size_t>(slot + 1) * 6);
172 ov.wm_n2.resize(static_cast<std::size_t>(slot + 1) * 6);
174 if (!scheme::gpFillRow(ref, slot, i, F, Cq, matrixOrder, rhsOrder)) {
175 // pre-check said non-clean but every face classified COUPLED (cannot normally happen —
176 // conservative float edge); drop the provisional row.
177 ov.cell.resize(static_cast<std::size_t>(slot));
178 ov.rescale.resize(static_cast<std::size_t>(slot));
179 ov.coupled.resize(static_cast<std::size_t>(slot));
180 ov.state.resize(static_cast<std::size_t>(slot) * 6);
181 ov.th.resize(static_cast<std::size_t>(slot) * 6);
182 ov.w_bc.resize(static_cast<std::size_t>(slot) * 6);
183 ov.w_n1.resize(static_cast<std::size_t>(slot) * 6);
184 ov.w_n2.resize(static_cast<std::size_t>(slot) * 6);
185 ov.wm_n1.resize(static_cast<std::size_t>(slot) * 6);
186 ov.wm_n2.resize(static_cast<std::size_t>(slot) * 6);
187 continue;
188 }
189 for (int a = 0; a < 3; ++a)
190 for (int q = 0; q < 5; ++q)
191 ov.nbr.push_back(chain[a][q]);
192 ov.invh.push_back(1.0 / pres.cellWidth(i));
193 ++ov.n;
194 }
195 return ov;
196}
197
204template <class SdfFn>
205inline auto makeBinaryOpenFn(SdfFn sdfFn, double h0) {
206 return [sdfFn, h0](const Vec<3>& fc, int axis) -> double {
207 Vec<3> pm = fc, pp = fc;
208 pm[axis] -= 0.5 * h0;
209 pp[axis] += 0.5 * h0;
210 const double sm = sdfFn(pm), sp = sdfFn(pp);
211 const float fm = static_cast<float>(sm), fp = static_cast<float>(sp);
212 return (sm > 0.0 && sp > 0.0 && 0.5f * (fm + fp) >= 0.0f) ? 1.0 : 0.0;
213 };
214}
215
225template <unsigned Bits>
226inline std::vector<char> findPocketCells(const BlockOctree<3, Bits>& t,
228 const std::vector<double>& sdfC) {
229 const Index n = t.numLeaves();
230 std::vector<char> pocket(static_cast<std::size_t>(n), 0);
231 std::vector<int> comp(static_cast<std::size_t>(n), -1);
232 auto fluid = [&](Index i) { return sdfC[static_cast<std::size_t>(i)] > 0.0; };
233 int nc = 0;
234 std::vector<Index> stack, compSize;
235 for (Index s = 0; s < n; ++s) {
236 if (!fluid(s) || comp[static_cast<std::size_t>(s)] >= 0)
237 continue;
238 const int id = nc++;
239 compSize.push_back(0);
240 stack.assign(1, s);
241 comp[static_cast<std::size_t>(s)] = id;
242 while (!stack.empty()) {
243 const Index i = stack.back();
244 stack.pop_back();
245 ++compSize[static_cast<std::size_t>(id)];
246 pres.forEachFaceNeighbor(i, [&](Index j, Real, int, double alpha) {
247 if (alpha > 0.5 && fluid(j) && comp[static_cast<std::size_t>(j)] < 0) {
248 comp[static_cast<std::size_t>(j)] = id;
249 stack.push_back(j);
250 }
251 });
252 }
253 }
254 if (nc <= 1)
255 return pocket;
256 int mainId = 0;
257 for (int c = 1; c < nc; ++c)
258 if (compSize[static_cast<std::size_t>(c)] > compSize[static_cast<std::size_t>(mainId)])
259 mainId = c;
260 Index nPocket = 0;
261 for (Index i = 0; i < n; ++i)
262 if (fluid(i) && comp[static_cast<std::size_t>(i)] != mainId) {
263 pocket[static_cast<std::size_t>(i)] = 1;
264 ++nPocket;
265 }
266 std::fprintf(stderr,
267 "[peclet.core.amr] ghost projection: binary coupled-face graph fragments into %d "
268 "components — decoupling %lld pocket cells outside the main component\n",
269 nc, static_cast<long long>(nPocket));
270 return pocket;
271}
272
273// ---- host (oracle) delta appliers --------------------------------------------------------------
274
277inline void ghostApplyDeltaHost(const GhostOverlay& ov, const std::vector<double>& x,
278 std::vector<double>& y) {
279 for (Index r = 0; r < ov.n; ++r) {
280 const std::size_t rr = static_cast<std::size_t>(r);
281 const Index c = ov.cell[rr];
282 if (!ov.coupled[rr]) {
283 y[static_cast<std::size_t>(c)] = 0.0;
284 continue;
285 }
286 auto X = [&](int a, int q) {
287 return x[static_cast<std::size_t>(ov.nbr[rr * 15 + static_cast<std::size_t>(a) * 5 +
288 static_cast<std::size_t>(q + 2)])];
289 };
290 double delta = 0.0;
291 for (int k = 0; k < 6; ++k) {
292 const int8_t st = ov.state[rr * 6 + static_cast<std::size_t>(k)];
293 if (st != scheme::GP_QUAD && st != scheme::GP_LIN)
294 continue;
295 const int a = k / 2;
296 const int sgn = (k & 1) ? -1 : 1; // odd k = minus side
297 const int mn = (k & 1) ? 1 : 0; // near-face relative index
298 const int mf = (k & 1) ? 2 : -1; // far-face relative index
299 const double w1 = ov.wm_n1[rr * 6 + static_cast<std::size_t>(k)];
300 const double w2 = ov.wm_n2[rr * 6 + static_cast<std::size_t>(k)];
301 delta += sgn * w1 * (X(a, mn) - X(a, mn - 1)); // +axis face gradient — AMR L sign
302 if (st == scheme::GP_QUAD && w2 != 0.0)
303 delta += sgn * w2 * (X(a, mf) - X(a, mf - 1));
304 }
305 const double ih = ov.invh[rr];
306 y[static_cast<std::size_t>(c)] =
307 ov.rescale[rr] * (y[static_cast<std::size_t>(c)] + ih * ih * delta);
308 }
309}
310
313inline void ghostDivergDeltaHost(const GhostOverlay& ov,
314 const std::array<std::vector<double>, 3>& u,
315 std::vector<double>& d) {
316 for (Index r = 0; r < ov.n; ++r) {
317 const std::size_t rr = static_cast<std::size_t>(r);
318 const Index c = ov.cell[rr];
319 if (!ov.coupled[rr]) {
320 d[static_cast<std::size_t>(c)] = 0.0;
321 continue;
322 }
323 auto U = [&](int a, int m) { // face-averaged value at face index i+m along axis a
324 const Index cm = ov.nbr[rr * 15 + static_cast<std::size_t>(a) * 5 +
325 static_cast<std::size_t>(m + 1)]; // q = m-1
326 const Index cp = ov.nbr[rr * 15 + static_cast<std::size_t>(a) * 5 +
327 static_cast<std::size_t>(m + 2)]; // q = m
328 return 0.5 * (u[static_cast<std::size_t>(a)][static_cast<std::size_t>(cm)] +
329 u[static_cast<std::size_t>(a)][static_cast<std::size_t>(cp)]);
330 };
331 double dd = 0.0;
332 for (int k = 0; k < 6; ++k) {
333 const int8_t st = ov.state[rr * 6 + static_cast<std::size_t>(k)];
334 if (st == scheme::GP_COUPLED)
335 continue;
336 const int a = k / 2;
337 const int sgn = (k & 1) ? -1 : 1;
338 const int mg = (k & 1) ? 0 : 1; // the closed face's own index
339 const int mn = (k & 1) ? 1 : 0;
340 const int mf = (k & 1) ? 2 : -1;
341 if (st == scheme::GP_EXPLICIT) {
342 dd += sgn * U(a, mg); // sliver without crossing: explicit u* flux
343 continue;
344 }
345 if (st == scheme::GP_BC_ONLY)
346 continue; // u_bc = 0
347 double val = ov.w_n1[rr * 6 + static_cast<std::size_t>(k)] * U(a, mn);
348 if (st == scheme::GP_QUAD)
349 val += ov.w_n2[rr * 6 + static_cast<std::size_t>(k)] * U(a, mf);
350 dd += sgn * val;
351 }
352 d[static_cast<std::size_t>(c)] =
353 ov.rescale[rr] * (d[static_cast<std::size_t>(c)] + ov.invh[rr] * dd);
354 }
355}
356
357// ---- device mirror + kernels (Kokkos TUs only; include after a Kokkos-carrying header) ---------
358#ifdef KOKKOS_INLINE_FUNCTION
359
369
372 d.n = h.n;
373 if (h.n == 0)
374 return d;
375 d.cell = toDevice(h.cell, "gp_cell");
376 d.nbr = toDevice(h.nbr, "gp_nbr");
377 d.rescale = toDevice(h.rescale, "gp_rescale");
378 d.coupled = toDevice(h.coupled, "gp_coupled");
379 d.state = toDevice(h.state, "gp_state");
380 d.w_n1 = toDevice(h.w_n1, "gp_wn1");
381 d.w_n2 = toDevice(h.w_n2, "gp_wn2");
382 d.wm_n1 = toDevice(h.wm_n1, "gp_wmn1");
383 d.wm_n2 = toDevice(h.wm_n2, "gp_wmn2");
384 d.invh = toDevice(h.invh, "gp_invh");
385 return d;
386}
387
390 if (ov.n == 0)
391 return;
392 auto cell = ov.cell;
393 auto nbr = ov.nbr;
394 auto resc = ov.rescale;
395 auto cpl = ov.coupled;
396 auto st = ov.state;
397 auto wm1 = ov.wm_n1;
398 auto wm2 = ov.wm_n2;
399 auto invh = ov.invh;
400 Kokkos::parallel_for(
401 "amr::gp_apply_delta", ov.n, KOKKOS_LAMBDA(const Index r) {
402 const Index c = cell(r);
403 if (!cpl(r)) {
404 y(c) = 0.0;
405 return;
406 }
407 double delta = 0.0;
408 for (int k = 0; k < 6; ++k) {
409 const int8_t s = st(r * 6 + k);
410 if (s != scheme::GP_QUAD && s != scheme::GP_LIN)
411 continue;
412 const int a = k / 2;
413 const int sgn = (k & 1) ? -1 : 1;
414 const int mn = (k & 1) ? 1 : 0;
415 const int mf = (k & 1) ? 2 : -1;
416 const double w1 = wm1(r * 6 + k), w2 = wm2(r * 6 + k);
417 delta += sgn * w1 * (x(nbr(r * 15 + a * 5 + mn + 2)) - x(nbr(r * 15 + a * 5 + mn + 1)));
418 if (s == scheme::GP_QUAD && w2 != 0.0)
419 delta +=
420 sgn * w2 * (x(nbr(r * 15 + a * 5 + mf + 2)) - x(nbr(r * 15 + a * 5 + mf + 1)));
421 }
422 const double ih = invh(r);
423 y(c) = resc(r) * (y(c) + ih * ih * delta);
424 });
425}
426
430 if (ov.n == 0)
431 return;
432 auto cell = ov.cell;
433 auto nbr = ov.nbr;
434 auto resc = ov.rescale;
435 auto cpl = ov.coupled;
436 auto st = ov.state;
437 auto w1v = ov.w_n1;
438 auto w2v = ov.w_n2;
439 auto invh = ov.invh;
440 Kokkos::parallel_for(
441 "amr::gp_diverg_delta", ov.n, KOKKOS_LAMBDA(const Index r) {
442 const Index c = cell(r);
443 if (!cpl(r)) {
444 d(c) = 0.0;
445 return;
446 }
447 auto U = [&](int a, int m) {
448 const Index cm = nbr(r * 15 + a * 5 + m + 1);
449 const Index cp = nbr(r * 15 + a * 5 + m + 2);
450 const double vm = (a == 0) ? u0(cm) : (a == 1) ? u1(cm) : u2(cm);
451 const double vp = (a == 0) ? u0(cp) : (a == 1) ? u1(cp) : u2(cp);
452 return 0.5 * (vm + vp);
453 };
454 double dd = 0.0;
455 for (int k = 0; k < 6; ++k) {
456 const int8_t s = st(r * 6 + k);
457 if (s == scheme::GP_COUPLED)
458 continue;
459 const int a = k / 2;
460 const int sgn = (k & 1) ? -1 : 1;
461 const int mg = (k & 1) ? 0 : 1;
462 const int mn = (k & 1) ? 1 : 0;
463 const int mf = (k & 1) ? 2 : -1;
464 if (s == scheme::GP_EXPLICIT) {
465 dd += sgn * U(a, mg);
466 continue;
467 }
468 if (s == scheme::GP_BC_ONLY)
469 continue;
470 double val = w1v(r * 6 + k) * U(a, mn);
471 if (s == scheme::GP_QUAD)
472 val += w2v(r * 6 + k) * U(a, mf);
473 dd += sgn * val;
474 }
475 d(c) = resc(r) * (d(c) + invh(r) * dd);
476 });
477}
478
479#endif // KOKKOS_INLINE_FUNCTION
480
481} // namespace peclet::core::amr
482
483#endif // PECLET_CORE_HAVE_MORTON
484#endif // PECLET_CORE_AMR_GHOST_PROJECTION_HPP
Cell-centered FV Poisson operator on one (periodic) block octree.
Definition poisson.hpp:44
Per-block adaptive octree over block-local Morton codes.
GhostOverlay buildGhostOverlay(const BlockOctree< 3, Bits > &t, const AmrPoisson< 3, Bits > &pres, const std::vector< double > &sdfC, int matrixOrder, int rhsOrder, bool *bandViolation=nullptr)
Build the overlay from the octree + the cell-centered SDF samples (AmrCutCell::sdfCRaw — EXTENDED ove...
GhostOverlayDev uploadGhostOverlay(const GhostOverlay &h)
std::vector< std::array< double, Dim > > transferFieldGradients(const BlockOctree< Dim, Bits > &oldT, const std::vector< double > &oldF)
Per-old-leaf minmod prolongation gradients (per fine-coordinate unit) — transferField's stencil,...
Definition adapt.hpp:53
void ghostApplyDelta(const GhostOverlayDev &ov, View< const double > x, View< double > y)
Device matrix overlay (== ghostApplyDeltaHost). Distinct rows per thread: no atomics.
std::vector< char > findPocketCells(const BlockOctree< 3, Bits > &t, const AmrPoisson< 3, Bits > &pres, const std::vector< double > &sdfC)
Fragmentation guard (the AMR port of flow's host-BFS pocket guard): the BINARY coupled-face graph (a ...
void ghostDivergDeltaHost(const GhostOverlay &ov, const std::array< std::vector< double >, 3 > &u, std::vector< double > &d)
Divergence overlay: d currently holds the BINARY divergence (physical); overwrite overlay rows with d...
void ghostApplyDeltaHost(const GhostOverlay &ov, const std::vector< double > &x, std::vector< double > &y)
Matrix overlay: y currently holds the BINARY L matvec; overwrite the overlay rows with y = rho·(y + i...
void ghostDivergDelta(const GhostOverlayDev &ov, View< const double > u0, View< const double > u1, View< const double > u2, View< double > d)
Device divergence overlay (== ghostDivergDeltaHost).
auto makeBinaryOpenFn(SdfFn sdfFn, double h0)
Binary openness callable factory for the MG surrogate: a face is open iff both adjacent centers (prob...
PECLET_CORE_GP_HD bool gpFillRow(const OV &ov, int slot, CellId cellId, const float F[3][4], const float Cq[3][5], int matrixOrder, int rhsOrder, const float *exStd=nullptr, const float *exSliver=nullptr)
Fill one overlay row from the per-axis sample sets.
std::array< Real, Dim > Vec
Multi-dimensional real vector.
Definition types.hpp:26
View< T > toDevice(const std::vector< T > &h, const std::string &label)
Upload a host std::vector into a freshly-sized device View (empty vector => empty view).
Definition view.hpp:44
Kokkos::View< T *, MemSpace > View
1D device array.
Definition view.hpp:26
double Real
Default host floating type. Device kernels may use float; conversions happen at the boundary.
Definition types.hpp:18
std::int64_t Index
Signed index type for grids and particles (supersedes block_decomposer's long int IndxT).
Definition types.hpp:15
Device mirror of GhostOverlay (uploaded once per setSolid).
Host ghost-projection overlay: one row per non-clean fluid leaf (== cut cell: some ±1 center sample s...
std::vector< Index > nbr
[n*15] ±2 neighbour chain per axis
std::vector< float > w_n2
[n*6] RHS/diagnostic closure weights (rhsOrder)
std::vector< float > rescale
[n] rho = min(1, min_f D_f) of the MATRIX weights
std::vector< int8_t > state
[n*6]
std::vector< float > wm_n2
[n*6] matrix (implicit phi) weights (matrixOrder)
std::vector< double > invh
[n] 1/cellWidth of the row (finest band)
std::vector< Index > cell
[n] leaf index
std::vector< float > th
[n*6] (diagnostics)
std::vector< int8_t > coupled
[n] 1 if the row has any phi coupling at all