flow 0.4.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
43#ifndef PECLET_FLOW_GHOST_PROJECTION_HPP
44#define PECLET_FLOW_GHOST_PROJECTION_HPP
45
46#include <Kokkos_Core.hpp>
47
48#include <cstdint>
49
50#include "cut_cell_ibm.hpp" // poly_D / poly_Nc / poly_N_nb (momentum IBM)
51#include "mac_cutcell.hpp" // CCField/CCConst, C3, CCExec
52// The PURE per-face closure pieces (state cascade, closure weights, row fill) are shared with
53// core's AMR octree band — lifted verbatim into peclet::core::scheme (float arithmetic identical
54// to the definitions that lived here; Kokkos_Core.hpp is included above, so they compile as
55// KOKKOS_INLINE_FUNCTION). The grid-specific parts (overlay SoA, periodic wrap, delta kernels,
56// gpCenterGrad) stay here.
57#include "peclet/core/scheme/ghost_closure.hpp"
58#include "gauge_exact_gradient.hpp" // gpCenterGrad (moved out: the production scheme needs it)
59
60namespace peclet::flow {
61
62using peclet::core::scheme::GP_THETA_MIN;
63using enum peclet::core::scheme::GpState;
64using peclet::core::scheme::GpFace;
65using peclet::core::scheme::GpState;
66using peclet::core::scheme::gpClassifyFace;
67using peclet::core::scheme::gpFillRow;
68using peclet::core::scheme::gpOrderWeights;
69
78template <class Space>
79struct GpOverlayT {
80 Kokkos::View<int*, Space> cell; // packed INNER flat index x + y*nx + z*nx*ny
81 Kokkos::View<float*, Space> rescale; // rho = min(1, min_f D_f) of the MATRIX weights
82 Kokkos::View<int8_t*, Space> coupled; // 1 if the row has any phi coupling at all
83 Kokkos::View<int8_t*, Space> state; // [slot*6+k]
84 Kokkos::View<float*, Space> th; // [slot*6+k] (parity/diagnostics)
85 Kokkos::View<float*, Space> w_bc, w_n1, w_n2; // [slot*6+k] RHS/diagnostic closure weights
86 Kokkos::View<float*, Space> wm_n1, wm_n2; // [slot*6+k] matrix (implicit phi) weights
87};
89
90inline GpOverlay gpMakeOverlay(long n) {
91 GpOverlay ov;
92 ov.cell = Kokkos::View<int*, CCMem>("gp_cell", n);
93 ov.rescale = Kokkos::View<float*, CCMem>("gp_rescale", n);
94 ov.coupled = Kokkos::View<int8_t*, CCMem>("gp_coupled", n);
95 ov.state = Kokkos::View<int8_t*, CCMem>("gp_state", 6 * n);
96 ov.th = Kokkos::View<float*, CCMem>("gp_th", 6 * n);
97 ov.w_bc = Kokkos::View<float*, CCMem>("gp_wbc", 6 * n);
98 ov.w_n1 = Kokkos::View<float*, CCMem>("gp_wn1", 6 * n);
99 ov.w_n2 = Kokkos::View<float*, CCMem>("gp_wn2", 6 * n);
100 ov.wm_n1 = Kokkos::View<float*, CCMem>("gp_wmn1", 6 * n);
101 ov.wm_n2 = Kokkos::View<float*, CCMem>("gp_wmn2", 6 * n);
102 return ov;
103}
104
106 v %= n;
107 return v < 0 ? v + n : v;
108}
109
117inline int buildGpOverlay(CCConst sdf, C3 ext, int g, C3 nn, const GpOverlay& ov,
118 Kokkos::View<int*, CCMem> idMap, Kokkos::View<int, CCMem> counter,
119 int matrixOrder = 2, int rhsOrder = 2, CCConst tx = CCConst(),
121 bool useGhost = false) {
123 Kokkos::deep_copy(space, counter, 0);
124 Kokkos::deep_copy(space, idMap, -1);
125 const bool hasEx = tx.size() > 0;
126 const bool ug = useGhost;
127 using MD = Kokkos::MDRangePolicy<CCExec, Kokkos::Rank<3>>;
128 Kokkos::parallel_for(
129 "peclet::flow::gp_build_overlay", MD(space, {0, 0, 0}, {nn.x, nn.y, nn.z}),
130 KOKKOS_LAMBDA(int x, int y, int z) {
131 auto S = [&](int dx, int dy, int dz) {
132 const long i = (long)((ug ? x + dx : gpWrap(x + dx, nn.x)) + g) +
133 (long)((ug ? y + dy : gpWrap(y + dy, nn.y)) + g) * ext.x +
134 (long)((ug ? z + dz : gpWrap(z + dz, nn.z)) + g) * (long)ext.x * ext.y;
135 return (float)sdf(i);
136 };
137 const float sc = S(0, 0, 0);
138 if (sc < 0.0f)
139 return; // solid-centered: decoupled row (phi = 0), not in the overlay
140 float F[3][4], Cq[3][5];
141 const int d[3][3] = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}};
142 for (int a = 0; a < 3; ++a) {
143 for (int q = -2; q <= 2; ++q)
144 Cq[a][q + 2] = S(q * d[a][0], q * d[a][1], q * d[a][2]);
145 for (int m = -1; m <= 2; ++m) // face i+m = mean of centers i+m-1, i+m
146 F[a][m + 1] = 0.5f * (Cq[a][m + 1] + Cq[a][m + 2]);
147 }
148 // cheap pre-check: fully interior rows (all six 1st neighbors fluid on faces+centers)
149 bool clean = true;
150 for (int a = 0; a < 3; ++a)
151 clean = clean && F[a][1] >= 0.0f && F[a][2] >= 0.0f && Cq[a][1] >= 0.0f &&
152 Cq[a][3] >= 0.0f;
153 if (clean)
154 return;
155 const int inner = x + y * nn.x + z * nn.x * nn.y;
156 // Optional analytic-SDF exact crossings (setExactCrossings): t_a(j) = exact crossing
157 // fraction from same-component face point j toward j + e_a along axis a (NaN = none).
158 // minus side: std theta = 1 - t_a(i); sliver theta = 2 - t_a(i-1)
159 // plus side: std theta = t_a(i); sliver theta = 1 + t_a(i+1)
160 float exStd[6], exSliver[6];
161 if (hasEx) {
162 const CCConst* ta[3] = {&tx, &ty, &tz};
163 for (int a = 0; a < 3; ++a) {
164 auto T = [&](int m) {
165 const int cx = a == 0 ? gpWrap(x + m, nn.x) : x;
166 const int cy = a == 1 ? gpWrap(y + m, nn.y) : y;
167 const int cz = a == 2 ? gpWrap(z + m, nn.z) : z;
168 return (float)(*ta[a])((long)cx + (long)cy * nn.x + (long)cz * (long)nn.x * nn.y);
169 };
170 exStd[2 * a + 1] = 1.0f - T(0);
171 exSliver[2 * a + 1] = 2.0f - T(-1);
172 exStd[2 * a] = T(0);
173 exSliver[2 * a] = 1.0f + T(1);
174 }
175 }
176 const int slot = Kokkos::atomic_fetch_add(&counter(), 1);
177 if (!gpFillRow(ov, slot, inner, F, Cq, matrixOrder, rhsOrder, hasEx ? exStd : nullptr,
178 hasEx ? exSliver : nullptr)) {
179 // all faces COUPLED after all (pre-check was conservative): release the slot lazily by
180 // marking it inert (zero-weight row on its own cell).
181 ov.cell(slot) = inner;
182 ov.rescale(slot) = 1.0f;
183 ov.coupled(slot) = 1;
184 for (int k = 0; k < 6; ++k) {
185 ov.state(slot * 6 + k) = GP_COUPLED;
186 ov.th(slot * 6 + k) = 1.0f;
187 ov.w_bc(slot * 6 + k) = 0.0f;
188 ov.w_n1(slot * 6 + k) = 0.0f;
189 ov.w_n2(slot * 6 + k) = 0.0f;
190 ov.wm_n1(slot * 6 + k) = 0.0f;
191 ov.wm_n2(slot * 6 + k) = 0.0f;
192 }
193 } else {
194 idMap(inner) = slot;
195 }
196 });
197 int cnt = 0;
198 Kokkos::deep_copy(cnt, counter);
199 return cnt;
200}
201
206inline void gpBinaryOpenness(CCField ox, CCField oy, CCField oz, CCConst sdf, C3 ext) {
208 using MD = Kokkos::MDRangePolicy<CCExec, Kokkos::Rank<3>>;
209 Kokkos::parallel_for(
210 "peclet::flow::gp_binary_openness", MD(space, {0, 0, 0}, {ext.x, ext.y, ext.z}),
211 KOKKOS_LAMBDA(int x, int y, int z) {
212 const long sy = ext.x, sz = (long)ext.x * ext.y;
213 const long i = (long)x + (long)y * sy + (long)z * sz;
214 auto cl = [](int v, int n) { return v < 0 ? 0 : (v >= n ? n - 1 : v); };
215 auto S = [&](int xx, int yy, int zz) {
216 return sdf((long)cl(xx, ext.x) + (long)cl(yy, ext.y) * sy + (long)cl(zz, ext.z) * sz);
217 };
218 const double sc = S(x, y, z);
219 const double sw = S(x - 1, y, z), ss = S(x, y - 1, z), sb = S(x, y, z - 1);
220 ox(i) = (0.5 * (sw + sc) >= 0.0 && sw >= 0.0 && sc >= 0.0) ? 1.0 : 0.0;
221 oy(i) = (0.5 * (ss + sc) >= 0.0 && ss >= 0.0 && sc >= 0.0) ? 1.0 : 0.0;
222 oz(i) = (0.5 * (sb + sc) >= 0.0 && sb >= 0.0 && sc >= 0.0) ? 1.0 : 0.0;
223 });
224}
225
234inline void gpApplyDelta(CCField y, CCConst x, const GpOverlay& ov, int nOv, C3 nn, C3 extY,
235 int gbY, C3 extX, int gbX, bool useGhost = false) {
236 if (nOv <= 0)
237 return;
239 const bool ug = useGhost;
240 Kokkos::parallel_for(
241 "peclet::flow::gp_apply_delta", Kokkos::RangePolicy<CCExec>(space, 0, nOv),
242 KOKKOS_LAMBDA(int s) {
243 const int inner = ov.cell(s);
244 const int ix = inner % nn.x, iy = (inner / nn.x) % nn.y, iz = inner / (nn.x * nn.y);
245 auto X = [&](int a, int q) { // phi at cell offset q along axis a
246 const int cx = a == 0 ? (ug ? ix + q : gpWrap(ix + q, nn.x)) : ix;
247 const int cy = a == 1 ? (ug ? iy + q : gpWrap(iy + q, nn.y)) : iy;
248 const int cz = a == 2 ? (ug ? iz + q : gpWrap(iz + q, nn.z)) : iz;
249 return x((long)(cx + gbX) + (long)(cy + gbX) * extX.x +
250 (long)(cz + gbX) * (long)extX.x * extX.y);
251 };
252 const long r = (long)(ix + gbY) + (long)(iy + gbY) * extY.x +
253 (long)(iz + gbY) * (long)extY.x * extY.y;
254 double delta = 0.0;
255 for (int k = 0; k < 6; ++k) {
256 const int8_t st = ov.state(s * 6 + k);
257 if (st != GP_QUAD && st != GP_LIN)
258 continue;
259 const int a = k / 2;
260 const int sgn = (k & 1) ? -1 : 1; // odd k = minus side
261 const int mn = (k & 1) ? 1 : 0; // near-face relative index
262 const int mf = (k & 1) ? 2 : -1; // far-face relative index
263 const double w1 = ov.wm_n1(s * 6 + k), w2 = ov.wm_n2(s * 6 + k);
264 delta += sgn * w1 * (X(a, mn - 1) - X(a, mn));
265 if (st == GP_QUAD && w2 != 0.0)
266 delta += sgn * w2 * (X(a, mf - 1) - X(a, mf));
267 }
268 const double rho = ov.rescale(s);
269 y(r) = ov.coupled(s) ? rho * (y(r) + delta) : 0.0;
270 });
271}
272
280inline void gpDivergDelta(CCField d, CCConst u, CCConst v, CCConst w, const GpOverlay& ov,
281 int nOv, C3 nn, C3 extb, int gb, bool useGhost = false) {
282 if (nOv <= 0)
283 return;
285 const bool ug = useGhost;
286 Kokkos::parallel_for(
287 "peclet::flow::gp_diverg_delta", Kokkos::RangePolicy<CCExec>(space, 0, nOv),
288 KOKKOS_LAMBDA(int s) {
289 const int inner = ov.cell(s);
290 const int ix = inner % nn.x, iy = (inner / nn.x) % nn.y, iz = inner / (nn.x * nn.y);
291 auto U = [&](int a, int m) { // face-field value at face index i+m along axis a
292 const int cx = a == 0 ? (ug ? ix + m : gpWrap(ix + m, nn.x)) : ix;
293 const int cy = a == 1 ? (ug ? iy + m : gpWrap(iy + m, nn.y)) : iy;
294 const int cz = a == 2 ? (ug ? iz + m : gpWrap(iz + m, nn.z)) : iz;
295 const long i = (long)(cx + gb) + (long)(cy + gb) * extb.x +
296 (long)(cz + gb) * (long)extb.x * extb.y;
297 return a == 0 ? u(i) : (a == 1 ? v(i) : w(i));
298 };
299 const long r = (long)(ix + gb) + (long)(iy + gb) * extb.x +
300 (long)(iz + gb) * (long)extb.x * extb.y;
301 double dd = d(r);
302 for (int k = 0; k < 6; ++k) {
303 const int8_t st = ov.state(s * 6 + k);
304 if (st == GP_COUPLED)
305 continue;
306 const int a = k / 2;
307 const int sgn = (k & 1) ? -1 : 1;
308 const int mg = (k & 1) ? 0 : 1; // the closed face's own index
309 const int mn = (k & 1) ? 1 : 0;
310 const int mf = (k & 1) ? 2 : -1;
311 if (st == GP_EXPLICIT) {
312 dd += sgn * U(a, mg); // sliver without crossing: explicit u* flux
313 continue;
314 }
315 if (st == GP_BC_ONLY)
316 continue; // u_bc = 0 (v1); w_bc kept in the overlay for moving walls
317 double val = ov.w_n1(s * 6 + k) * U(a, mn); // + w_bc*u_bc (= 0)
318 if (st == GP_QUAD)
319 val += ov.w_n2(s * 6 + k) * U(a, mf);
320 dd += sgn * val;
321 }
322 d(r) = ov.coupled(s) ? ov.rescale(s) * dd : 0.0;
323 });
324}
325
326} // namespace peclet::flow
327
328#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 gpApplyDelta(CCField y, CCConst x, const GpOverlay &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...
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...
GpOverlay gpMakeOverlay(long n)
int gpWrap(int v, int n)
void ibmFillEntry(const OV &o, int list_idx, int c_idx, float sdf_c, const float sdf_n[6], int bc_type, const float *thEx)
Kokkos::View< double *, CCMem > CCField
void gpDivergDelta(CCField d, CCConst u, CCConst v, CCConst w, const GpOverlay &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),...
Kokkos::DefaultExecutionSpace CCExec
Kokkos::View< const double *, CCMem > CCConst
int buildGpOverlay(CCConst sdf, C3 ext, int g, C3 nn, const GpOverlay &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,...
Kokkos::View< int8_t *, Space > state
Kokkos::View< float *, Space > w_n1
Kokkos::View< int *, Space > cell
Kokkos::View< float *, Space > w_n2
Kokkos::View< float *, Space > wm_n1
Kokkos::View< float *, Space > rescale
Kokkos::View< float *, Space > th
Kokkos::View< int8_t *, Space > coupled
Kokkos::View< float *, Space > wm_n2
Kokkos::View< float *, Space > w_bc
static constexpr double F