flow 1.3.0
Kokkos cut-cell IBM incompressible Navier-Stokes solver + pnm pore extraction
Loading...
Searching...
No Matches
ghost_projection.hpp
Go to the documentation of this file.
1
65#ifndef PECLET_FLOW_GHOST_PROJECTION_HPP
66#define PECLET_FLOW_GHOST_PROJECTION_HPP
67
68#include <cstdint>
69#include <Kokkos_Core.hpp>
70
71#include "cut_cell_ibm.hpp" // poly_D / poly_Nc / poly_N_nb (momentum IBM)
72#include "mac_cutcell.hpp" // CCField/CCConst, C3, CCExec
73// The PURE per-face closure pieces (state cascade, closure weights, row fill) are shared with
74// core's AMR octree band — lifted verbatim into peclet::core::scheme (float arithmetic identical
75// to the definitions that lived here; Kokkos_Core.hpp is included above, so they compile as
76// KOKKOS_INLINE_FUNCTION). The grid-specific parts (overlay SoA, periodic wrap, delta kernels,
77// gpCenterGrad) stay here.
78#include "gauge_exact_gradient.hpp" // gpCenterGrad (moved out: the production scheme needs it)
79#include "peclet/core/scheme/ghost_closure.hpp"
80#include "policy.hpp"
81
82namespace peclet::flow {
83
84using peclet::core::scheme::GP_THETA_MIN;
85using enum peclet::core::scheme::GpState;
86using peclet::core::scheme::gpClassifyFace;
87using peclet::core::scheme::GpFace;
88using peclet::core::scheme::gpFillRow;
89using peclet::core::scheme::gpOrderWeights;
90using peclet::core::scheme::GpState;
91
100template <class Space, class Real = float>
102 Kokkos::View<int*, Space> cell; // packed INNER flat index x + y*nx + z*nx*ny
103 Kokkos::View<Real*, Space> rescale; // rho = min(1, min_f D_f) of the MATRIX weights
104 Kokkos::View<int8_t*, Space> coupled; // 1 if the row has any phi coupling at all
105 Kokkos::View<int8_t*, Space> state; // [slot*6+k]
106 Kokkos::View<Real*, Space> th; // [slot*6+k] (parity/diagnostics)
107 Kokkos::View<Real*, Space> w_bc, w_n1, w_n2; // [slot*6+k] RHS/diagnostic closure weights
108 Kokkos::View<Real*, Space> wm_n1, wm_n2; // [slot*6+k] matrix (implicit phi) weights
109};
110template <class Real>
113
114template <class Real = float>
117 ov.cell = Kokkos::View<int*, CCMem>("gp_cell", n);
118 ov.rescale = Kokkos::View<Real*, CCMem>("gp_rescale", n);
119 ov.coupled = Kokkos::View<int8_t*, CCMem>("gp_coupled", n);
120 ov.state = Kokkos::View<int8_t*, CCMem>("gp_state", 6 * n);
121 ov.th = Kokkos::View<Real*, CCMem>("gp_th", 6 * n);
122 ov.w_bc = Kokkos::View<Real*, CCMem>("gp_wbc", 6 * n);
123 ov.w_n1 = Kokkos::View<Real*, CCMem>("gp_wn1", 6 * n);
124 ov.w_n2 = Kokkos::View<Real*, CCMem>("gp_wn2", 6 * n);
125 ov.wm_n1 = Kokkos::View<Real*, CCMem>("gp_wmn1", 6 * n);
126 ov.wm_n2 = Kokkos::View<Real*, CCMem>("gp_wmn2", 6 * n);
127 return ov;
128}
129
131 v %= n;
132 return v < 0 ? v + n : v;
133}
134
142template <class Real>
143inline int buildGpOverlay(CCConst sdf, C3 ext, int g, C3 nn, const GpOverlayReal<Real>& ov,
144 Kokkos::View<int*, CCMem> idMap, Kokkos::View<int, CCMem> counter,
145 int matrixOrder = 2, int rhsOrder = 2, CCConst tx = CCConst(),
146 CCConst ty = CCConst(), CCConst tz = CCConst(), bool useGhost = false) {
148 Kokkos::deep_copy(space, counter, 0);
149 Kokkos::deep_copy(space, idMap, -1);
150 const bool hasEx = tx.size() > 0;
151 const bool ug = useGhost;
152 using MD = MDRange3<CCExec>;
153 Kokkos::parallel_for(
154 "peclet::flow::gp_build_overlay", MD(space, {0, 0, 0}, {nn.x, nn.y, nn.z}),
155 KOKKOS_LAMBDA(int x, int y, int z) {
156 auto S = [&](int dx, int dy, int dz) {
157 const long i = (long)((ug ? x + dx : gpWrap(x + dx, nn.x)) + g) +
158 (long)((ug ? y + dy : gpWrap(y + dy, nn.y)) + g) * ext.x +
159 (long)((ug ? z + dz : gpWrap(z + dz, nn.z)) + g) * (long)ext.x * ext.y;
160 return (float)sdf(i); // PRECISION-EXEMPT: feeds core::scheme::gpFillRow, float-hardcoded
161 };
162 const float sc = S(0, 0, 0);
163 if (sc < 0.0f)
164 return; // solid-centered: decoupled row (phi = 0), not in the overlay
165 float F[3][4], Cq[3][5];
166 const int d[3][3] = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}};
167 for (int a = 0; a < 3; ++a) {
168 for (int q = -2; q <= 2; ++q)
169 Cq[a][q + 2] = S(q * d[a][0], q * d[a][1], q * d[a][2]);
170 for (int m = -1; m <= 2; ++m) // face i+m = mean of centers i+m-1, i+m
171 F[a][m + 1] = 0.5f * (Cq[a][m + 1] + Cq[a][m + 2]);
172 }
173 // cheap pre-check: fully interior rows (all six 1st neighbors fluid on faces+centers)
174 bool clean = true;
175 for (int a = 0; a < 3; ++a)
176 clean =
177 clean && F[a][1] >= 0.0f && F[a][2] >= 0.0f && Cq[a][1] >= 0.0f && Cq[a][3] >= 0.0f;
178 if (clean)
179 return;
180 const int inner = x + y * nn.x + z * nn.x * nn.y;
181 // Optional analytic-SDF exact crossings (setExactCrossings): t_a(j) = exact crossing
182 // fraction from same-component face point j toward j + e_a along axis a (NaN = none).
183 // minus side: std theta = 1 - t_a(i); sliver theta = 2 - t_a(i-1)
184 // plus side: std theta = t_a(i); sliver theta = 1 + t_a(i+1)
185 float exStd[6], exSliver[6];
186 if (hasEx) {
187 const CCConst* ta[3] = {&tx, &ty, &tz};
188 for (int a = 0; a < 3; ++a) {
189 auto T = [&](int m) {
190 const int cx = a == 0 ? gpWrap(x + m, nn.x) : x;
191 const int cy = a == 1 ? gpWrap(y + m, nn.y) : y;
192 const int cz = a == 2 ? gpWrap(z + m, nn.z) : z;
193 const long ii = (long)cx + (long)cy * nn.x + (long)cz * (long)nn.x * nn.y;
194 return (float)(*ta[a])(ii); // PRECISION-EXEMPT: ditto
195 };
196 exStd[2 * a + 1] = 1.0f - T(0);
197 exSliver[2 * a + 1] = 2.0f - T(-1);
198 exStd[2 * a] = T(0);
199 exSliver[2 * a] = 1.0f + T(1);
200 }
201 }
202 const int slot = Kokkos::atomic_fetch_add(&counter(), 1);
203 if (!gpFillRow(ov, slot, inner, F, Cq, matrixOrder, rhsOrder, hasEx ? exStd : nullptr,
204 hasEx ? exSliver : nullptr)) {
205 // all faces COUPLED after all (pre-check was conservative): release the slot lazily by
206 // marking it inert (zero-weight row on its own cell).
207 ov.cell(slot) = inner;
208 ov.rescale(slot) = 1.0f;
209 ov.coupled(slot) = 1;
210 for (int k = 0; k < 6; ++k) {
211 ov.state(slot * 6 + k) = GP_COUPLED;
212 ov.th(slot * 6 + k) = 1.0f;
213 ov.w_bc(slot * 6 + k) = 0.0f;
214 ov.w_n1(slot * 6 + k) = 0.0f;
215 ov.w_n2(slot * 6 + k) = 0.0f;
216 ov.wm_n1(slot * 6 + k) = 0.0f;
217 ov.wm_n2(slot * 6 + k) = 0.0f;
218 }
219 } else {
220 idMap(inner) = slot;
221 }
222 });
223 int cnt = 0;
224 Kokkos::deep_copy(cnt, counter);
225 return cnt;
226}
227
232inline void gpBinaryOpenness(CCField ox, CCField oy, CCField oz, CCConst sdf, C3 ext) {
234 using MD = MDRange3<CCExec>;
235 Kokkos::parallel_for(
236 "peclet::flow::gp_binary_openness", MD(space, {0, 0, 0}, {ext.x, ext.y, ext.z}),
237 KOKKOS_LAMBDA(int x, int y, int z) {
238 const long sy = ext.x, sz = (long)ext.x * ext.y;
239 const long i = (long)x + (long)y * sy + (long)z * sz;
240 auto cl = [](int v, int n) { return v < 0 ? 0 : (v >= n ? n - 1 : v); };
241 auto S = [&](int xx, int yy, int zz) {
242 return sdf((long)cl(xx, ext.x) + (long)cl(yy, ext.y) * sy + (long)cl(zz, ext.z) * sz);
243 };
244 const double sc = S(x, y, z);
245 const double sw = S(x - 1, y, z), ss = S(x, y - 1, z), sb = S(x, y, z - 1);
246 ox(i) = (0.5 * (sw + sc) >= 0.0 && sw >= 0.0 && sc >= 0.0) ? 1.0 : 0.0;
247 oy(i) = (0.5 * (ss + sc) >= 0.0 && ss >= 0.0 && sc >= 0.0) ? 1.0 : 0.0;
248 oz(i) = (0.5 * (sb + sc) >= 0.0 && sb >= 0.0 && sc >= 0.0) ? 1.0 : 0.0;
249 });
250}
251
260template <class Real>
261inline void gpApplyDelta(CCField y, CCConst x, const GpOverlayReal<Real>& ov, int nOv, C3 nn,
262 C3 extY, int gbY, C3 extX, int gbX, bool useGhost = false) {
263 if (nOv <= 0)
264 return;
266 const bool ug = useGhost;
267 Kokkos::parallel_for(
268 "peclet::flow::gp_apply_delta", Kokkos::RangePolicy<CCExec>(space, 0, nOv),
269 KOKKOS_LAMBDA(int s) {
270 const int inner = ov.cell(s);
271 const int ix = inner % nn.x, iy = (inner / nn.x) % nn.y, iz = inner / (nn.x * nn.y);
272 auto X = [&](int a, int q) { // phi at cell offset q along axis a
273 const int cx = a == 0 ? (ug ? ix + q : gpWrap(ix + q, nn.x)) : ix;
274 const int cy = a == 1 ? (ug ? iy + q : gpWrap(iy + q, nn.y)) : iy;
275 const int cz = a == 2 ? (ug ? iz + q : gpWrap(iz + q, nn.z)) : iz;
276 return x((long)(cx + gbX) + (long)(cy + gbX) * extX.x +
277 (long)(cz + gbX) * (long)extX.x * extX.y);
278 };
279 const long r =
280 (long)(ix + gbY) + (long)(iy + gbY) * extY.x + (long)(iz + gbY) * (long)extY.x * extY.y;
281 double delta = 0.0;
282 for (int k = 0; k < 6; ++k) {
283 const int8_t st = ov.state(s * 6 + k);
284 if (st != GP_QUAD && st != GP_LIN)
285 continue;
286 const int a = k / 2;
287 const int sgn = (k & 1) ? -1 : 1; // odd k = minus side
288 const int mn = (k & 1) ? 1 : 0; // near-face relative index
289 const int mf = (k & 1) ? 2 : -1; // far-face relative index
290 const double w1 = ov.wm_n1(s * 6 + k), w2 = ov.wm_n2(s * 6 + k);
291 delta += sgn * w1 * (X(a, mn - 1) - X(a, mn));
292 if (st == GP_QUAD && w2 != 0.0)
293 delta += sgn * w2 * (X(a, mf - 1) - X(a, mf));
294 }
295 const double rho = ov.rescale(s);
296 y(r) = ov.coupled(s) ? rho * (y(r) + delta) : 0.0;
297 });
298}
299
307template <class Real>
309 int nOv, C3 nn, C3 extb, int gb, bool useGhost = false) {
310 if (nOv <= 0)
311 return;
313 const bool ug = useGhost;
314 Kokkos::parallel_for(
315 "peclet::flow::gp_diverg_delta", Kokkos::RangePolicy<CCExec>(space, 0, nOv),
316 KOKKOS_LAMBDA(int s) {
317 const int inner = ov.cell(s);
318 const int ix = inner % nn.x, iy = (inner / nn.x) % nn.y, iz = inner / (nn.x * nn.y);
319 auto U = [&](int a, int m) { // face-field value at face index i+m along axis a
320 const int cx = a == 0 ? (ug ? ix + m : gpWrap(ix + m, nn.x)) : ix;
321 const int cy = a == 1 ? (ug ? iy + m : gpWrap(iy + m, nn.y)) : iy;
322 const int cz = a == 2 ? (ug ? iz + m : gpWrap(iz + m, nn.z)) : iz;
323 const long i =
324 (long)(cx + gb) + (long)(cy + gb) * extb.x + (long)(cz + gb) * (long)extb.x * extb.y;
325 return a == 0 ? u(i) : (a == 1 ? v(i) : w(i));
326 };
327 const long r =
328 (long)(ix + gb) + (long)(iy + gb) * extb.x + (long)(iz + gb) * (long)extb.x * extb.y;
329 double dd = d(r);
330 for (int k = 0; k < 6; ++k) {
331 const int8_t st = ov.state(s * 6 + k);
332 if (st == GP_COUPLED)
333 continue;
334 const int a = k / 2;
335 const int sgn = (k & 1) ? -1 : 1;
336 const int mg = (k & 1) ? 0 : 1; // the closed face's own index
337 const int mn = (k & 1) ? 1 : 0;
338 const int mf = (k & 1) ? 2 : -1;
339 if (st == GP_EXPLICIT) {
340 dd += sgn * U(a, mg); // sliver without crossing: explicit u* flux
341 continue;
342 }
343 if (st == GP_BC_ONLY)
344 continue; // u_bc = 0 (v1); w_bc kept in the overlay for moving walls
345 double val = ov.w_n1(s * 6 + k) * U(a, mn); // + w_bc*u_bc (= 0)
346 if (st == GP_QUAD)
347 val += ov.w_n2(s * 6 + k) * U(a, mf);
348 dd += sgn * val;
349 }
350 d(r) = ov.coupled(s) ? ov.rescale(s) * dd : 0.0;
351 });
352}
353
354} // namespace peclet::flow
355
356#endif // PECLET_FLOW_GHOST_PROJECTION_HPP
flow — portable (Kokkos) Robust-Scaled cut-cell IBM primitives + per-cut-cell overlay build.
flow — the gauge-exact directional cell-centre pressure gradient.
flow — portable (Kokkos) cut-cell pressure-operator face openness from an SDF.
void gpDivergDelta(CCField d, CCConst u, CCConst v, CCConst w, const GpOverlayReal< Real > &ov, int nOv, C3 nn, C3 extb, int gb, bool useGhost=false)
Overlay divergence correction: d(r) = rho_r * (d(r) + closure/BC/explicit face values),...
void gpBinaryOpenness(CCField ox, CCField oy, CCField oz, CCConst sdf, C3 ext)
Binary openness for the symmetric MG surrogate, on the extended-block layout of buildOpenness: o(face...
GpOverlayReal< Real > gpMakeOverlay(long n)
void gpApplyDelta(CCField y, CCConst x, const GpOverlayReal< Real > &ov, int nOv, C3 nn, C3 extY, int gbY, C3 extX, int gbX, bool useGhost=false)
Overlay matvec correction: y(r) = rho_r * (y(r) + closure-face phi terms), where y currently holds th...
int buildGpOverlay(CCConst sdf, C3 ext, int g, C3 nn, const GpOverlayReal< Real > &ov, Kokkos::View< int *, CCMem > idMap, Kokkos::View< int, CCMem > counter, int matrixOrder=2, int rhsOrder=2, CCConst tx=CCConst(), CCConst ty=CCConst(), CCConst tz=CCConst(), bool useGhost=false)
Build the overlay over the inner grid nn from the cell-centered sdf on the extended block (ext,...
int gpWrap(int v, int n)
Kokkos::View< double *, CCMem > CCField
Kokkos::DefaultExecutionSpace CCExec
Kokkos::View< const double *, CCMem > CCConst
flow — the multidimensional iteration policies: iteration order follows storage.
Kokkos::View< Real *, Space > th
Kokkos::View< int8_t *, Space > state
Kokkos::View< Real *, Space > w_n2
Kokkos::View< int8_t *, Space > coupled
Kokkos::View< int *, Space > cell
Kokkos::View< Real *, Space > wm_n2
Kokkos::View< Real *, Space > rescale
Kokkos::View< Real *, Space > w_n1
Kokkos::View< Real *, Space > w_bc
Kokkos::View< Real *, Space > wm_n1
static constexpr double F
static constexpr double U