flow 1.3.0
Kokkos cut-cell IBM incompressible Navier-Stokes solver + pnm pore extraction
Loading...
Searching...
No Matches
block_batch.hpp
Go to the documentation of this file.
1
21#ifndef PECLET_FLOW_VOF_BLOCK_BATCH_HPP
22#define PECLET_FLOW_VOF_BLOCK_BATCH_HPP
23
24#include <Kokkos_Core.hpp>
25
26#include "mac_stencils.hpp" // peclet::flow::SExec, SField, I3, L3
27#include "vof/advect_wy.hpp"
28#include "vof/curvature_field.hpp" // VofRawField
29
30namespace peclet::flow::vof {
31
33struct VofRawMask {
34 const unsigned char* p;
35 KOKKOS_INLINE_FUNCTION unsigned char operator()(long i) const { return p[i]; }
36};
37
41 double *c, *mx, *my, *mz, *al, *fl;
42 double* u[3];
43 unsigned char* cc;
44 const unsigned char* outside;
45 I3 e, n, o;
46 int g;
47 double dth;
48 double weps;
50 bool span[3];
51 unsigned char bcOwn[6];
52};
53
55inline constexpr int kVofBlockBatch = 16;
56
67
69KOKKOS_INLINE_FUNCTION int vofJobOf(const long* off, long t) {
70 int b = 0;
71 while (t >= off[b + 1])
72 ++b;
73 return b;
74}
75
77template <class F>
78inline long vofSetOffsets(VofBlockTable& T, F size) {
79 T.off[0] = 0;
80 for (int k = 0; k < T.nj; ++k)
81 T.off[k + 1] = T.off[k] + size(T.job[k]);
82 for (int k = T.nj; k < kVofBlockBatch; ++k)
83 T.off[k + 1] = T.off[T.nj];
84 return T.off[T.nj];
85}
86
87inline long vofJobLen(const VofBlockJob& J) {
88 return static_cast<long>(J.e.x) * J.e.y * J.e.z;
89}
90inline long vofJobInner(const VofBlockJob& J) {
91 return static_cast<long>(J.n.x) * J.n.y * J.n.z;
92}
94inline long vofJobGrown1(const VofBlockJob& J) {
95 return static_cast<long>(J.n.x + 2) * (J.n.y + 2) * (J.n.z + 2);
96}
97
98// ---- H-4: the host launch forms (`doc/vof_step_performance_design.md` §14.3) --------------------
99//
100// On a host backend GCC compiles a static `RangePolicy` to `schedule(static)`: one contiguous block
101// of the flat index per thread. The device forms above therefore serialise on a host -- a list
102// kernel launched over the upper bound has each block's active entries at the head of its range,
103// on one thread -- and every region kernel pays 64-bit div/mod per cell. The launchers below are
104// the host branches; each batched launcher selects them at compile time on its execution space's
105// memory space (as `ccdetail::ccRows3` does), so a device build never instantiates them and its
106// launches are untouched. Every host form visits the same cells with the same per-cell body, and
107// every body writes only its own cell(s) or adds integers atomically, so the state is bitwise the
108// device form's on the same host.
109
111template <class Exec>
112inline constexpr bool kVofHostExec = std::is_same_v<typename Exec::memory_space, Kokkos::HostSpace>;
113
116 long o[kVofBlockBatch + 1];
117};
118
123template <class Exec, class Cnt, class F>
124inline void vofHostListFor(const char* name, int nj, Cnt cnt, F body) {
125 Exec().fence(); // the counts were written by an earlier launch
127 C.o[0] = 0;
128 for (int k = 0; k < nj; ++k) {
129 const long n = cnt(k);
130 C.o[k + 1] = C.o[k] + (n > 0 ? n : 0);
131 }
132 for (int k = nj; k < kVofBlockBatch; ++k)
133 C.o[k + 1] = C.o[nj];
134 Kokkos::RangePolicy<Exec, Kokkos::Schedule<Kokkos::Dynamic>> pol(Exec(), 0, C.o[nj]);
135 pol.set_chunk_size(16);
136 Kokkos::parallel_for(name, pol, [=](const long t) {
137 const int k = vofJobOf(C.o, t);
138 body(k, t - C.o[k]);
139 });
140}
141
143template <class Tab>
144inline auto vofListCount(const Tab& T, LField start, LField end) {
145 return [=](int k) { return end(T.base + k) - start(T.base + k); };
146}
147
148// The host x loops of the region kernels carry `omp simd` where `ccFor3`'s contract holds (an
149// iteration writes only its own cell and reads no cell another iteration of the row writes); with
150// the host's -ffp-contract=off that is bit-identical to the scalar loop. Same definition as
151// `mac_cutcell.hpp`'s.
152#ifndef PECLET_FLOW_OMP_SIMD
153#if defined(_OPENMP)
154#define PECLET_FLOW_OMP_SIMD _Pragma("omp simd")
155#else
156#define PECLET_FLOW_OMP_SIMD
157#endif
158#endif
159
163};
164
168template <class Exec, class Rows, class F>
169inline void vofHostRowFor(const char* name, int nj, Rows rows, F row) {
170 VofRowTable R;
171 R.off[0] = 0;
172 for (int k = 0; k < nj; ++k)
173 R.off[k + 1] = R.off[k] + rows(k);
174 for (int k = nj; k < kVofBlockBatch; ++k)
175 R.off[k + 1] = R.off[nj];
176 Kokkos::parallel_for(name, Kokkos::RangePolicy<Exec>(Exec(), 0, R.off[nj]), [=](const long t) {
177 const int k = vofJobOf(R.off, t);
178 row(k, t - R.off[k]);
179 });
180}
181
186template <class Exec, class Rows, class RowOf, class Hit>
187inline void vofHostRowScan(const char* name, int nj, int base, Rows rows, RowOf rowOf, Hit hit,
188 LField list, LField start, LField end) {
189 VofRowTable R;
190 R.off[0] = 0;
191 for (int k = 0; k < nj; ++k)
192 R.off[k + 1] = R.off[k] + rows(k);
193 for (int k = nj; k < kVofBlockBatch; ++k)
194 R.off[k + 1] = R.off[nj];
195 Kokkos::parallel_scan(name, Kokkos::RangePolicy<Exec>(Exec(), 0, R.off[nj]),
196 [=](const long t, long& upd, const bool final) {
197 const int k = vofJobOf(R.off, t);
198 const long r = t - R.off[k];
199 int x0, x1;
200 const long i0 = rowOf(k, r, x0, x1);
201 long q = upd;
202 if (final) {
203 if (r == 0)
204 start(base + k) = q;
205 for (int x = x0; x < x1; ++x)
206 if (hit(k, i0 + x))
207 list(q++) = i0 + x;
208 if (t + 1 == R.off[k + 1])
209 end(base + k) = q;
210 } else {
211 for (int x = x0; x < x1; ++x)
212 if (hit(k, i0 + x))
213 ++q;
214 }
215 upd = q;
216 });
217}
218
219// ---- stage 1: the Courant numbers ---------------------------------------------------------------
220
225inline void vofBatchCfl(const VofBlockTable& T, SField out) {
226 using Team = Kokkos::TeamPolicy<SExec>;
227 Kokkos::parallel_for(
228 "vof::block::batch_cfl", Team(SExec(), T.nj, Kokkos::AUTO),
229 KOKKOS_LAMBDA(const typename Team::member_type& tm) {
230 const int k = tm.league_rank();
231 const VofBlockJob& J = T.job[k];
232 const I3 e = J.e, n = J.n;
233 const int g = J.g;
234 const long sx = 1, sy = e.x, sz = static_cast<long>(e.x) * e.y;
235 const long region = static_cast<long>(n.x) * n.y * n.z;
236 const VofRawField c{J.c}, u{J.u[0]}, v{J.u[1]}, w{J.u[2]};
237 const double weps = J.weps;
238 const bool band0 = J.interfaceCfl;
239 double m = 0.0;
240 Kokkos::parallel_reduce(
241 Kokkos::TeamThreadRange(tm, region),
242 [&](const long r, double& acc) {
243 const int x = static_cast<int>(r % n.x) + g;
244 const int y = static_cast<int>((r / n.x) % n.y) + g;
245 const int z = static_cast<int>(r / (static_cast<long>(n.x) * n.y)) + g;
246 const long i = L3(x, y, z, e);
247 if (!band0) { // WyAdvector::maxCourant
248 acc = Kokkos::fmax(acc, Kokkos::fabs(u(i)));
249 acc = Kokkos::fmax(acc, Kokkos::fabs(v(i)));
250 acc = Kokkos::fmax(acc, Kokkos::fabs(w(i)));
251 return;
252 }
253 // WyAdvector::maxCourantInterface
254 const double ci = c(i);
255 const bool band =
256 wyIsMixed(ci, weps) || wyColourJump(c(i - sx), ci, weps) ||
257 wyColourJump(c(i + sx), ci, weps) || wyColourJump(c(i - sy), ci, weps) ||
258 wyColourJump(c(i + sy), ci, weps) || wyColourJump(c(i - sz), ci, weps) ||
259 wyColourJump(c(i + sz), ci, weps);
260 if (!band)
261 return;
262 acc = Kokkos::fmax(acc, Kokkos::fabs(u(i)));
263 acc = Kokkos::fmax(acc, Kokkos::fabs(u(i - sx)));
264 acc = Kokkos::fmax(acc, Kokkos::fabs(v(i)));
265 acc = Kokkos::fmax(acc, Kokkos::fabs(v(i - sy)));
266 acc = Kokkos::fmax(acc, Kokkos::fabs(w(i)));
267 acc = Kokkos::fmax(acc, Kokkos::fabs(w(i - sz)));
268 },
269 Kokkos::Max<double>(m));
270 Kokkos::single(Kokkos::PerTeam(tm), [&]() {
271 // the two return expressions of maxCourant / maxCourantInterface
272 out(T.base + k) = band0 ? Kokkos::fmax(m, 0.0) * J.dth : m * J.dth;
273 });
274 });
275}
276
277// ---- stage 2: the frozen dilation flag ----------------------------------------------------------
278
281template <class Exec = SExec>
282inline void vofBatchFreeze(const VofBlockTable& T) {
283 if constexpr (kVofHostExec<Exec>) {
285 "vof::block::batch_freeze", T.nj,
286 [&](const int k) { return static_cast<long>(T.job[k].e.y) * T.job[k].e.z; },
287 [=](const int k, const long r) {
288 const VofBlockJob& J = T.job[k];
289 const long i0 = r * J.e.x; // extended row r = (r % e.y, r / e.y)
290 const double* c = J.c;
291 unsigned char* cc = J.cc;
292 PECLET_FLOW_OMP_SIMD
293 for (int x = 0; x < J.e.x; ++x)
294 cc[i0 + x] = c[i0 + x] > 0.5 ? 1u : 0u;
295 });
296 return;
297 }
298 Kokkos::parallel_for(
299 "vof::block::batch_freeze", Kokkos::RangePolicy<SExec>(SExec(), 0, T.off[T.nj]),
300 KOKKOS_LAMBDA(const long t) {
301 const int k = vofJobOf(T.off, t);
302 const VofBlockJob& J = T.job[k];
303 const long i = t - T.off[k];
304 J.cc[i] = J.c[i] > 0.5 ? 1u : 0u;
305 });
306}
307
308// ---- stage 3a/b: the worklist and the PLIC reconstruction --------------------------------------
309
314template <class Exec = SExec>
316 if constexpr (kVofHostExec<Exec>) { // H-4(d): the grown-by-one rows
318 "vof::block::batch_worklist", T.nj, T.base,
319 [&](const int k) { return static_cast<long>(T.job[k].n.y + 2) * (T.job[k].n.z + 2); },
320 [=](const int k, const long r, int& x0, int& x1) {
321 const VofBlockJob& J = T.job[k];
322 const int ry = J.n.y + 2, g = J.g;
323 x0 = g - 1;
324 x1 = g + J.n.x + 1;
325 return L3(0, g - 1 + static_cast<int>(r % ry), g - 1 + static_cast<int>(r / ry), J.e);
326 },
327 [=](const int k, const long i) { return wyIsMixed(T.job[k].c[i], T.job[k].weps); }, list,
328 start, end);
329 return;
330 }
331 Kokkos::parallel_scan(
332 "vof::block::batch_worklist", Kokkos::RangePolicy<SExec>(SExec(), 0, T.off[T.nj]),
333 KOKKOS_LAMBDA(const long t, long& upd, const bool final) {
334 const int k = vofJobOf(T.off, t);
335 const VofBlockJob& J = T.job[k];
336 const long r = t - T.off[k];
337 const int rx = J.n.x + 2, ry = J.n.y + 2;
338 const int ix = static_cast<int>(r % rx);
339 const int iy = static_cast<int>((r / rx) % ry);
340 const int iz = static_cast<int>(r / (static_cast<long>(rx) * ry));
341 const int g = J.g;
342 const long i = L3(g - 1 + ix, g - 1 + iy, g - 1 + iz, J.e);
343 const bool mixed = wyIsMixed(J.c[i], J.weps);
344 if (final) {
345 if (r == 0)
346 start(T.base + k) = upd;
347 if (mixed)
348 list(upd) = i;
349 if (t + 1 == T.off[k + 1])
350 end(T.base + k) = upd + (mixed ? 1 : 0);
351 }
352 if (mixed)
353 ++upd;
354 });
355}
356
359template <class Exec = SExec>
360inline void vofBatchPlic(const VofBlockTable& T, LField list, LField start, LField end) {
361 if constexpr (kVofHostExec<Exec>) {
362 vofHostListFor<Exec>("vof::block::batch_plic", T.nj, vofListCount(T, start, end),
363 [=](const int k, const long t) {
364 const long q = start(T.base + k) + t;
365 const VofBlockJob& J = T.job[k];
366 const long sy = J.e.x, sz = static_cast<long>(J.e.x) * J.e.y;
367 wyReconstructCell(VofRawField{J.c}, list(q), sy, sz, VofRawField{J.mx},
368 VofRawField{J.my}, VofRawField{J.mz},
369 VofRawField{J.al});
370 });
371 return;
372 }
373 Kokkos::parallel_for(
374 "vof::block::batch_plic", Kokkos::RangePolicy<SExec>(SExec(), 0, T.off[T.nj]),
375 KOKKOS_LAMBDA(const long t) {
376 const int k = vofJobOf(T.off, t);
377 const long s0 = start(T.base + k);
378 const long q = s0 + (t - T.off[k]);
379 if (q >= end(T.base + k))
380 return;
381 const VofBlockJob& J = T.job[k];
382 const long sy = J.e.x, sz = static_cast<long>(J.e.x) * J.e.y;
383 wyReconstructCell(VofRawField{J.c}, list(q), sy, sz, VofRawField{J.mx}, VofRawField{J.my},
384 VofRawField{J.mz}, VofRawField{J.al});
385 });
386}
387
388// ---- stage 3c/d: fluxes and the sweep update ----------------------------------------------------
389
391inline long vofJobFaces(const VofBlockJob& J, int d) {
392 return static_cast<long>(J.n.x + (d == 0)) * (J.n.y + (d == 1)) * (J.n.z + (d == 2));
393}
394
397 const I3 e = J.e;
398 const long sd = d == 0 ? 1 : (d == 1 ? static_cast<long>(e.x) : static_cast<long>(e.x) * e.y);
399 const VofRawField c{J.c}, mx{J.mx}, my{J.my}, mz{J.mz}, al{J.al}, u{J.u[d]};
400 if (J.outside != nullptr) {
401 J.fl[p] =
402 wyFaceFluxBc(u(p) * J.dth, p, sd, d, c, mx, my, mz, al, VofRawMask{J.outside}, J.weps);
403 return;
404 }
405 J.fl[p] = wyFaceFlux(u(p) * J.dth, p, sd, d, c, mx, my, mz, al, J.weps);
406}
407
411template <class Exec = SExec>
412inline void vofBatchFlux(const VofBlockTable& T, int d) {
413 if constexpr (kVofHostExec<Exec>) {
415 "vof::block::batch_flux", T.nj,
416 [&](const int k) {
417 const VofBlockJob& J = T.job[k];
418 return static_cast<long>(J.n.y + (d == 1)) * (J.n.z + (d == 2));
419 },
420 [=](const int k, const long r) {
421 const VofBlockJob& J = T.job[k];
422 const int ay = J.n.y + (d == 1), g = J.g;
423 const int y = static_cast<int>(r % ay) + g - (d == 1);
424 const int z = static_cast<int>(r / ay) + g - (d == 2);
425 const long i0 = L3(0, y, z, J.e);
426 const int x0 = g - (d == 0), x1 = g + J.n.x;
427 PECLET_FLOW_OMP_SIMD
428 for (int x = x0; x < x1; ++x)
429 vofBatchFluxCell(J, d, i0 + x);
430 });
431 return;
432 }
433 Kokkos::parallel_for(
434 "vof::block::batch_flux", Kokkos::RangePolicy<SExec>(SExec(), 0, T.off[T.nj]),
435 KOKKOS_LAMBDA(const long t) {
436 const int k = vofJobOf(T.off, t);
437 const VofBlockJob& J = T.job[k];
438 const long r = t - T.off[k];
439 const int ax = J.n.x + (d == 0), ay = J.n.y + (d == 1);
440 const int g = J.g;
441 const int x = static_cast<int>(r % ax) + g - (d == 0);
442 const int y = static_cast<int>((r / ax) % ay) + g - (d == 1);
443 const int z = static_cast<int>(r / (static_cast<long>(ax) * ay)) + g - (d == 2);
444 vofBatchFluxCell(J, d, L3(x, y, z, J.e));
445 });
446}
447
453inline void vofBatchBcLedger(const VofBlockTable& T, int d, SField out, int stride, int slot0) {
454 Kokkos::parallel_for(
455 "vof::block::batch_bcvol", Kokkos::RangePolicy<SExec>(SExec(), 0, 2 * T.nj),
456 KOKKOS_LAMBDA(const long t) {
457 const int k = static_cast<int>(t / 2), s = static_cast<int>(t % 2);
458 const VofBlockJob& J = T.job[k];
459 const int f = 2 * d + s;
460 if (J.outside == nullptr || !J.bcOwn[f])
461 return;
462 const I3 e = J.e, n = J.n;
463 const int g = J.g;
464 const int b = (d + 1) % 3, c2 = (d + 2) % 3;
465 const long st[3] = {1, e.x, static_cast<long>(e.x) * e.y};
466 const long sd = st[d], sb = st[b], sc = st[c2];
467 const int nn[3] = {n.x, n.y, n.z};
468 const int fa = (s == 0) ? (g - 1) : (g + nn[d] - 1);
469 double acc = 0.0;
470 for (int p1 = g; p1 < g + nn[c2]; ++p1)
471 for (int p0 = g; p0 < g + nn[b]; ++p0)
472 acc += J.fl[static_cast<long>(p0) * sb + static_cast<long>(p1) * sc +
473 static_cast<long>(fa) * sd];
474 out(static_cast<long>(stride) * (T.base + k) + slot0 + f) += (s == 0) ? acc : -acc;
475 });
476}
477
480 const I3 e = J.e;
481 const long sd = d == 0 ? 1 : (d == 1 ? static_cast<long>(e.x) : static_cast<long>(e.x) * e.y);
482 const double dth = J.dth;
483 const VofRawField c{J.c}, fl{J.fl}, u{J.u[d]};
484 const unsigned char* cc = J.cc;
485 // The dilation term must scale the SAME uf by the SAME dt/h as the flux, or the exact
486 // cancellation in full cells (advect_wy.hpp header) is lost to rounding.
487 const double aP = u(i) * dth, aM = u(i - sd) * dth;
488 const double dil = cc[i] ? (aP - aM) : 0.0;
489 c(i) = c(i) + (fl(i - sd) - fl(i)) + dil;
490}
491
494template <class Exec = SExec>
495inline void vofBatchUpdate(const VofBlockTable& T, int d) {
496 if constexpr (kVofHostExec<Exec>) {
498 "vof::block::batch_update", T.nj,
499 [&](const int k) { return static_cast<long>(T.job[k].n.y) * T.job[k].n.z; },
500 [=](const int k, const long r) {
501 const VofBlockJob& J = T.job[k];
502 const int g = J.g;
503 const long i0 =
504 L3(0, static_cast<int>(r % J.n.y) + g, static_cast<int>(r / J.n.y) + g, J.e);
505 PECLET_FLOW_OMP_SIMD
506 for (int x = g; x < g + J.n.x; ++x)
507 vofBatchUpdateCell(J, d, i0 + x);
508 });
509 return;
510 }
511 Kokkos::parallel_for(
512 "vof::block::batch_update", Kokkos::RangePolicy<SExec>(SExec(), 0, T.off[T.nj]),
513 KOKKOS_LAMBDA(const long t) {
514 const int k = vofJobOf(T.off, t);
515 const VofBlockJob& J = T.job[k];
516 const long r = t - T.off[k];
517 const I3 e = J.e, n = J.n;
518 const int g = J.g;
519 const int x = static_cast<int>(r % n.x) + g;
520 const int y = static_cast<int>((r / n.x) % n.y) + g;
521 const int z = static_cast<int>(r / (static_cast<long>(n.x) * n.y)) + g;
522 vofBatchUpdateCell(J, d, L3(x, y, z, e));
523 });
524}
525
526// ---- stage 3e: the block ghost policy (`VofBlockSet::fillBlockGhosts`), pass by pass ------------
527
531template <class Exec = SExec>
532inline void vofBatchGhostZero(const VofBlockTable& T) {
533 if constexpr (kVofHostExec<Exec>) {
535 "vof::block::batch_ghost_zero", T.nj,
536 [&](const int k) {
537 const VofBlockJob& J = T.job[k];
538 return static_cast<long>(J.e.z - J.n.z) * J.e.y +
539 static_cast<long>(J.n.z) * (J.e.y - J.n.y) + static_cast<long>(J.n.z) * J.n.y;
540 },
541 [=](const int k, const long r) {
542 const VofBlockJob& J = T.job[k];
543 const I3 e = J.e, n = J.n;
544 const int g = J.g;
545 const long rZ = static_cast<long>(e.z - n.z) * e.y,
546 rY = static_cast<long>(n.z) * (e.y - n.y);
547 double* c = J.c;
548 if (r < rZ + rY) { // a whole ghost row
549 int y, z;
550 if (r < rZ) { // z-ghost plane row
551 const int l = static_cast<int>(r / e.y);
552 y = static_cast<int>(r - static_cast<long>(l) * e.y);
553 z = l < g ? l : n.z + l;
554 } else { // y-ghost row of the inner z range
555 const long t2 = r - rZ;
556 const int zi = static_cast<int>(t2 / (e.y - n.y));
557 const int l = static_cast<int>(t2 - static_cast<long>(zi) * (e.y - n.y));
558 y = l < g ? l : n.y + l;
559 z = g + zi;
560 }
561 const long i0 = L3(0, y, z, e);
562 PECLET_FLOW_OMP_SIMD
563 for (int x = 0; x < e.x; ++x)
564 c[i0 + x] = 0.0;
565 return;
566 }
567 const long t3 = r - rZ - rY; // the x-ghost runs of inner row (yi, zi)
568 const int zi = static_cast<int>(t3 / n.y),
569 yi = static_cast<int>(t3 - static_cast<long>(zi) * n.y);
570 const long i0 = L3(0, g + yi, g + zi, e);
571 for (int x = 0; x < g; ++x)
572 c[i0 + x] = 0.0;
573 for (int x = g + n.x; x < e.x; ++x)
574 c[i0 + x] = 0.0;
575 });
576 return;
577 }
578 Kokkos::parallel_for(
579 "vof::block::batch_ghost_zero", Kokkos::RangePolicy<SExec>(SExec(), 0, T.off[T.nj]),
580 KOKKOS_LAMBDA(const long t) {
581 const int k = vofJobOf(T.off, t);
582 const VofBlockJob& J = T.job[k];
583 const long i = t - T.off[k];
584 const I3 e = J.e, n = J.n;
585 const int g = J.g;
586 const int x = static_cast<int>(i % e.x);
587 const int y = static_cast<int>((i / e.x) % e.y);
588 const int z = static_cast<int>(i / (static_cast<long>(e.x) * e.y));
589 if (x >= g && x < g + n.x && y >= g && y < g + n.y && z >= g && z < g + n.z)
590 return;
591 J.c[i] = 0.0;
592 });
593}
594
600template <class Exec = SExec>
601inline void vofBatchPeriodic(const VofBlockTable& T, int a) {
602 if constexpr (kVofHostExec<Exec>) {
604 "vof::block::batch_periodic", T.nj,
605 [&](const int k) {
606 const I3 e = T.job[k].e;
607 const int g = T.job[k].g;
608 return a == 0 ? static_cast<long>(e.y) * e.z
609 : (a == 1 ? 2L * g * e.z : static_cast<long>(e.y) * 2 * g);
610 },
611 [=](const int k, const long r) {
612 const VofBlockJob& J = T.job[k];
613 const I3 e = J.e;
614 const int g = J.g;
615 double* f = J.c;
616 if (a == 0) { // extended row r: the g x-ghosts at each end
617 const int N = e.x - 2 * g;
618 const long base = r * e.x;
619 for (int gl = 0; gl < g; ++gl) {
620 f[base + gl] = f[base + gl + N];
621 f[base + g + N + gl] = f[base + g + gl];
622 }
623 return;
624 }
625 long dst, src;
626 if (a == 1) { // ghost row (l, z): y = l (low) or g + N + (l - g) (high)
627 const int N = e.y - 2 * g;
628 const int z = static_cast<int>(r / (2 * g)), l = static_cast<int>(r % (2 * g));
629 const int y = l < g ? l : N + l, sy = l < g ? l + N : l; // high: y - N = g + (l - g)
630 dst = L3(0, y, z, e);
631 src = L3(0, sy, z, e);
632 } else { // ghost row (y, l)
633 const int N = e.z - 2 * g;
634 const int l = static_cast<int>(r / e.y), y = static_cast<int>(r % e.y);
635 const int z = l < g ? l : N + l, sz = l < g ? l + N : l;
636 dst = L3(0, y, z, e);
637 src = L3(0, y, sz, e);
638 }
640 for (int x = 0; x < e.x; ++x)
641 f[dst + x] = f[src + x];
642 });
643 return;
644 }
645 Kokkos::parallel_for(
646 "vof::block::batch_periodic", Kokkos::RangePolicy<SExec>(SExec(), 0, T.off[T.nj]),
647 KOKKOS_LAMBDA(const long t) {
648 const int k = vofJobOf(T.off, t);
649 const VofBlockJob& J = T.job[k];
650 const long r = t - T.off[k];
651 const I3 e = J.e;
652 const int g = J.g;
653 const long st[3] = {1, e.x, static_cast<long>(e.x) * e.y};
654 const int dims[3] = {e.x, e.y, e.z};
655 const int b = (a + 1) % 3, c = (a + 2) % 3;
656 const long sa = st[a], sb = st[b], sc = st[c];
657 const int N = dims[a] - 2 * g;
658 const int p0 = static_cast<int>(r % dims[b]);
659 const int p1 = static_cast<int>(r / dims[b]);
660 const VofRawField f{J.c};
661 const long base = static_cast<long>(p0) * sb + static_cast<long>(p1) * sc;
662 for (int gl = 0; gl < g; ++gl) {
663 f(base + static_cast<long>(gl) * sa) = f(base + static_cast<long>(gl + N) * sa);
664 f(base + static_cast<long>(g + N + gl) * sa) = f(base + static_cast<long>(g + gl) * sa);
665 }
666 });
667}
668
670KOKKOS_INLINE_FUNCTION void vofBatchClampCell(const VofBlockJob& J, int x, int y, int z, int q0,
671 int q1, int q2, bool p0, bool p1, bool p2) {
672 const I3 e = J.e, o = J.o;
673 const int g = J.g;
674 const int gx = x - g + o.x, gy = y - g + o.y, gz = z - g + o.z;
675 const int cx = p0 ? gx : (gx < 0 ? 0 : (gx >= q0 ? q0 - 1 : gx));
676 const int cy = p1 ? gy : (gy < 0 ? 0 : (gy >= q1 ? q1 - 1 : gy));
677 const int cz = p2 ? gz : (gz < 0 ? 0 : (gz >= q2 ? q2 - 1 : gz));
678 if (cx != gx || cy != gy || cz != gz)
679 J.c[L3(x, y, z, e)] = J.c[L3(cx - o.x + g, cy - o.y + g, cz - o.z + g, e)];
680}
681
686template <class Exec = SExec>
687inline void vofBatchClamp(const VofBlockTable& T, I3 gs, bool px, bool py, bool pz) {
688 const bool p0 = px, p1 = py, p2 = pz;
689 const int q0 = gs.x, q1 = gs.y, q2 = gs.z;
690 if constexpr (kVofHostExec<Exec>) {
692 "vof::block::batch_clamp", T.nj,
693 [&](const int k) { return static_cast<long>(T.job[k].e.y) * T.job[k].e.z; },
694 [=](const int k, const long r) {
695 const VofBlockJob& J = T.job[k];
696 const I3 e = J.e, o = J.o;
697 const int g = J.g;
698 const int y = static_cast<int>(r % e.y), z = static_cast<int>(r / e.y);
699 const int gy = y - g + o.y, gz = z - g + o.z, gx0 = o.x - g, gx1 = o.x - g + e.x;
700 if ((p1 || (gy >= 0 && gy < q1)) && (p2 || (gz >= 0 && gz < q2)) &&
701 (p0 || (gx0 >= 0 && gx1 <= q0)))
702 return; // inside on every non-periodic axis: nothing in this row writes
703 for (int x = 0; x < e.x; ++x)
704 vofBatchClampCell(J, x, y, z, q0, q1, q2, p0, p1, p2);
705 });
706 return;
707 }
708 Kokkos::parallel_for(
709 "vof::block::batch_clamp", Kokkos::RangePolicy<SExec>(SExec(), 0, T.off[T.nj]),
710 KOKKOS_LAMBDA(const long t) {
711 const int k = vofJobOf(T.off, t);
712 const VofBlockJob& J = T.job[k];
713 const long i = t - T.off[k];
714 const I3 e = J.e;
715 const int x = static_cast<int>(i % e.x);
716 const int y = static_cast<int>((i / e.x) % e.y);
717 const int z = static_cast<int>(i / (static_cast<long>(e.x) * e.y));
718 vofBatchClampCell(J, x, y, z, q0, q1, q2, p0, p1, p2);
719 });
720}
721
722// ---- stage 4: marker debris and sub-wispEps residue (`VofBlockSet::debrisPassBatchImpl`) --------
723
727template <class SF>
728KOKKOS_INLINE_FUNCTION bool vofDebrisCell(const SF& c, long i, long sy, long sz, double ieps,
729 double cfull) {
730 if (!vofIsInterface(c(i), ieps))
731 return false;
732 for (int oz = -2; oz <= 2; ++oz)
733 for (int oy = -2; oy <= 2; ++oy)
734 for (int ox = -2; ox <= 2; ++ox)
735 if (c(i + ox + oy * sy + oz * sz) >= cfull)
736 return false; // attached: a cell at least half full of this marker within two
737 return true;
738}
739
741template <class SF>
742KOKKOS_INLINE_FUNCTION bool vofAttachedCell(const SF& c, long i, long sy, long sz, double ieps,
743 double cfull) {
744 if (!vofIsInterface(c(i), ieps))
745 return false;
746 bool attached = false;
747 for (int oz = -2; oz <= 2 && !attached; ++oz)
748 for (int oy = -2; oy <= 2 && !attached; ++oy)
749 for (int ox = -2; ox <= 2 && !attached; ++ox)
750 attached = c(i + ox + oy * sy + oz * sz) >= cfull;
751 return attached;
752}
753
755KOKKOS_INLINE_FUNCTION bool vofResidueCell(double ci, double weps) {
756 return ci != 0.0 && Kokkos::fabs(ci) <= weps;
757}
758
762template <class Exec = SExec>
763inline void vofBatchMark(const VofBlockTable& T, int kind, double ieps, double cfull, double weps,
764 LField list, LField start, LField end) {
765 if constexpr (kVofHostExec<Exec>) { // H-4(d): the inner rows
767 "vof::block::batch_debris_mark", T.nj, T.base,
768 [&](const int k) { return static_cast<long>(T.job[k].n.y) * T.job[k].n.z; },
769 [=](const int k, const long r, int& x0, int& x1) {
770 const VofBlockJob& J = T.job[k];
771 const int g = J.g;
772 x0 = g;
773 x1 = g + J.n.x;
774 return L3(0, g + static_cast<int>(r % J.n.y), g + static_cast<int>(r / J.n.y), J.e);
775 },
776 [=](const int k, const long i) {
777 const VofBlockJob& J = T.job[k];
778 const long sy = J.e.x, sz = static_cast<long>(J.e.x) * J.e.y;
779 const VofRawField c{J.c};
780 return kind == 0 ? vofDebrisCell(c, i, sy, sz, ieps, cfull)
781 : kind == 1 ? vofResidueCell(c(i), weps)
782 : vofAttachedCell(c, i, sy, sz, ieps, cfull);
783 },
784 list, start, end);
785 return;
786 }
787 Kokkos::parallel_scan(
788 "vof::block::batch_debris_mark", Kokkos::RangePolicy<SExec>(SExec(), 0, T.off[T.nj]),
789 KOKKOS_LAMBDA(const long t, long& upd, const bool final) {
790 const int k = vofJobOf(T.off, t);
791 const VofBlockJob& J = T.job[k];
792 const long r = t - T.off[k];
793 const int nx = J.n.x, ny = J.n.y, g = J.g;
794 const int ix = static_cast<int>(r % nx);
795 const int iy = static_cast<int>((r / nx) % ny);
796 const int iz = static_cast<int>(r / (static_cast<long>(nx) * ny));
797 const long i = L3(g + ix, g + iy, g + iz, J.e);
798 const long sy = J.e.x, sz = static_cast<long>(J.e.x) * J.e.y;
799 const VofRawField c{J.c};
800 const bool hit = kind == 0 ? vofDebrisCell(c, i, sy, sz, ieps, cfull)
801 : kind == 1 ? vofResidueCell(c(i), weps)
802 : vofAttachedCell(c, i, sy, sz, ieps, cfull);
803 if (final) {
804 if (r == 0)
805 start(T.base + k) = upd;
806 if (hit)
807 list(upd) = i;
808 if (t + 1 == T.off[k + 1])
809 end(T.base + k) = upd + (hit ? 1 : 0);
810 }
811 if (hit)
812 ++upd;
813 });
814}
815
829
832 bool doDebris, doResidue, remove;
833 double vaMin;
834};
835
838inline void vofBatchDebrisSums(const VofBlockTable& T, VofDebrisFlags F, LField listD, LField listR,
840 LField sA0, LField eA0, SField out, int stride, int slot0) {
841 Kokkos::parallel_for(
842 "vof::block::batch_debris_sums", Kokkos::RangePolicy<SExec>(SExec(), 0, T.nj),
843 KOKKOS_LAMBDA(const long q) {
844 const int j = T.base + static_cast<int>(q);
845 const VofRawField c{T.job[q].c};
846 const long nD = F.doDebris ? eD0(j) - sD0(j) : 0;
847 const long nR = F.doResidue ? eR0(j) - sR0(j) : 0;
848 const bool removeD = F.doDebris && F.remove && nD > 0;
849 const bool pending = removeD || nR > 0;
850 const long ob = static_cast<long>(stride) * j + slot0;
851 double sD = 0.0, sR = 0.0, W = 0.0, VA = 0.0, dV = 0.0;
852 const long d0 = F.doDebris ? sD0(j) : 0;
853 for (long t = 0; t < nD; ++t)
854 sD += c(listD(d0 + t));
855 double acted = 0.0;
856 if (pending) {
857 const long r0 = F.doResidue ? sR0(j) : 0, a0 = sA0(j), nA = eA0(j) - sA0(j);
858 for (long t = 0; t < nR; ++t)
859 sR += c(listR(r0 + t));
860 for (long t = 0; t < nA; ++t) {
861 const double ca = c(listA(a0 + t));
862 W += ca * (1.0 - ca);
863 VA += ca;
864 }
865 acted = (VA < F.vaMin || !(W > 0.0)) ? 0.0 : 1.0;
866 // the removed volume, in the fixed order D (if removing) then R -- the serial order
867 const long nDr = removeD ? nD : 0;
868 for (long t = 0; t < nDr; ++t)
869 dV += c(listD(d0 + t));
870 for (long t = 0; t < nR; ++t)
871 dV += c(listR(r0 + t));
872 }
873 out(ob + kDbSumD) = sD;
874 out(ob + kDbSumR) = sR;
875 out(ob + kDbW) = W;
876 out(ob + kDbVA) = VA;
877 out(ob + kDbDV) = dV;
878 out(ob + kDbActed) = acted;
879 out(ob + kDbND) = static_cast<double>(nD);
880 out(ob + kDbNR) = static_cast<double>(nR);
881 out(ob + kDbPending) = pending ? 1.0 : 0.0;
882 });
883}
884
888 SField out, long ob, int j) {
889 if (out(ob + kDbActed) == 0.0)
890 return 0;
891 const long nD = static_cast<long>(out(ob + kDbND)), nR = static_cast<long>(out(ob + kDbNR));
892 const bool removeD = F.doDebris && F.remove && nD > 0;
893 const long nDr = removeD ? nD : 0, nA = eA0(j) - sA0(j);
894 return nDr + nR + nA;
895}
896
900 int k, long t, LField listD, LField listR,
902 LField eA0, SField out, int stride, int slot0,
903 SField ex, Kokkos::View<int*, SMem> capf) {
904 const int j = T.base + k;
905 const long ob = static_cast<long>(stride) * j + slot0;
906 if (out(ob + kDbActed) == 0.0)
907 return;
908 const long nD = static_cast<long>(out(ob + kDbND)), nR = static_cast<long>(out(ob + kDbNR));
909 const bool removeD = F.doDebris && F.remove && nD > 0;
910 const long nDr = removeD ? nD : 0, nA = eA0(j) - sA0(j);
911 const long nZ = nDr + nR, nTot = nZ + nA;
912 if (t >= nTot)
913 return;
914 const VofRawField c{T.job[k].c};
915 if (t < nDr) {
916 c(listD(sD0(j) + t)) = 0.0;
917 return;
918 }
919 if (t < nZ) {
920 c(listR(sR0(j) + t - nDr)) = 0.0;
921 return;
922 }
923 const double dV = out(ob + kDbDV), W = out(ob + kDbW);
924 const long ta = t - nZ;
925 const long i = listA(sA0(j) + ta);
926 const double ca = c(i);
927 const double d = dV * (ca * (1.0 - ca)) / W;
928 double cn = ca + d;
929 double e = 0.0;
930 if (cn > 1.0) {
931 e = cn - 1.0;
932 cn = 1.0;
933 Kokkos::atomic_max(&capf(j), 1);
934 }
935 ex(T.off[k] + ta) = e;
936 c(i) = cn;
937}
938
943template <class Exec = SExec>
945 LField listR, LField listA, LField sD0, LField sR0, LField sA0,
946 LField eA0, SField out, int stride, int slot0, SField ex,
947 Kokkos::View<int*, SMem> capf) {
948 if constexpr (kVofHostExec<Exec>) {
950 "vof::block::batch_debris_write", T.nj,
951 [&](const int k) {
952 const int j = T.base + k;
953 return vofDebrisActCount(F, sA0, eA0, out, static_cast<long>(stride) * j + slot0, j);
954 },
955 [=](const int k, const long t) {
956 vofDebrisActEntry(T, F, k, t, listD, listR, listA, sD0, sR0, sA0, eA0, out, stride, slot0,
957 ex, capf);
958 });
959 return;
960 }
961 Kokkos::parallel_for(
962 "vof::block::batch_debris_write", Kokkos::RangePolicy<SExec>(SExec(), 0, T.off[T.nj]),
963 KOKKOS_LAMBDA(const long tt) {
964 const int k = vofJobOf(T.off, tt);
965 vofDebrisActEntry(T, F, k, tt - T.off[k], listD, listR, listA, sD0, sR0, sA0, eA0, out,
966 stride, slot0, ex, capf);
967 });
968}
969
973 int stride, int slot0, SField ex, Kokkos::View<int*, SMem> capf) {
974 Kokkos::parallel_for(
975 "vof::block::batch_debris_lost", Kokkos::RangePolicy<SExec>(SExec(), 0, T.nj),
976 KOKKOS_LAMBDA(const long q) {
977 const int j = T.base + static_cast<int>(q);
978 const long ob = static_cast<long>(stride) * j + slot0;
979 if (out(ob + kDbActed) == 0.0)
980 return;
981 double lost = 0.0;
982 if (capf(j) > 0) {
983 const long nA = eA0(j) - sA0(j), e0 = T.off[q];
984 double a = 0.0; // list order, as the serial loop accumulated it
985 for (long t = 0; t < nA; ++t)
986 a += ex(e0 + t);
987 lost = a;
988 }
989 out(ob + kDbDV) = lost;
990 });
991}
992
993// ---- stage 5: the bubble boxes (`VofBlockSet::bubbleBox`) ------------------------------------
994
1003template <class Exec = SExec>
1004inline void vofBatchBox(const VofBlockTable& T, double eps, SField out, int stride, int slot0) {
1005 if constexpr (kVofHostExec<Exec>) {
1006 int box[6 * kVofBlockBatch];
1007 for (int k = 0; k < T.nj; ++k)
1008 for (int a = 0; a < 6; ++a)
1009 box[6 * k + a] =
1010 a < 3 ? Kokkos::reduction_identity<int>::min() : Kokkos::reduction_identity<int>::max();
1011 int* bx = box;
1013 "vof::block::batch_bbox", T.nj,
1014 [&](const int k) { return static_cast<long>(T.job[k].n.y) * T.job[k].n.z; },
1015 [=](const int k, const long r) {
1016 const VofBlockJob& J = T.job[k];
1017 const I3 e = J.e, n = J.n;
1018 const int g = J.g;
1019 const int py = static_cast<int>(r % n.y), pz = static_cast<int>(r / n.y);
1020 const long i0 = L3(g, py + g, pz + g, e);
1021 const VofRawField c{J.c};
1022 int lo = -1, hi = -1;
1023 for (int px = 0; px < n.x; ++px)
1024 if (Kokkos::fabs(c(i0 + px)) > eps) {
1025 if (lo < 0)
1026 lo = px;
1027 hi = px;
1028 }
1029 if (lo < 0)
1030 return; // no cell of the row is in the box
1031 int* s = bx + 6 * k;
1032 Kokkos::atomic_min(&s[0], lo);
1033 Kokkos::atomic_min(&s[1], py);
1034 Kokkos::atomic_min(&s[2], pz);
1035 Kokkos::atomic_max(&s[3], hi);
1036 Kokkos::atomic_max(&s[4], py);
1037 Kokkos::atomic_max(&s[5], pz);
1038 });
1039 Exec().fence();
1040 for (int k = 0; k < T.nj; ++k) {
1041 const long ob = static_cast<long>(stride) * (T.base + k) + slot0;
1042 for (int a = 0; a < 6; ++a)
1043 out(ob + a) = static_cast<double>(box[6 * k + a]);
1044 }
1045 return;
1046 }
1047 using Team = Kokkos::TeamPolicy<SExec>;
1048 Kokkos::parallel_for(
1049 "vof::block::batch_bbox", Team(SExec(), T.nj, Kokkos::AUTO),
1050 KOKKOS_LAMBDA(const typename Team::member_type& tm) {
1051 const int k = tm.league_rank();
1052 const VofBlockJob& J = T.job[k];
1053 const I3 e = J.e, n = J.n;
1054 const int g = J.g;
1055 const long region = static_cast<long>(n.x) * n.y * n.z;
1056 const VofRawField c{J.c};
1057 int v[6];
1058 for (int a = 0; a < 6; ++a) {
1059 int r = 0;
1060 auto body = [&](const long q, int& acc) {
1061 const int px = static_cast<int>(q % n.x);
1062 const int py = static_cast<int>((q / n.x) % n.y);
1063 const int pz = static_cast<int>(q / (static_cast<long>(n.x) * n.y));
1064 if (!(Kokkos::fabs(c(L3(px + g, py + g, pz + g, e))) > eps))
1065 return;
1066 const int p = (a % 3 == 0) ? px : (a % 3 == 1 ? py : pz);
1067 if (a < 3)
1068 acc = p < acc ? p : acc;
1069 else
1070 acc = p > acc ? p : acc;
1071 };
1072 if (a < 3)
1073 Kokkos::parallel_reduce(Kokkos::TeamThreadRange(tm, region), body, Kokkos::Min<int>(r));
1074 else
1075 Kokkos::parallel_reduce(Kokkos::TeamThreadRange(tm, region), body, Kokkos::Max<int>(r));
1076 v[a] = r;
1077 }
1078 Kokkos::single(Kokkos::PerTeam(tm), [&]() {
1079 const long ob = static_cast<long>(stride) * (T.base + k) + slot0;
1080 for (int a = 0; a < 6; ++a)
1081 out(ob + a) = static_cast<double>(v[a]);
1082 });
1083 });
1084}
1085
1086// ---- stage 6: the curvature cascade and the CSF face force (`VofBlockSet::computeCsf`) ---------
1087
1093 double *c, *mx, *my, *mz, *al, *kap, *br;
1094 int* slot;
1095 double* f[3];
1096 I3 e, n;
1097 int g;
1098 double ieps, mtol, ptW, peps, dW, cmin, km;
1099 bool forceFb, oneDir, useFit;
1100 VofMetric gm;
1101};
1102
1110
1111template <class F>
1112inline long vofSetOffsets(VofCurvTable& T, F size) {
1113 T.off[0] = 0;
1114 for (int k = 0; k < T.nj; ++k)
1115 T.off[k + 1] = T.off[k] + size(T.job[k]);
1116 for (int k = T.nj; k < kVofBlockBatch; ++k)
1117 T.off[k + 1] = T.off[T.nj];
1118 return T.off[T.nj];
1119}
1120inline long vofCurvInner(const VofCurvJob& J) {
1121 return static_cast<long>(J.n.x) * J.n.y * J.n.z;
1122}
1124inline long vofCurvGrown(const VofCurvJob& J) {
1125 return static_cast<long>(J.n.x + 2 * kPvHalf) * (J.n.y + 2 * kPvHalf) * (J.n.z + 2 * kPvHalf);
1126}
1127
1129KOKKOS_INLINE_FUNCTION long vofCurvCell(const VofCurvJob& J, long r, bool grown) {
1130 const int gr = grown ? kPvHalf : 0;
1131 const int rx = J.n.x + 2 * gr, ry = J.n.y + 2 * gr;
1132 const int ix = static_cast<int>(r % rx);
1133 const int iy = static_cast<int>((r / rx) % ry);
1134 const int iz = static_cast<int>(r / (static_cast<long>(rx) * ry));
1135 return L3(J.g - gr + ix, J.g - gr + iy, J.g - gr + iz, J.e);
1136}
1137
1139inline long vofCurvRows(const VofCurvJob& J, bool grown) {
1140 const int gr = grown ? kPvHalf : 0;
1141 return static_cast<long>(J.n.y + 2 * gr) * (J.n.z + 2 * gr);
1142}
1143
1145inline long vofCurvRow(const VofCurvJob& J, long r, bool grown, int& x0, int& x1) {
1146 const int gr = grown ? kPvHalf : 0;
1147 const int ry = J.n.y + 2 * gr;
1148 x0 = J.g - gr;
1149 x1 = J.g + J.n.x + gr;
1150 return L3(0, J.g - gr + static_cast<int>(r % ry), J.g - gr + static_cast<int>(r / ry), J.e);
1151}
1152
1155template <class Exec = SExec>
1156inline void vofCurvCompact(const VofCurvTable& T, bool grown, LField list, LField start,
1157 LField end) {
1158 if constexpr (kVofHostExec<Exec>) { // H-4(d): the grown or inner rows
1160 "vof::block::batch_curv_compact", T.nj, T.base,
1161 [&](const int k) { return vofCurvRows(T.job[k], grown); },
1162 [=](const int k, const long r, int& x0, int& x1) {
1163 return vofCurvRow(T.job[k], r, grown, x0, x1);
1164 },
1165 [=](const int k, const long i) { return vofIsInterface(T.job[k].c[i], T.job[k].ieps); },
1166 list, start, end);
1167 return;
1168 }
1169 Kokkos::parallel_scan(
1170 "vof::block::batch_curv_compact", Kokkos::RangePolicy<SExec>(SExec(), 0, T.off[T.nj]),
1171 KOKKOS_LAMBDA(const long t, long& upd, const bool final) {
1172 const int k = vofJobOf(T.off, t);
1173 const VofCurvJob& J = T.job[k];
1174 const long r = t - T.off[k];
1175 const long i = vofCurvCell(J, r, grown);
1176 const bool hit = vofIsInterface(J.c[i], J.ieps);
1177 if (final) {
1178 if (r == 0)
1179 start(T.base + k) = upd;
1180 if (hit)
1181 list(upd) = i;
1182 if (t + 1 == T.off[k + 1])
1183 end(T.base + k) = upd + (hit ? 1 : 0);
1184 }
1185 if (hit)
1186 ++upd;
1187 });
1188}
1189
1192template <class Exec = SExec>
1193inline void vofCurvPlanesZero(const VofCurvTable& T) {
1194 if constexpr (kVofHostExec<Exec>) {
1196 "vof::block::batch_curv_planes_zero", T.nj,
1197 [&](const int k) { return vofCurvRows(T.job[k], true); },
1198 [=](const int k, const long r) {
1199 const VofCurvJob& J = T.job[k];
1200 int x0, x1;
1201 const long i0 = vofCurvRow(J, r, true, x0, x1);
1202 PECLET_FLOW_OMP_SIMD
1203 for (int x = x0; x < x1; ++x) {
1204 J.mx[i0 + x] = 0.0;
1205 J.my[i0 + x] = 0.0;
1206 J.mz[i0 + x] = 0.0;
1207 J.al[i0 + x] = 0.0;
1208 J.slot[i0 + x] = -1;
1209 }
1210 });
1211 return;
1212 }
1213 Kokkos::parallel_for(
1214 "vof::block::batch_curv_planes_zero", Kokkos::RangePolicy<SExec>(SExec(), 0, T.off[T.nj]),
1215 KOKKOS_LAMBDA(const long t) {
1216 const int k = vofJobOf(T.off, t);
1217 const VofCurvJob& J = T.job[k];
1218 const long i = vofCurvCell(J, t - T.off[k], true);
1219 J.mx[i] = 0.0;
1220 J.my[i] = 0.0;
1221 J.mz[i] = 0.0;
1222 J.al[i] = 0.0;
1223 J.slot[i] = -1;
1224 });
1225}
1226
1229 const VofCurvJob& J = T.job[k];
1230 const long sy = J.e.x, sz = static_cast<long>(J.e.x) * J.e.y;
1231 const long i = list(q);
1232 wyReconstructCell(VofRawField{J.c}, i, sy, sz, VofRawField{J.mx}, VofRawField{J.my},
1233 VofRawField{J.mz}, VofRawField{J.al});
1234 curvMomentsBuild(J.mx[i], J.my[i], J.mz[i], J.al[i], J.gm, T.cache[q]);
1235 J.slot[i] = static_cast<int>(q);
1236}
1237
1241template <class Exec = SExec>
1243 if constexpr (kVofHostExec<Exec>) {
1245 "vof::block::batch_curv_planes", T.nj, vofListCount(T, start, end),
1246 [=](const int k, const long t) { vofCurvPlaneEntry(T, k, start(T.base + k) + t, list); });
1247 return;
1248 }
1249 Kokkos::parallel_for(
1250 "vof::block::batch_curv_planes", Kokkos::RangePolicy<SExec>(SExec(), 0, T.off[T.nj]),
1251 KOKKOS_LAMBDA(const long t) {
1252 const int k = vofJobOf(T.off, t);
1253 const long q = start(T.base + k) + (t - T.off[k]);
1254 if (q >= end(T.base + k))
1255 return;
1256 vofCurvPlaneEntry(T, k, q, list);
1257 });
1258}
1259
1262template <class Exec = SExec>
1263inline void vofCurvHfReset(const VofCurvTable& T) {
1264 if constexpr (kVofHostExec<Exec>) {
1266 "vof::block::batch_curv_hf_reset", T.nj,
1267 [&](const int k) { return vofCurvRows(T.job[k], false); },
1268 [=](const int k, const long r) {
1269 const VofCurvJob& J = T.job[k];
1270 int x0, x1;
1271 const long i0 = vofCurvRow(J, r, false, x0, x1);
1272 PECLET_FLOW_OMP_SIMD
1273 for (int x = x0; x < x1; ++x) {
1274 J.kap[i0 + x] = 0.0;
1275 J.br[i0 + x] = static_cast<double>(kCurvNone);
1276 }
1277 });
1278 return;
1279 }
1280 Kokkos::parallel_for(
1281 "vof::block::batch_curv_hf_reset", Kokkos::RangePolicy<SExec>(SExec(), 0, T.off[T.nj]),
1282 KOKKOS_LAMBDA(const long t) {
1283 const int k = vofJobOf(T.off, t);
1284 const VofCurvJob& J = T.job[k];
1285 const long i = vofCurvCell(J, t - T.off[k], false);
1286 J.kap[i] = 0.0;
1287 J.br[i] = static_cast<double>(kCurvNone);
1288 });
1289}
1290
1303inline constexpr int kVofTier3MaxTeams = 1 << 16;
1305 using Policy = Kokkos::TeamPolicy<SExec>;
1306 constexpr int side = 2 * kPvHalf + 1, nk = side * side * side;
1307 const int league =
1308 std::max(1, std::min(kVofTier3MaxTeams, static_cast<int>(SExec().concurrency() / kVofWarp)));
1310 pol.set_scratch_size(0, Kokkos::PerTeam(VofPvTeamScratch::shmem(nk)));
1311 Kokkos::parallel_for(
1312 "vof::block::batch_curv_pv_team", pol, KOKKOS_LAMBDA(const typename Policy::member_type& tm) {
1313 const VofPvTeamScratch sc(tm, nk);
1314 long cum[kVofBlockBatch + 1]; // device prefix of the jobs' entry counts
1315 cum[0] = 0;
1316 for (int k = 0; k < T.nj; ++k)
1317 cum[k + 1] = cum[k] + (end(T.base + k) - start(T.base + k));
1318 for (long g = tm.league_rank(); g < cum[T.nj]; g += tm.league_size()) {
1319 int k = 0;
1320 while (g >= cum[k + 1])
1321 ++k;
1322 const VofCurvJob& J = T.job[k];
1323 const long i = list(start(T.base + k) + (g - cum[k]));
1324 const long sy = J.e.x, sz = static_cast<long>(J.e.x) * J.e.y;
1325 curvFallbackTeam(tm, sc, i, VofPvCached{J.slot, T.cache}, VofRawField{J.kap},
1326 VofRawField{J.br}, sy, sz, kPvHalf, J.dW, J.cmin, J.gm);
1327 tm.team_barrier(); // the single lane is done with the scratch before the next entry's
1328 // scan
1329 }
1330 });
1331}
1332
1337 LField list, LField cnt) {
1338 const VofCurvJob& J = T.job[k];
1339 const long i = list(q);
1340 const long sy = J.e.x, sz = static_cast<long>(J.e.x) * J.e.y;
1341 const VofRawField c{J.c}, mx{J.mx}, my{J.my}, mz{J.mz}, al{J.al}, kap{J.kap}, br{J.br};
1342 const bool all = pass == 4;
1343 if (pass == 0 || all) {
1344 curvHeightCell(i, c, mx, my, mz, al, kap, br, 1L, sy, sz, J.mtol, J.ptW, J.ieps, J.forceFb,
1345 J.oneDir, J.useFit, J.gm, J.peps);
1346 if (!all)
1347 return;
1348 }
1349 if (pass == 1 || all) {
1350 curvFallbackCell(i, VofPvCached{J.slot, T.cache}, kap, br, sy, sz, kPvHalf, J.dW, J.cmin, J.gm);
1351 if (!all)
1352 return;
1353 }
1354 long* ct = &cnt(8 * (T.base + k));
1355 if (pass == 2 || all) {
1356 const double km = J.km;
1357 // km <= 0: this cascade's clip is off (VofCurvature::clipPass returns 0).
1358 // `!(|k| <= km)` rather than `|k| > km`: a NaN curvature is clipped (to +km, the sign of
1359 // a NaN is meaningless) and counted, never silently kept.
1360 if (km > 0.0 && csfKappaDefined(br(i)) && !(Kokkos::fabs(kap(i)) <= km)) {
1361 kap(i) = (kap(i) == kap(i)) ? Kokkos::copysign(km, kap(i)) : km;
1362 Kokkos::atomic_add(&ct[7], 1L);
1363 }
1364 if (!all)
1365 return;
1366 }
1367 const int b = static_cast<int>(br(i));
1368 if (b == kCurvNone)
1369 return;
1370 Kokkos::atomic_add(&ct[0], 1L);
1371 if (b == kCurvHf)
1372 Kokkos::atomic_add(&ct[1], 1L);
1373 else if (b == kCurvHfMixed)
1374 Kokkos::atomic_add(&ct[2], 1L);
1375 else if (b == kCurvHfFit)
1376 Kokkos::atomic_add(&ct[3], 1L);
1377 else if (b == kCurvPv)
1378 Kokkos::atomic_add(&ct[4], 1L);
1379 else if (b == kCurvPvReduced)
1380 Kokkos::atomic_add(&ct[5], 1L);
1381 else
1382 Kokkos::atomic_add(&ct[6], 1L);
1383}
1384
1390template <class Exec = SExec>
1391inline void vofCurvListPass(const VofCurvTable& T, int pass, LField list, LField start, LField end,
1392 LField cnt) {
1393 if constexpr (kVofHostExec<Exec>) { // tier 3 included: the host keeps the loop
1394 vofHostListFor<Exec>("vof::block::batch_curv_list", T.nj, vofListCount(T, start, end),
1395 [=](const int k, const long t) {
1396 vofCurvListEntry(T, pass, k, start(T.base + k) + t, list, cnt);
1397 });
1398 return;
1399 }
1400 if constexpr (!Kokkos::SpaceAccessibility<Kokkos::HostSpace, SField::memory_space>::accessible) {
1401 if (pass == 1) { // tier 3 on a device: persistent warp-teams (C3); the host keeps the loop
1402 vofCurvFallbackTeams(T, list, start, end);
1403 return;
1404 }
1405 }
1406 Kokkos::parallel_for(
1407 "vof::block::batch_curv_list", Kokkos::RangePolicy<SExec>(SExec(), 0, T.off[T.nj]),
1408 KOKKOS_LAMBDA(const long t) {
1409 const int k = vofJobOf(T.off, t);
1410 const long q = start(T.base + k) + (t - T.off[k]);
1411 if (q >= end(T.base + k))
1412 return;
1413 vofCurvListEntry(T, pass, k, q, list, cnt);
1414 });
1415}
1416
1423template <class Exec = SExec>
1424inline void vofCurvListPasses(const VofCurvTable& T, bool anyClip, LField list, LField start,
1425 LField end, LField cnt) {
1426 if constexpr (kVofHostExec<Exec>) {
1427 vofCurvListPass<Exec>(T, 4, list, start, end, cnt);
1428 return;
1429 }
1430 vofCurvListPass<Exec>(T, 0, list, start, end, cnt); // tiers 1-2
1431 vofCurvListPass<Exec>(T, 1, list, start, end, cnt); // tier 3
1432 if (anyClip)
1433 vofCurvListPass<Exec>(T, 2, list, start, end, cnt); // the admissibility clip
1434 vofCurvListPass<Exec>(T, 3, list, start, end, cnt); // the census
1435}
1436
1439KOKKOS_INLINE_FUNCTION void vofCsfForceCell(const VofCurvJob& J, long i, double sig, double w0,
1440 double w1, double w2) {
1441 const long sy = J.e.x, sz = static_cast<long>(J.e.x) * J.e.y;
1442 const VofRawField cv{J.c}, kp{J.kap}, kb{J.br};
1443 for (int cc = 0; cc < 3; ++cc) {
1444 const double wc = cc == 0 ? w0 : (cc == 1 ? w1 : w2);
1445 const long strd = (cc == 0) ? 1 : (cc == 1 ? sy : sz);
1446 const double dC = cv(i) - cv(i - strd);
1447 double f = 0.0;
1448 if (dC != 0.0) {
1449 double kf = 0.0;
1450 vof::csfFaceCurvature(kp(i - strd), kb(i - strd), kp(i), kb(i), kf);
1451 f = vof::csfFaceForce(sig, kf, dC, wc);
1452 }
1453 J.f[cc][i] = f;
1454 }
1455}
1456
1460template <class Exec = SExec>
1461inline void vofCsfForceBatch(const VofCurvTable& T, double sig, double w0, double w1, double w2) {
1462 if constexpr (kVofHostExec<Exec>) {
1464 "vof::block::batch_csf_force", T.nj,
1465 [&](const int k) { return vofCurvRows(T.job[k], false); },
1466 [=](const int k, const long r) {
1467 const VofCurvJob& J = T.job[k];
1468 int x0, x1;
1469 const long i0 = vofCurvRow(J, r, false, x0, x1);
1470 PECLET_FLOW_OMP_SIMD
1471 for (int x = x0; x < x1; ++x)
1472 vofCsfForceCell(J, i0 + x, sig, w0, w1, w2);
1473 });
1474 return;
1475 }
1476 Kokkos::parallel_for(
1477 "vof::block::batch_csf_force", Kokkos::RangePolicy<SExec>(SExec(), 0, T.off[T.nj]),
1478 KOKKOS_LAMBDA(const long t) {
1479 const int k = vofJobOf(T.off, t);
1480 vofCsfForceCell(T.job[k], vofCurvCell(T.job[k], t - T.off[k], false), sig, w0, w1, w2);
1481 });
1482}
1483
1484// ---- C2: the block statistics (§5.10) ----------------------------------------------------------
1485
1487template <int N>
1488struct VofSumN {
1489 double v[N];
1491 for (int i = 0; i < N; ++i)
1492 v[i] = 0.0;
1493 }
1495 for (int i = 0; i < N; ++i)
1496 v[i] += o.v[i];
1497 return *this;
1498 }
1499};
1500
1501} // namespace peclet::flow::vof
1502
1503namespace Kokkos {
1504template <int N>
1505struct reduction_identity<peclet::flow::vof::VofSumN<N>> {
1506 KOKKOS_FORCEINLINE_FUNCTION static peclet::flow::vof::VofSumN<N> sum() {
1508 }
1509};
1510} // namespace Kokkos
1511
1512namespace peclet::flow::vof {
1513
1517 double c[kVofBlockBatch][3];
1518 unsigned char valid[kVofBlockBatch];
1519};
1520
1522inline constexpr int kVofStats = 14;
1523
1530inline void vofBatchStats(const VofBlockTable& T, const VofStatsPrev& P, double hh, double aeps,
1531 double dt, SField out, long off) {
1532 using Team = Kokkos::TeamPolicy<SExec>;
1533 Kokkos::parallel_for(
1534 "vof::block::batch_stats", Team(SExec(), T.nj, Kokkos::AUTO),
1535 KOKKOS_LAMBDA(const typename Team::member_type& tm) {
1536 const int k = tm.league_rank();
1537 const VofBlockJob& J = T.job[k];
1538 const I3 e = J.e, n = J.n, o = J.o;
1539 const int g = J.g;
1540 const long sy = e.x, sz = static_cast<long>(e.x) * e.y;
1541 const long region = static_cast<long>(n.x) * n.y * n.z;
1542 const VofRawField c{J.c};
1543 double* q0 = out.data() + off + static_cast<long>(kVofStats) * (T.base + k);
1544 VofSumN<4> m1;
1545 Kokkos::parallel_reduce(
1546 Kokkos::TeamThreadRange(tm, region),
1547 [&](const long r, VofSumN<4>& a) {
1548 const int x = static_cast<int>(r % n.x) + g;
1549 const int y = static_cast<int>((r / n.x) % n.y) + g;
1550 const int z = static_cast<int>(r / (static_cast<long>(n.x) * n.y)) + g;
1551 const double q = c(L3(x, y, z, e));
1552 a.v[0] += q;
1553 a.v[1] += q * (x - g + o.x + 0.5) * hh;
1554 a.v[2] += q * (y - g + o.y + 0.5) * hh;
1555 a.v[3] += q * (z - g + o.z + 0.5) * hh;
1556 },
1557 Kokkos::Sum<VofSumN<4>>(m1));
1558 const double v = m1.v[0];
1559 if (v <= 0.0) { // measure()'s empty-marker branch: everything but the volume is zero
1560 Kokkos::single(Kokkos::PerTeam(tm), [&]() {
1561 q0[0] = v;
1562 for (int d = 1; d < kVofStats; ++d)
1563 q0[d] = 0.0;
1564 });
1565 return;
1566 }
1567 const double cx = m1.v[1] / v, cy = m1.v[2] / v, cz = m1.v[3] / v;
1568 VofSumN<7> m2;
1569 Kokkos::parallel_reduce(
1570 Kokkos::TeamThreadRange(tm, region),
1571 [&](const long r, VofSumN<7>& a) {
1572 const int x = static_cast<int>(r % n.x) + g;
1573 const int y = static_cast<int>((r / n.x) % n.y) + g;
1574 const int z = static_cast<int>(r / (static_cast<long>(n.x) * n.y)) + g;
1575 const long i = L3(x, y, z, e);
1576 const double q = c(i);
1577 const double px = (x - g + o.x + 0.5) * hh - cx, py = (y - g + o.y + 0.5) * hh - cy,
1578 pz = (z - g + o.z + 0.5) * hh - cz;
1579 a.v[0] += q * px * px;
1580 a.v[1] += q * py * py;
1581 a.v[2] += q * pz * pz;
1582 a.v[3] += q * px * py;
1583 a.v[4] += q * px * pz;
1584 a.v[5] += q * py * pz;
1585 if (!(q > aeps) || !(q < 1.0 - aeps)) // interfaceArea: mixed cells only
1586 return;
1587 double st[27];
1588 for (int dz = -1; dz <= 1; ++dz)
1589 for (int dy = -1; dy <= 1; ++dy)
1590 for (int dx = -1; dx <= 1; ++dx)
1591 st[vof::plicSt(dx + 1, dy + 1, dz + 1)] = c(i + dx + dy * sy + dz * sz);
1592 double m[3];
1593 vof::mycNormal(st, m);
1594 const double al = vof::plicAlpha(m[0], m[1], m[2], q);
1595 double pv[8][3], ctr[3], ar = 0.0;
1596 const int nv = vof::plicPolygon(m[0], m[1], m[2], al, pv);
1597 vof::polygonAreaCentroid(pv, nv, ctr, ar);
1598 a.v[6] += ar;
1599 },
1600 Kokkos::Sum<VofSumN<7>>(m2));
1601 Kokkos::single(Kokkos::PerTeam(tm), [&]() {
1602 const double nc[3] = {cx, cy, cz};
1603 q0[0] = v;
1604 for (int d = 0; d < 3; ++d) {
1605 q0[1 + d] = nc[d];
1606 q0[4 + d] = (P.valid[k] && dt > 0.0) ? (nc[d] - P.c[k][d]) / dt : 0.0;
1607 }
1608 q0[7] = m2.v[6];
1609 const double iv = 1.0 / v;
1610 for (int d = 0; d < 6; ++d)
1611 q0[8 + d] = m2.v[d] * iv;
1612 });
1613 });
1614}
1615
1616} // namespace peclet::flow::vof
1617
1618#endif // PECLET_FLOW_VOF_BLOCK_BATCH_HPP
flow — VoF rung V1: Weymouth & Yue (2010) directionally-split, exactly volume-conserving geometric ad...
#define PECLET_FLOW_OMP_SIMD
flow — VoF rung V3 (WO-O): the view-level driver for the curvature cascade.
flow — portable (Kokkos) MAC stencil operators: Red-Black Gauss-Seidel smoothers + divergence.
int vofJobOf(const long *off, long t)
Job of flat index t (off[0] <= t < off[nj]): a walk over at most 16 offsets.
constexpr bool kVofHostExec
The host branches select on this (§14.2).
long vofJobGrown1(const VofBlockJob &J)
The inner region grown by one cell per side: WyAdvector::reconstructImpl's range.
void vofCurvCompact(const VofCurvTable &T, bool grown, LField list, LField start, LField end)
VofCurvature::compact: the interfacial cells of the grown (grown) or inner region,...
bool vofDebrisCell(const SF &c, long i, long sy, long sz, double ieps, double cfull)
The debris predicate of doc/vof_overlap_design.md §11 on cell i: interfacial, and NO cell of the mark...
void vofBatchFlux(const VofBlockTable &T, int d)
WyAdvector::computeFluxesImpl (uncut): one flux per d-face, wyFaceFlux (or, for a job carrying the WO...
Kokkos::View< long *, SMem > LField
Definition advect_wy.hpp:90
void vofCurvListPasses(const VofCurvTable &T, bool anyClip, LField list, LField start, LField end, LField cnt)
The whole cascade list stage of a chunk: tiers 1-2, tier 3, the clip (only if some job clips),...
void vofBatchCfl(const VofBlockTable &T, SField out)
One team per job: WyAdvector::maxCourant or maxCourantInterface (per the job's interfaceCfl),...
bool csfKappaDefined(double branch)
Is the curvature of a cell carrying branch code branch usable?
void vofCsfForceCell(const VofCurvJob &J, long i, double sig, double w0, double w1, double w2)
The three CSF face-force components at the low faces of inner cell i of job J (the per-cell body of v...
long vofSetOffsets(VofBlockTable &T, F size)
Fill T.off from a per-job range size.
constexpr int kVofWarp
The team size of a device tier-3 launch: one warp / wavefront.
bool vofIsInterface(double c, double eps)
wyIsMixed with a wisp threshold: the cell carries an interface only while eps < C < 1 - eps.
void vofBatchClamp(const VofBlockTable &T, I3 gs, bool px, bool py, bool pz)
Pass 3 (colour_field.hpp::clampFill, skip = 0): the part of each extended block outside a NON-periodi...
void vofBatchPeriodic(const VofBlockTable &T, int a)
Pass 2 on axis a (colour_field.hpp::periodicFill, one axis): only the jobs whose block spans the peri...
double wyFaceFluxBc(double a, long p, long sd, int dir, const SF &c, const SF &mx, const SF &my, const SF &mz, const SF &alpha, const MF &outside, double eps=0.0)
WO-R sibling of wyFaceFlux for a face whose DONOR lies outside the global domain.
void vofBatchGhostZero(const VofBlockTable &T)
Pass 1: every ghost cell of every job -> 0.
constexpr int kVofStats
Doubles per job in the statistics packet: volume, centroid[3], velocity[3], area, moment[6].
constexpr int kVofBlockBatch
Jobs per launch; a container with more master blocks runs each stage in chunks.
void vofCurvFallbackTeams(const VofCurvTable &T, LField list, LField start, LField end)
VofDebrisSlot
The per-job slots of the debris packet (out(stride * (base + k) + slot)).
@ kDbND
debris count
@ kDbW
sum_A C(1 - C)
@ kDbActed
1 when the return ran (pending and VA >= kDebrisMinAttached and W > 0)
@ kDbSumR
sum_R C in list order (signed residue)
@ kDbNR
residue count
@ kDbPending
1 when there was something to remove (removeD or nR > 0)
@ kDbSumD
sum_D C in list order (the removed / census debris volume)
@ kDbDV
the volume to return (D if removing, then R); replaced by lost once acted
void vofBatchDebrisSums(const VofBlockTable &T, VofDebrisFlags F, LField listD, LField listR, LField listA, LField sD0, LField eD0, LField sR0, LField eR0, LField sA0, LField eA0, SField out, int stride, int slot0)
Step 2 of the pass: one thread per job, every sum in list order (the per-block kernel's single thread...
void wyReconstructCell(const SF &c, long i, long sy, long sz, SF mx, SF my, SF mz, SF alpha)
PLIC reconstruction of one cell: MYC normal from the 3^3 colour stencil + the analytic plane offset.
long vofJobFaces(const VofBlockJob &J, int d)
A sweep's face range of one job: the inner region with one extra layer on the low side of d.
void vofCurvHfReset(const VofCurvTable &T)
heightPass (worklist mode), part 1: every inner cell to kappa = 0, branch = kCurvNone.
long vofJobInner(const VofBlockJob &J)
void vofCurvPlanesList(const VofCurvTable &T, LField list, LField start, LField end)
Part 2: wyReconstructCell over the grown interfacial list (upper bound: the grown region),...
void vofHostRowScan(const char *name, int nj, int base, Rows rows, RowOf rowOf, Hit hit, LField list, LField start, LField end)
H-4(d): a host compaction scan over ROWS.
void vofBatchMark(const VofBlockTable &T, int kind, double ieps, double cfull, double weps, LField list, LField start, LField end)
One mark scan over the concatenated inner regions (T.off = vofJobInner), in each block's inner-box in...
void vofBatchFluxCell(const VofBlockJob &J, int d, long p)
The flux of d-face p of job J (the per-cell body of vofBatchFlux, both launch forms).
void vofCsfForceBatch(const VofCurvTable &T, double sig, double w0, double w1, double w2)
VofBlockSet::buildCsfForce for every job, the three components per cell: the V4 balanced-force CSF at...
void vofBatchBox(const VofBlockTable &T, double eps, SField out, int stride, int slot0)
One team per job: the tight box of |C| > eps over the inner region, in the block's LOCAL inner frame ...
void vofCurvListEntry(const VofCurvTable &T, int pass, int k, long q, LField list, LField cnt)
Entry q (an absolute list position) of job k of a cascade list pass (see below): the body shared by t...
bool wyColourJump(double a, double b, double eps)
The interface-BAND jump test of maxCourantInterface: two neighbouring cells carry a DIFFERENT colour.
void vofBatchPlic(const VofBlockTable &T, LField list, LField start, LField end)
wyReconstructCell over each job's compacted list: launched over the upper bound (the job's region),...
bool wyIsMixed(double c)
The mixed-cell predicate.
Definition advect_wy.hpp:95
void vofCurvPlanesZero(const VofCurvTable &T)
reconstructPlanes (worklist mode), part 1: zero the four plane fields and clear the slot map (design ...
void vofBatchDebrisLost(const VofBlockTable &T, LField sA0, LField eA0, SField out, int stride, int slot0, SField ex, Kokkos::View< int *, SMem > capf)
lost: the capped excess of an acted job summed in list order on one thread (only where a cap fired,...
void vofBatchBcLedger(const VofBlockTable &T, int d, SField out, int stride, int slot0)
The WO-R boundary ledger (WyAdvector::accumulateBcFaceVolume) of the jobs carrying the mask: one thre...
long vofJobLen(const VofBlockJob &J)
long vofCurvCell(const VofCurvJob &J, long r, bool grown)
Cell r of job J's grown (grown) or inner region, in the region's x-fastest order.
void curvFallbackCell(long i, const Acc &acc, SF kap, SF br, long sy, long sz, int gr, double dW, double cmin, VofMetric g)
Tier 3 for one target, one thread (host): the frame from the target's entry (pvFrameMoments: the phys...
long vofCurvRow(const VofCurvJob &J, long r, bool grown, int &x0, int &x1)
The first cell of row r of job J's grown or inner region, and the region's x range.
auto vofListCount(const Tab &T, LField start, LField end)
The [start, end) count of job k of a list launch.
bool vofAttachedCell(const SF &c, long i, long sy, long sz, double ieps, double cfull)
The ATTACHED predicate (§5.3 step 2): interfacial with a cell at or above cfull within two.
long vofCurvRows(const VofCurvJob &J, bool grown)
Rows of job J's grown (grown) or inner region, for the host row forms (H-4b).
void curvHeightCell(long i, SF c, SF mx, SF my, SF mz, SF al, SF kap, SF br, long s0, long s1, long s2, double mtol, double ptW, double ieps, bool forceFb, bool oneDir, bool useFit, VofMetric g, double peps)
WO-V9 — the per-cell bodies of the two cascade tiers, lifted VERBATIM out of the kernels they used to...
void vofHostRowFor(const char *name, int nj, Rows rows, F row)
H-4(b): a host region kernel over ROWS – rows(k) x-runs for job k, row(k, r) handling job k's local r...
PECLET_VOF_DEVICE_NOINLINE void curvMomentsBuild(double mx, double my, double mz, double alpha, const VofMetric &g, PvMoments &P)
pvMomentsBuild, the one call every planes pass and the dense accessor make (out of line on a device,...
void vofBatchClampCell(const VofBlockJob &J, int x, int y, int z, int q0, int q1, int q2, bool p0, bool p1, bool p2)
The clamp of extended cell (x, y, z) of job J (the per-cell body of vofBatchClamp).
void vofBatchWorklist(const VofBlockTable &T, LField list, LField start, LField end)
WyAdvector::reconstructImpl's worklist scan, over the concatenated grown-by-one regions (T....
long vofCurvInner(const VofCurvJob &J)
double wyFaceFlux(double a, long p, long sd, int dir, const SF &c, const SF &mx, const SF &my, const SF &mz, const SF &alpha, double eps=0.0)
Signed Eulerian donor-cell flux through the dir-face between cell p and cell p + sd,...
PECLET_VOF_DEVICE_NOINLINE void curvFallbackTeam(const Member &tm, const VofPvTeamScratch &sc, long i, const Acc &acc, SF kap, SF br, long sy, long sz, int gr, double dW, double cmin, VofMetric g)
curvFallbackCell for one target on a whole team (design G §5.4; the team shape of C3,...
void vofBatchStats(const VofBlockTable &T, const VofStatsPrev &P, double hh, double aeps, double dt, SField out, long off)
VofBlockSet::measure for every job in one launch, one team per job: the first moments,...
void vofCurvPlaneEntry(const VofCurvTable &T, int k, long q, LField list)
Entry q (an absolute grown-list position) of job k of the planes pass (below).
void vofBatchFreeze(const VofBlockTable &T)
WyAdvector::freezeDilationFlag over every job's extended block.
void vofBatchUpdate(const VofBlockTable &T, int d)
WyAdvector::applySweepImpl (uncut) over the inner regions (T.off = vofJobInner); the expression verba...
bool vofResidueCell(double ci, double weps)
The residue predicate (§13): 0 < |C| <= wispEps.
void vofDebrisActEntry(const VofBlockTable &T, const VofDebrisFlags &F, int k, long t, LField listD, LField listR, LField listA, LField sD0, LField sR0, LField sA0, LField eA0, SField out, int stride, int slot0, SField ex, Kokkos::View< int *, SMem > capf)
Act entry t of job k (the per-block write kernel's expressions verbatim); t past the job's count,...
void vofCurvListPass(const VofCurvTable &T, int pass, LField list, LField start, LField end, LField cnt)
The per-list-cell passes of the cascade over the inner interfacial list (upper bound: the inner regio...
void vofHostListFor(const char *name, int nj, Cnt cnt, F body)
H-4(a): a host list kernel over the EXACT entry counts, cnt(k) (read directly: the count Views are Ho...
long vofDebrisActCount(const VofDebrisFlags &F, LField sA0, LField eA0, SField out, long ob, int j)
The number of act entries of job j (its D removed, then R, then A), 0 when it did not act: the per-bl...
constexpr int kVofTier3MaxTeams
Tier 3 of the batched cascade on a DEVICE (C3, doc/vof_step_performance_design.md §4....
long vofCurvGrown(const VofCurvJob &J)
The inner region grown by kPvHalf: every cell VofCurvature::reconstructPlanes writes.
void vofBatchUpdateCell(const VofBlockJob &J, int d, long i)
The sweep update of inner cell i of job J (the per-cell body of vofBatchUpdate).
void vofBatchDebrisWrite(const VofBlockTable &T, VofDebrisFlags F, LField listD, LField listR, LField listA, LField sD0, LField sR0, LField sA0, LField eA0, SField out, int stride, int slot0, SField ex, Kokkos::View< int *, SMem > capf)
Step 4, the act: removed cells -> 0, attached C += dV C(1-C)/W capped at 1 (the per-block write kerne...
long L3(int x, int y, int z, I3 e)
Kokkos::DefaultExecutionSpace Exec
Kokkos::DefaultExecutionSpace SExec
Kokkos::View< double *, SMem > SField
One master block's share of a batched container launch.
unsigned char * cc
the frozen dilation flag
double dth
dt / h of this block's advector
double weps
the advector's wisp tolerance
double * u[3]
face velocities uf, vf, wf
bool interfaceCfl
interfaceLocalCfl: maxCourantInterface, else maxCourant
bool span[3]
the block spans this globally periodic axis (wrap in-block)
I3 o
extended extent, inner counts, global index of inner (0,0,0)
const unsigned char * outside
the WO-R out-of-domain mask; null when there is none
unsigned char bcOwn[6]
WO-R: the global domain faces this block owns.
The job table of one launch, by value.
long off[kVofBlockBatch+1]
VofBlockJob job[kVofBlockBatch]
The exact offset table of a host list launch: job k's entries are [o[k], o[k + 1]).
One master block's cascade (VofCurvature, worklist mode) for the batched launches: its planes,...
int * slot
the block's slot map (design G §5.2): absolute cListG_ positions
PvMoments * cache
the chunk's moment cache, indexed like the grown list (design G §5.2)
Flags of the pass, uniform over the blocks.
The cache-backed accessor: slot[j] >= 0 iff cell j of the grown region is interfacial,...
A raw-pointer field accessor with the View<double*> call syntax, for kernels that walk SEVERAL blocks...
Read-only raw accessor of an unsigned-char field (the WO-R out-of-domain mask).
const unsigned char * p
unsigned char operator()(long i) const
The row offsets of a host region launch: job k's rows are [off[k], off[k + 1]).
long off[kVofBlockBatch+1]
What measure() reads besides the colour: each job's previous centroid and whether it is valid (the ho...
double c[kVofBlockBatch][3]
unsigned char valid[kVofBlockBatch]
N double sums in one team reduction (Kokkos::Sum<VofSumN<N>>).
VofSumN & operator+=(const VofSumN &o)
static constexpr int N
static constexpr double F