flow 0.4.0
Kokkos cut-cell IBM incompressible Navier-Stokes solver + pnm pore extraction
Loading...
Searching...
No Matches
mac_ibm.hpp
Go to the documentation of this file.
1
11#ifndef PECLET_FLOW_MAC_IBM_HPP
12#define PECLET_FLOW_MAC_IBM_HPP
13
14#include <Kokkos_Core.hpp>
15#include <type_traits>
16
17#include "cut_cell_ibm.hpp" // IbmOverlay, ibmFillEntry
18#include "mac_cutcell.hpp" // peclet::flow::C3, peclet::flow::ccSampleExt, CCConst
19
20namespace peclet::flow {
21
22using mreal = float; // matrix coefficient type (matches cfd's mreal)
23using MConst = Kokkos::View<const float*, CCMem>;
24
25struct Off3 {
26 float x, y, z;
27};
28
29// Cut cell = fluid centre with at least one solid axis neighbour.
30KOKKOS_INLINE_FUNCTION bool ibmIsCut(float sc, const float sn[6]) {
31 if (sc <= 0.0f)
32 return false;
33 for (int k = 0; k < 6; ++k)
34 if (sn[k] < 0.0f)
35 return true;
36 return false;
37}
38
39// Find cut cells over the inner block and build the Robust-Scaled overlay (port of ibm_count_ext_k
40// + ibm_geometry_ext_k): per inner cell, gather the 7 staggered SDF samples; if cut, atomically
41// claim a slot, set idMap[cell]=slot, and fill the overlay. counter/idMap are reset here. Returns
42// the cut count (overlay arrays must be sized >= number of inner cells). bc_type 0=Dirichlet,
43// 1=Neumann.
44template <int SCHEME>
45inline int buildIbmOverlay(CCConst sdf, C3 ext, int g, Off3 off, int bc_type, const IbmOverlay& ov,
46 Kokkos::View<int*, CCMem> idMap, Kokkos::View<int, CCMem> counter,
48 C3 nn = C3{0, 0, 0}) {
50 Kokkos::deep_copy(space, counter, 0);
51 Kokkos::deep_copy(space, idMap, -1);
52 const bool hasEx = tx.size() > 0;
53 using MD = Kokkos::MDRangePolicy<CCExec, Kokkos::Rank<3>>;
54 Kokkos::parallel_for(
55 "peclet::flow::ibm_build_overlay", MD(space, {g, g, g}, {ext.x - g, ext.y - g, ext.z - g}),
56 KOKKOS_LAMBDA(int lx, int ly, int lz) {
57 const long idx = (long)lx + (long)ly * ext.x + (long)lz * (long)ext.x * ext.y;
58 const float sc = (float)ccSampleExt(sdf, ext, lx + off.x, ly + off.y, lz + off.z);
59 const int d[6][3] = {{1, 0, 0}, {-1, 0, 0}, {0, 1, 0}, {0, -1, 0}, {0, 0, 1}, {0, 0, -1}};
60 float sn[6];
61 for (int k = 0; k < 6; ++k)
62 sn[k] = (float)ccSampleExt(sdf, ext, lx + d[k][0] + off.x, ly + d[k][1] + off.y,
63 lz + d[k][2] + off.z);
64 if (!ibmIsCut(sc, sn))
65 return;
66 const int slot = Kokkos::atomic_fetch_add(&counter(), 1);
67 idMap(idx) = slot;
68 // Optional analytic-SDF exact crossings (setExactCrossings): tx/ty/tz are INNER-grid
69 // arrays, t_a(i) = exact crossing fraction from this component's staggered point i toward
70 // i + e_a (NaN = no crossing). +a dir: theta = t_a(i); -a dir: theta = 1 - t_a(i - e_a),
71 // periodic wrap on the inner grid.
72 float thEx[6];
73 if (hasEx) {
74 const int ix = lx - g, iy = ly - g, iz = lz - g;
75 auto wrap = [](int v, int n) { v %= n; return v < 0 ? v + n : v; };
76 const CCConst* ta[3] = {&tx, &ty, &tz};
77 for (int a = 0; a < 3; ++a) {
78 const long ip = (long)ix + (long)iy * nn.x + (long)iz * (long)nn.x * nn.y;
79 const int mx = a == 0 ? wrap(ix - 1, nn.x) : ix;
80 const int my = a == 1 ? wrap(iy - 1, nn.y) : iy;
81 const int mz = a == 2 ? wrap(iz - 1, nn.z) : iz;
82 const long im = (long)mx + (long)my * nn.x + (long)mz * (long)nn.x * nn.y;
83 thEx[2 * a] = (float)(*ta[a])(ip);
84 thEx[2 * a + 1] = 1.0f - (float)(*ta[a])(im); // NaN propagates -> fallback
85 }
86 }
88 });
89
90 int cnt = 0;
91 Kokkos::deep_copy(cnt, counter);
92 return cnt;
93}
94
95// Volume fraction theta = clamp(0.5 + sdf_sample, 0, 1) at the staggered point (lx+off, ...).
96inline void ibmVolfrac(CCField theta, CCConst sdf, C3 ext, Off3 off) {
98 using MD = Kokkos::MDRangePolicy<CCExec, Kokkos::Rank<3>>;
99 Kokkos::parallel_for(
100 "peclet::flow::ibm_volfrac", MD(space, {0, 0, 0}, {ext.x, ext.y, ext.z}),
101 KOKKOS_LAMBDA(int lx, int ly, int lz) {
102 const long i = (long)lx + (long)ly * ext.x + (long)lz * (long)ext.x * ext.y;
103 const double sd = ccSampleExt(sdf, ext, lx + off.x, ly + off.y, lz + off.z);
104 const double t = 0.5 + sd;
105 theta(i) = t < 0.0 ? 0.0 : (t > 1.0 ? 1.0 : t);
106 });
107}
108
109// Solid mask: 1 where the staggered SDF point is inside the solid (sd<0), else 0.
110inline void ibmSolidMask(CCField mask, CCConst sdf, C3 ext, Off3 off) {
112 using MD = Kokkos::MDRangePolicy<CCExec, Kokkos::Rank<3>>;
113 Kokkos::parallel_for(
114 "peclet::flow::ibm_solid", MD(space, {0, 0, 0}, {ext.x, ext.y, ext.z}),
115 KOKKOS_LAMBDA(int lx, int ly, int lz) {
116 const long i = (long)lx + (long)ly * ext.x + (long)lz * (long)ext.x * ext.y;
117 const double sd = ccSampleExt(sdf, ext, lx + off.x, ly + off.y, lz + off.z);
118 mask(i) = (sd < 0.0) ? 1.0 : 0.0;
119 });
120}
121
122// Clean-fluid-interior mask: 1 only at fluid cells with no solid neighbour (not cut, not solid).
123inline void ibmCleanFluidMask(CCField m, CCConst sdf, C3 ext, Off3 off) {
125 using MD = Kokkos::MDRangePolicy<CCExec, Kokkos::Rank<3>>;
126 Kokkos::parallel_for(
127 "peclet::flow::ibm_clean", MD(space, {0, 0, 0}, {ext.x, ext.y, ext.z}),
128 KOKKOS_LAMBDA(int lx, int ly, int lz) {
129 const long i = (long)lx + (long)ly * ext.x + (long)lz * (long)ext.x * ext.y;
130 const float sc = (float)ccSampleExt(sdf, ext, lx + off.x, ly + off.y, lz + off.z);
131 const int d[6][3] = {{1, 0, 0}, {-1, 0, 0}, {0, 1, 0}, {0, -1, 0}, {0, 0, 1}, {0, 0, -1}};
132 float sn[6];
133 for (int k = 0; k < 6; ++k)
134 sn[k] = (float)ccSampleExt(sdf, ext, lx + d[k][0] + off.x, ly + d[k][1] + off.y,
135 lz + d[k][2] + off.z);
136 const bool solid = (sc <= 0.0f);
137 m(i) = (solid || ibmIsCut(sc, sn)) ? 0.0 : 1.0;
138 });
139}
140
141// One Red-Black sweep of the variable-coefficient stencil: x[i] = (b[i] - sum(A_off*x_nbr)) /
142// A_C[i]. float matrix coeffs promote to double; solid cells pinned to 0. Call colour 0 then 1.
143// Host backends take a line-sweep form: one task per (y,z) pencil, contiguous stride-2 x-loop
144// starting at the colour's parity — no per-cell parity branch, unit-stride reads. Bit-identical to
145// the MDRange form (same-colour cells are independent, so update order within a colour cannot
146// change the result); the device keeps the MDRange form untouched.
148 MConst AN, MConst AB, MConst AT, CCConst solidmask, C3 ext, C3 og,
149 int g, int color) {
151 const bool hasMask = (solidmask.extent(0) != 0);
152 if constexpr (std::is_same_v<typename CCExec::memory_space, Kokkos::HostSpace>) {
153 const int nyi = ext.y - 2 * g, nzi = ext.z - 2 * g;
154 Kokkos::parallel_for(
155 "peclet::flow::ibm_rbgs", Kokkos::RangePolicy<CCExec>(space, 0, (long)nyi * nzi),
156 KOKKOS_LAMBDA(long t) {
157 const int ly = g + (int)(t % nyi), lz = g + (int)(t / nyi);
158 const long sx = 1, sy = ext.x, sz = (long)ext.x * ext.y;
159 const int P = (color + og.x + og.y + ly + og.z + lz) & 1;
160 for (int lx = g + ((P ^ (g & 1)) & 1); lx < ext.x - g; lx += 2) {
161 const long i = (long)lx + (long)ly * sy + (long)lz * sz;
162 if (hasMask && solidmask(i) > 0.5) {
163 x(i) = 0.0;
164 continue;
165 }
166 const double ac = AC(i);
167 if (Kokkos::fabs(ac) < 1e-30)
168 continue;
169 const double s = (double)AE(i) * x(i + sx) + (double)AW(i) * x(i - sx) +
170 (double)AN(i) * x(i + sy) + (double)AS(i) * x(i - sy) +
171 (double)AT(i) * x(i + sz) + (double)AB(i) * x(i - sz);
172 x(i) = (b(i) - s) / ac;
173 }
174 });
175 return;
176 }
177 using MD = Kokkos::MDRangePolicy<CCExec, Kokkos::Rank<3>>;
178 Kokkos::parallel_for(
179 "peclet::flow::ibm_rbgs", MD(space, {g, g, g}, {ext.x - g, ext.y - g, ext.z - g}),
180 KOKKOS_LAMBDA(int lx, int ly, int lz) {
181 if (((og.x + lx + og.y + ly + og.z + lz) & 1) != color)
182 return;
183 const long sx = 1, sy = ext.x, sz = (long)ext.x * ext.y;
184 const long i = (long)lx + (long)ly * ext.x + (long)lz * sz;
185 if (hasMask && solidmask(i) > 0.5) {
186 x(i) = 0.0;
187 return;
188 }
189 const double ac = AC(i);
190 if (Kokkos::fabs(ac) < 1e-30)
191 return;
192 const double s = (double)AE(i) * x(i + sx) + (double)AW(i) * x(i - sx) +
193 (double)AN(i) * x(i + sy) + (double)AS(i) * x(i - sy) +
194 (double)AT(i) * x(i + sz) + (double)AB(i) * x(i - sz);
195 x(i) = (b(i) - s) / ac;
196 });
197}
198
199// ibmRbgsStencilColor + a fused max|Δx| reduction over the swept colour (values already in
200// registers — no extra memory pass). The momentum tolerance stop runs this as the SECOND colour of
201// a sweep: one colour's max increment is the convergence proxy. Same math as the plain sweep.
203 MConst AS, MConst AN, MConst AB, MConst AT, CCConst solidmask,
204 C3 ext, C3 og, int g, int color) {
206 const bool hasMask = (solidmask.extent(0) != 0);
207 double du = 0.0;
208 if constexpr (std::is_same_v<typename CCExec::memory_space, Kokkos::HostSpace>) {
209 const int nyi = ext.y - 2 * g, nzi = ext.z - 2 * g;
210 Kokkos::parallel_reduce(
211 "peclet::flow::ibm_rbgs_du", Kokkos::RangePolicy<CCExec>(space, 0, (long)nyi * nzi),
212 KOKKOS_LAMBDA(long t, double& m) {
213 const int ly = g + (int)(t % nyi), lz = g + (int)(t / nyi);
214 const long sx = 1, sy = ext.x, sz = (long)ext.x * ext.y;
215 const int P = (color + og.x + og.y + ly + og.z + lz) & 1;
216 for (int lx = g + ((P ^ (g & 1)) & 1); lx < ext.x - g; lx += 2) {
217 const long i = (long)lx + (long)ly * sy + (long)lz * sz;
218 if (hasMask && solidmask(i) > 0.5) {
219 x(i) = 0.0;
220 continue;
221 }
222 const double ac = AC(i);
223 if (Kokkos::fabs(ac) < 1e-30)
224 continue;
225 const double s = (double)AE(i) * x(i + sx) + (double)AW(i) * x(i - sx) +
226 (double)AN(i) * x(i + sy) + (double)AS(i) * x(i - sy) +
227 (double)AT(i) * x(i + sz) + (double)AB(i) * x(i - sz);
228 const double xn = (b(i) - s) / ac;
229 const double d = Kokkos::fabs(xn - x(i));
230 if (d > m)
231 m = d;
232 x(i) = xn;
233 }
234 },
235 Kokkos::Max<double>(du));
236 return du;
237 }
238 using MD = Kokkos::MDRangePolicy<CCExec, Kokkos::Rank<3>>;
239 Kokkos::parallel_reduce(
240 "peclet::flow::ibm_rbgs_du", MD(space, {g, g, g}, {ext.x - g, ext.y - g, ext.z - g}),
241 KOKKOS_LAMBDA(int lx, int ly, int lz, double& m) {
242 if (((og.x + lx + og.y + ly + og.z + lz) & 1) != color)
243 return;
244 const long sx = 1, sy = ext.x, sz = (long)ext.x * ext.y;
245 const long i = (long)lx + (long)ly * ext.x + (long)lz * sz;
246 if (hasMask && solidmask(i) > 0.5) {
247 x(i) = 0.0;
248 return;
249 }
250 const double ac = AC(i);
251 if (Kokkos::fabs(ac) < 1e-30)
252 return;
253 const double s = (double)AE(i) * x(i + sx) + (double)AW(i) * x(i - sx) +
254 (double)AN(i) * x(i + sy) + (double)AS(i) * x(i - sy) +
255 (double)AT(i) * x(i + sz) + (double)AB(i) * x(i - sz);
256 const double xn = (b(i) - s) / ac;
257 const double d = Kokkos::fabs(xn - x(i));
258 if (d > m)
259 m = d;
260 x(i) = xn;
261 },
262 Kokkos::Max<double>(du));
263 return du;
264}
265
266// Box-restricted variant for the distributed overlap smoother: sweeps [rlo,rhi) only, skipping
267// [slo,shi) (pass slo==shi to skip nothing). Interior-then-shell split of one colour's sweep is
268// bit-identical to the full sweep — a colour's cells never read same-colour cells (see
269// cutcellSmoothColorBox in mac_pressure.hpp).
271 MConst AS, MConst AN, MConst AB, MConst AT, CCConst solidmask,
272 C3 ext, C3 og, int color, C3 rlo, C3 rhi, C3 slo, C3 shi) {
273 if (rhi.x <= rlo.x || rhi.y <= rlo.y || rhi.z <= rlo.z)
274 return;
276 const bool hasMask = (solidmask.extent(0) != 0);
277 using MD = Kokkos::MDRangePolicy<CCExec, Kokkos::Rank<3>>;
278 Kokkos::parallel_for(
279 "peclet::flow::ibm_rbgs_box", MD(space, {rlo.x, rlo.y, rlo.z}, {rhi.x, rhi.y, rhi.z}),
280 KOKKOS_LAMBDA(int lx, int ly, int lz) {
281 if (lx >= slo.x && lx < shi.x && ly >= slo.y && ly < shi.y && lz >= slo.z && lz < shi.z)
282 return;
283 if (((og.x + lx + og.y + ly + og.z + lz) & 1) != color)
284 return;
285 const long sx = 1, sy = ext.x, sz = (long)ext.x * ext.y;
286 const long i = (long)lx + (long)ly * ext.x + (long)lz * sz;
287 if (hasMask && solidmask(i) > 0.5) {
288 x(i) = 0.0;
289 return;
290 }
291 const double ac = AC(i);
292 if (Kokkos::fabs(ac) < 1e-30)
293 return;
294 const double s = (double)AE(i) * x(i + sx) + (double)AW(i) * x(i - sx) +
295 (double)AN(i) * x(i + sy) + (double)AS(i) * x(i - sy) +
296 (double)AT(i) * x(i + sz) + (double)AB(i) * x(i - sz);
297 x(i) = (b(i) - s) / ac;
298 });
299}
300
301// ibmRbgsStencilColorBox + the fused max|Δx| reduction (the box form of ibmRbgsStencilColorDu),
302// for the distributed overlap of the momentum tolerance stop: interior-then-shell partial maxima
303// combine by max, which is order-independent — max(interior, shell) is bit-identical to the
304// one-pass reduction.
306 MConst AS, MConst AN, MConst AB, MConst AT,
307 CCConst solidmask, C3 ext, C3 og, int color, C3 rlo, C3 rhi,
308 C3 slo, C3 shi) {
309 if (rhi.x <= rlo.x || rhi.y <= rlo.y || rhi.z <= rlo.z)
310 return 0.0;
312 const bool hasMask = (solidmask.extent(0) != 0);
313 double du = 0.0;
314 using MD = Kokkos::MDRangePolicy<CCExec, Kokkos::Rank<3>>;
315 Kokkos::parallel_reduce(
316 "peclet::flow::ibm_rbgs_du_box", MD(space, {rlo.x, rlo.y, rlo.z}, {rhi.x, rhi.y, rhi.z}),
317 KOKKOS_LAMBDA(int lx, int ly, int lz, double& m) {
318 if (lx >= slo.x && lx < shi.x && ly >= slo.y && ly < shi.y && lz >= slo.z && lz < shi.z)
319 return;
320 if (((og.x + lx + og.y + ly + og.z + lz) & 1) != color)
321 return;
322 const long sx = 1, sy = ext.x, sz = (long)ext.x * ext.y;
323 const long i = (long)lx + (long)ly * ext.x + (long)lz * sz;
324 if (hasMask && solidmask(i) > 0.5) {
325 x(i) = 0.0;
326 return;
327 }
328 const double ac = AC(i);
329 if (Kokkos::fabs(ac) < 1e-30)
330 return;
331 const double s = (double)AE(i) * x(i + sx) + (double)AW(i) * x(i - sx) +
332 (double)AN(i) * x(i + sy) + (double)AS(i) * x(i - sy) +
333 (double)AT(i) * x(i + sz) + (double)AB(i) * x(i - sz);
334 const double xn = (b(i) - s) / ac;
335 const double d = Kokkos::fabs(xn - x(i));
336 if (d > m)
337 m = d;
338 x(i) = xn;
339 },
340 Kokkos::Max<double>(du));
341 return du;
342}
343
344inline void ibmRbgsSweep(CCField x, CCConst b, MConst AC, MConst AW, MConst AE, MConst AS,
345 MConst AN, MConst AB, MConst AT, CCConst solidmask, C3 ext, C3 og, int g) {
346 ibmRbgsStencilColor(x, b, AC, AW, AE, AS, AN, AB, AT, solidmask, ext, og, g, 0);
347 ibmRbgsStencilColor(x, b, AC, AW, AE, AS, AN, AB, AT, solidmask, ext, og, g, 1);
348}
349
350} // namespace peclet::flow
351
352#endif // PECLET_FLOW_MAC_IBM_HPP
flow — portable (Kokkos) Robust-Scaled cut-cell IBM primitives + per-cut-cell overlay build.
flow — portable (Kokkos) cut-cell pressure-operator face openness from an SDF.
void ibmSolidMask(CCField mask, CCConst sdf, C3 ext, Off3 off)
Definition mac_ibm.hpp:110
void ibmCleanFluidMask(CCField m, CCConst sdf, C3 ext, Off3 off)
Definition mac_ibm.hpp:123
double ibmRbgsStencilColorDuBox(CCField x, CCConst b, MConst AC, MConst AW, MConst AE, MConst AS, MConst AN, MConst AB, MConst AT, CCConst solidmask, C3 ext, C3 og, int color, C3 rlo, C3 rhi, C3 slo, C3 shi)
Definition mac_ibm.hpp:305
double ibmRbgsStencilColorDu(CCField x, CCConst b, MConst AC, MConst AW, MConst AE, MConst AS, MConst AN, MConst AB, MConst AT, CCConst solidmask, C3 ext, C3 og, int g, int color)
Definition mac_ibm.hpp:202
void ibmRbgsStencilColor(CCField x, CCConst b, MConst AC, MConst AW, MConst AE, MConst AS, MConst AN, MConst AB, MConst AT, CCConst solidmask, C3 ext, C3 og, int g, int color)
Definition mac_ibm.hpp:147
bool ibmIsCut(float sc, const float sn[6])
Definition mac_ibm.hpp:30
void ibmRbgsSweep(CCField x, CCConst b, MConst AC, MConst AW, MConst AE, MConst AS, MConst AN, MConst AB, MConst AT, CCConst solidmask, C3 ext, C3 og, int g)
Definition mac_ibm.hpp:344
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)
int buildIbmOverlay(CCConst sdf, C3 ext, int g, Off3 off, int bc_type, const IbmOverlay &ov, Kokkos::View< int *, CCMem > idMap, Kokkos::View< int, CCMem > counter, CCConst tx=CCConst(), CCConst ty=CCConst(), CCConst tz=CCConst(), C3 nn=C3{0, 0, 0})
Definition mac_ibm.hpp:45
Kokkos::View< double *, CCMem > CCField
float mreal
Definition mac_ibm.hpp:22
Kokkos::DefaultExecutionSpace CCExec
void ibmRbgsStencilColorBox(CCField x, CCConst b, MConst AC, MConst AW, MConst AE, MConst AS, MConst AN, MConst AB, MConst AT, CCConst solidmask, C3 ext, C3 og, int color, C3 rlo, C3 rhi, C3 slo, C3 shi)
Definition mac_ibm.hpp:270
Kokkos::View< const float *, CCMem > MConst
double ccSampleExt(CCConst sdf, C3 ext, double x, double y, double z)
Kokkos::View< const double *, CCMem > CCConst
void ibmVolfrac(CCField theta, CCConst sdf, C3 ext, Off3 off)
Definition mac_ibm.hpp:96
static constexpr double AC