59#ifndef PECLET_FLOW_SCALAR_MG_HPP
60#define PECLET_FLOW_SCALAR_MG_HPP
66#include <Kokkos_Core.hpp>
91 return (
unk(i) > 0.5 &&
unk(i -
st) > 0.5) ?
lam *
ap(i) : 0.0;
115 "peclet::flow::smg_restrict_max",
C3{0, 0, 0},
cinner,
119 for (
int dz = 0; dz < ratio.z; ++dz)
120 for (
int dy = 0; dy < ratio.y; ++dy)
121 for (
int dx = 0; dx < ratio.x; ++dx) {
128 (
long)(
icz + gc) * (
long)
cext.x *
cext.y) = m > 0.5 ? 1.0 : 0.0;
135 "peclet::flow::smg_zero_pinned",
C3{g, g, g},
C3{e.x - g, e.y - g, e.z - g},
137 const long i = (
long)
lx + (
long)
ly * e.x + (
long)
lz * (
long)e.x * e.y;
148 const int ext[3] = {e.x, e.y, e.z};
149 const int b = (a + 1) % 3, c = (a + 2) % 3;
150 const long st[3] = {1, (
long)e.x, (
long)e.x * e.y};
151 const long sa = st[a],
sb = st[b], sc = st[c];
152 const int nb = ext[b], nc = ext[c];
155 const int pb = (int)(t % nb),
pc = (int)(t / nb);
156 for (
int k = lo; k < hi; ++k)
159 if constexpr (std::is_same_v<typename CCExec::memory_space, Kokkos::HostSpace>) {
161 for (
long t = 0; t < (
long)nb * nc; ++t)
166 Kokkos::parallel_for(
"peclet::flow::smg_zero_planes",
167 Kokkos::RangePolicy<CCExec>(
space, 0, (
long)nb * nc), row);
181 const int nx = inner.x, ny = inner.y, nz = inner.z;
182 const long exy = (
long)e.x * e.y;
183 const bool px = per[0], py = per[1], pz = per[2];
185 if constexpr (std::is_same_v<typename CCExec::memory_space, Kokkos::HostSpace>) {
187 const long rZ = 2L * g * e.y,
rY = (
long)nz * 2 * g,
rX = (
long)nz * ny;
188 auto row = [=](
long t) {
192 const int l = (int)(t / e.y), y = (int)(t - (
long)l * e.y);
193 const int z = l < g ? l : nz + l;
195 const int sy = y < g ? y + ny : (y >= g + ny ? y - ny : y);
196 const int sz = z < g ? z + nz : z - nz;
197 zero = !pz || (
yg && !py);
198 dst = (
long)y * e.x + (
long)z *
exy;
199 src = (
long)sy * e.x + (
long)sz *
exy;
200 }
else if (t <
rZ +
rY) {
201 const long t2 = t -
rZ;
202 const int zi = (int)(
t2 / (2 * g)), l = (
int)(
t2 - (
long)
zi * 2 * g);
203 const int y = l < g ? l : ny + l, z = g +
zi;
204 const int sy = y < g ? y + ny : y - ny;
206 dst = (
long)y * e.x + (
long)z *
exy;
207 src = (
long)sy * e.x + (
long)z *
exy;
209 const long t3 = t -
rZ -
rY;
210 const int zi = (int)(
t3 / ny),
yi = (int)(
t3 - (
long)
zi * ny);
212 for (
int x = 0; x < g; ++x)
213 ff(base + x) = px ?
ff(base + x + nx) : 0.0;
214 for (
int x = g + nx; x < e.x; ++x)
215 ff(base + x) = px ?
ff(base + x - nx) : 0.0;
219 for (
int x = 0; x < e.x; ++x)
223 for (
int x = 0; x < g; ++x)
224 ff(dst + x) = px ?
ff(src + x + nx) : 0.0;
225 for (
int x = g; x < g + nx; ++x)
226 ff(dst + x) =
ff(src + x);
227 for (
int x = g + nx; x < e.x; ++x)
228 ff(dst + x) = px ?
ff(src + x - nx) : 0.0;
230 const long cells = 2L * g *
exy + (
long)nz * 2 * g * e.x + (
long)nz * ny * 2 * g;
232 for (
long t = 0; t <
rZ +
rY +
rX; ++t)
236 Kokkos::parallel_for(
"peclet::flow::smg_fill_shell",
237 Kokkos::RangePolicy<CCExec>(
space, 0,
rZ +
rY +
rX), row);
240 const long nZ = 2L * g *
exy,
nY = (
long)nz * 2 * g * e.x,
nX = (
long)nz * ny * 2 * g;
241 Kokkos::parallel_for(
242 "peclet::flow::smg_fill_shell", Kokkos::RangePolicy<CCExec>(
space, 0,
nZ +
nY +
nX),
246 const int l = (int)(t /
exy);
247 const long r = t - (
long)l *
exy;
250 z = l < g ? l : nz + l;
251 }
else if (t <
nZ +
nY) {
252 const long t2 = t -
nZ, rw = 2L * g * e.x;
253 const long zi =
t2 / rw, r =
t2 -
zi * rw;
254 const int l = (int)(r / e.x);
256 y = l < g ? l : ny + l;
259 const long t3 = t -
nZ -
nY, rw = (
long)ny * 2 * g;
260 const long zi =
t3 / rw, r =
t3 -
zi * rw;
261 const int l = (int)(r % (2 * g));
262 x = l < g ? l : nx + l;
263 y = g + (int)(r / (2 * g));
267 const long d = (
long)x + (
long)y * e.x + (
long)z *
exy;
268 if ((
gx && !px) || (
gy && !py) || (
gz && !pz)) {
272 const int sx = x < g ? x + nx : (x >= g + nx ? x - nx : x);
273 const int sy = y < g ? y + ny : (y >= g + ny ? y - ny : y);
274 const int sz = z < g ? z + nz : (z >= g + nz ? z - nz : z);
275 ff(d) =
ff((
long)
sx + (
long)sy * e.x + (
long)sz *
exy);
289 w.xm = ((wrap & 1) &&
lx == g) ? i + (
long)(n.x - 1) : i - 1;
290 w.xp = ((wrap & 1) &&
lx == g + n.x - 1) ? i - (
long)(n.x - 1) : i + 1;
291 w.ym = ((wrap & 2) &&
ly == g) ? i + (
long)(n.y - 1) * sy : i - sy;
292 w.yp = ((wrap & 2) &&
ly == g + n.y - 1) ? i - (
long)(n.y - 1) * sy : i + sy;
293 w.zm = ((wrap & 4) &&
lz == g) ? i + (
long)(n.z - 1) * sz : i - sz;
294 w.zp = ((wrap & 4) &&
lz == g + n.z - 1) ? i - (
long)(n.z - 1) * sz : i + sz;
303 if constexpr (std::is_same_v<typename CCExec::memory_space, Kokkos::HostSpace>) {
305 return Nb{i + 1, i - 1, i + sy, i - sy, i + sz, i - sz};
307 return nbrs(
lx,
ly,
lz, n, g, i, sy, sz, wrap);
315 int g,
int color,
C3 n,
int wrap) {
317 if constexpr (std::is_same_v<typename CCExec::memory_space, Kokkos::HostSpace>) {
318 const int nyi = e.y - 2 * g,
nzi = e.z - 2 * g;
319 const long cells = (
long)
nyi *
nzi * (e.x - 2 * g);
321 const int ly = g + (int)(t %
nyi),
lz = g + (int)(t /
nyi);
322 const long sx = 1, sy = e.x, sz = (
long)e.x * e.y;
323 const int P = (
color + og.x + og.y +
ly + og.z +
lz) & 1;
325 for (
int lx = g + ((P ^ (g & 1)) & 1);
lx < e.x - g;
lx += 2) {
328 cutcellSmoothFaceCell(x, rhs,
AC, AFX, AFY, AFZ, i,
sx, sy, sz, w.xp, w.xm, w.yp, w.ym,
337 Kokkos::parallel_for(
"peclet::flow::smg_smooth",
342 Kokkos::parallel_for(
343 "peclet::flow::smg_smooth",
MD(
space, {g, g, g}, {e.x - g, e.y - g, e.z - g}),
345 if (((og.x +
lx + og.y +
ly + og.z +
lz) & 1) !=
color)
347 const long sx = 1, sy = e.x, sz = (
long)e.x * e.y;
350 cutcellSmoothFaceCell(x, rhs,
AC, AFX, AFY, AFZ, i,
sx, sy, sz, w.xp, w.xm, w.yp, w.ym,
360 "peclet::flow::smg_residual",
C3{g, g, g},
C3{e.x - g, e.y - g, e.z - g},
362 const long sx = 1, sy = e.x, sz = (
long)e.x * e.y;
365 cutcellResidualFaceCell(r, x, b,
AC, AFX, AFY, AFZ, i,
sx, sy, sz, w.xp, w.xm, w.yp, w.ym,
381 return (
unk(i) > 0.5 &&
unk(i -
st) > 0.5) ? Kokkos::fmax(
sgn *
phi(i), 0.0) : 0.0;
387template <
class XV,
class BV>
389 const BV& AN,
const BV& AB,
const BV& AT,
long i,
391 return ((AW(i) * x(w.xm) + AE(i) * x(w.xp)) + (AS(i) * x(w.ym) + AN(i) * x(w.yp))) +
392 (AB(i) * x(w.zm) + AT(i) * x(w.zp));
398template <
class BV,
class UV>
402 if constexpr (std::is_same_v<typename CCExec::memory_space, Kokkos::HostSpace>) {
403 const int nyi = e.y - 2 * g,
nzi = e.z - 2 * g;
404 const long cells = (
long)
nyi *
nzi * (e.x - 2 * g);
406 const int ly = g + (int)(t %
nyi),
lz = g + (int)(t /
nyi);
407 const long sy = e.x, sz = (
long)e.x * e.y;
408 const int P = (
color + og.x + og.y +
ly + og.z +
lz) & 1;
409 for (
int lx = g + ((P ^ (g & 1)) & 1);
lx < e.x - g;
lx += 2) {
414 x(i) = (rhs(i) -
bandOff(x, AW, AE, AS, AN, AB, AT, i, w)) /
AC(i);
422 Kokkos::parallel_for(
"peclet::flow::smg_smooth_band",
427 Kokkos::parallel_for(
428 "peclet::flow::smg_smooth_band",
MD(
space, {g, g, g}, {e.x - g, e.y - g, e.z - g}),
430 if (((og.x +
lx + og.y +
ly + og.z +
lz) & 1) !=
color)
432 const long sy = e.x, sz = (
long)e.x * e.y;
437 x(i) = (rhs(i) -
bandOff(x, AW, AE, AS, AN, AB, AT, i, w)) /
AC(i);
443template <
class XV,
class OV>
445 long i,
long sx,
long sy,
long sz,
const Nb& w) {
446 return AFX(i +
sx) * x(w.xp) + AFX(i) * x(w.xm) + AFY(i + sy) * x(w.yp) + AFY(i) * x(w.ym) +
447 AFZ(i + sz) * x(w.zp) + AFZ(i) * x(w.zm);
458template <
bool Band,
class XV,
class BV>
460 const BV& AFY,
const BV& AFZ,
const BV& AW,
const BV& AE,
461 const BV& AS,
const BV& AN,
const BV& AB,
const BV& AT,
462 const XV& xs,
const XV& rhss,
const BV& ACs,
463 const BV& AFXs,
const BV& AFYs,
const BV& AFZs,
465 long sz,
const Nb& w) {
466 const double wc = W(i);
467 const double af =
AC(i),
as = ACs(i);
469 const double nf =
Band ?
bandOff(x, AW, AE, AS, AN, AB, AT, i, w)
470 :
faceOff(x, AFX, AFY, AFZ, i,
sx, sy, sz, w);
471 const double rf = rhs(i) -
nf;
472 const double rs = rhss(i) -
faceOff(xs, AFXs, AFYs, AFZs, i,
sx, sy, sz, w);
478 x(i) = (rf +
wc * xs(i)) /
af;
479 xs(i) = (
rs +
wc * x(i)) /
as;
483 if constexpr (
Band) {
485 x(i) = (rhs(i) -
bandOff(x, AW, AE, AS, AN, AB, AT, i, w)) /
af;
488 x(i) = (rhs(i) -
faceOff(x, AFX, AFY, AFZ, i,
sx, sy, sz, w)) /
af;
491 xs(i) = (rhss(i) -
faceOff(xs, AFXs, AFYs, AFZs, i,
sx, sy, sz, w)) /
as;
502 if constexpr (std::is_same_v<typename CCExec::memory_space, Kokkos::HostSpace>) {
503 const int nyi = e.y - 2 * g,
nzi = e.z - 2 * g;
504 const long cells = (
long)
nyi *
nzi * (e.x - 2 * g);
506 const int ly = g + (int)(t %
nyi),
lz = g + (int)(t /
nyi);
507 const long sx = 1, sy = e.x, sz = (
long)e.x * e.y;
508 const int P = (
color + og.x + og.y +
ly + og.z +
lz) & 1;
509 for (
int lx = g + ((P ^ (g & 1)) & 1);
lx < e.x - g;
lx += 2) {
512 twoPhaseCell<Band>(x, rhs,
AC, AFX, AFY, AFZ, AW, AE, AS, AN, AB, AT, xs, rhss, ACs, AFXs,
513 AFYs, AFZs, W,
unkF, i,
sx, sy, sz, w);
521 Kokkos::parallel_for(
"peclet::flow::smg_smooth_two",
526 Kokkos::parallel_for(
527 "peclet::flow::smg_smooth_two",
MD(
space, {g, g, g}, {e.x - g, e.y - g, e.z - g}),
529 if (((og.x +
lx + og.y +
ly + og.z +
lz) & 1) !=
color)
531 const long sx = 1, sy = e.x, sz = (
long)e.x * e.y;
534 twoPhaseCell<Band>(x, rhs,
AC, AFX, AFY, AFZ, AW, AE, AS, AN, AB, AT, xs, rhss, ACs, AFXs,
535 AFYs, AFZs, W,
unkF, i,
sx, sy, sz, w);
547 "peclet::flow::smg_residual_two",
C3{g, g, g},
C3{e.x - g, e.y - g, e.z - g},
549 const long sx = 1, sy = e.x, sz = (
long)e.x * e.y;
552 const double ax =
Band ?
AC(i) * x(i) +
bandOff(x, AW, AE, AS, AN, AB, AT, i, w)
553 :
AC(i) * x(i) +
faceOff(x, AFX, AFY, AFZ, i,
sx, sy, sz, w);
554 const double axs = ACs(i) * xs(i) +
faceOff(xs, AFXs, AFYs, AFZs, i,
sx, sy, sz, w);
555 const double wc = W(i);
556 r(i) = (b(i) - ax) +
wc * xs(i);
566 "peclet::flow::smg_residual_band",
C3{g, g, g},
C3{e.x - g, e.y - g, e.z - g},
568 const long sy = e.x, sz = (
long)e.x * e.y;
571 r(i) = b(i) - (
AC(i) * x(i) +
bandOff(x, AW, AE, AS, AN, AB, AT, i, w));
579 static constexpr int G0 = 2;
580 static constexpr int GC = 1;
593 C3 ext{0, 0, 0},
inner{0, 0, 0},
ratio{1, 1, 1},
cfac{1, 1, 1};
602 Kokkos::View<double*, CCMem>
plane[6];
613 CCField xs,
rhss,
ress,
ACs,
AFXs,
AFYs,
AFZs,
unks,
masss,
pxs,
pys,
pzs,
W;
618#ifdef PECLET_FLOW_MPI
619 std::shared_ptr<GridHaloTopology<3>> halo;
620 std::shared_ptr<GridHalo<double>> dev;
626 for (
int a = 0; a < 3; ++a) {
630 aniso_ = !(hp_[0] == 1.0 && hp_[1] == 1.0 && hp_[2] == 1.0);
634 for (
int a = 0; a < 3; ++a)
646 distributed_ =
false;
647 fill0_ = std::move(
fill0);
648 C3 inner{nx, ny, nz}, cf{1, 1, 1};
651 v.g = L == 0 ?
G0 :
GC;
654 v.ext =
C3{inner.x + 2 * v.g, inner.y + 2 * v.g, inner.z + 2 * v.g};
656 v.n = (std::size_t)v.ext.x * v.ext.y * v.ext.z;
657 const bool canA[3] = {
can(inner.x),
can(inner.y),
can(inner.z)};
659 const C3 next{ratio.x == 2 ? inner.x / 2 : inner.x, ratio.y == 2 ? inner.y / 2 : inner.y,
660 ratio.z == 2 ? inner.z / 2 : inner.z};
664 if (next.x == inner.x && next.y == inner.y && next.z == inner.z)
667 cf =
C3{cf.x * ratio.x, cf.y * ratio.y, cf.z * ratio.z};
670#ifdef PECLET_FLOW_MPI
679 fill0_ = std::move(
fill0);
682 std::array<bool, 3> per{
true,
true,
true};
683 const auto& g0 = dec0.globalSize();
684 C3 gs{(int)g0[0], (
int)g0[1], (int)g0[2]}, cf{1, 1, 1};
685 auto evenOn = [](
const peclet::core::decomp::BlockDecomposer<3>&
d,
int ax) {
686 for (std::size_t b = 0;
b <
d.numBlocks(); ++
b) {
687 const auto blk =
d.block(b);
688 if (blk.origin[ax] % 2 != 0 || blk.size[ax] % 2 != 0)
693 peclet::core::decomp::BlockDecomposer<3> dec = dec0;
698 const auto blk = dec.block((std::size_t)
rank);
699 v.inner = C3{(
int)blk.size[0], (
int)blk.size[1], (
int)blk.size[2]};
700 v.ext = C3{
v.inner.x + 2 *
G0,
v.inner.y + 2 *
G0,
v.inner.z + 2 *
G0};
703 v.halo = std::make_shared<GridHaloTopology<3>>();
704 v.halo->buildTopology(dec, rank,
v.g, per, comm);
705 v.dev = std::make_shared<GridHalo<double>>();
706 v.dev->init(*
v.halo);
707 v.dev->setLabel(
"smg L" + std::to_string(L));
708 const auto& idx =
v.halo->indexer();
709 const auto eg = idx.sizeInclGhost(), ino = idx.sizeInner(), oig = idx.originInclGhost();
710 v.ext = {(
int)eg[0], (
int)
eg[1], (
int)eg[2]};
711 v.inner = {(
int)ino[0], (
int)ino[1], (
int)ino[2]};
712 v.og = {(
int)oig[0] +
v.g, (
int)oig[1] +
v.g, (
int)oig[2] +
v.g};
716 v.n = (std::size_t)
v.ext.x *
v.ext.y *
v.ext.z;
717 const bool canA[3] = {
can(
gs.x) && evenOn(dec, 0),
can(
gs.y) && evenOn(dec, 1),
718 can(
gs.z) && evenOn(dec, 2)};
725 if (next.x ==
gs.x && next.y ==
gs.y && next.z ==
gs.z)
734 int levels()
const {
return (
int)lv_.size(); }
741 for (
const auto& v : lv_)
742 r.push_back(v.ratio);
755 Kokkos::View<const double*, CCMem>
facetW;
756 bool dirFace[6] = {
false,
false,
false,
false,
false,
false};
778 Kokkos::View<const double*, CCMem>
facetC;
783 singular_ =
in.singular;
785 nUse_ =
in.fullTable ? (int)lv_.size() : 1;
808 for (
int f = 0; f < 6; ++f)
809 dirFace_[f] =
in.dirFace[f];
813 for (
int f = 0; f < 6; ++f)
816 for (
int L = 1; L < nUse_; ++L)
828 Kokkos::deep_copy(
CCExec(), z, 0.0);
830 for (
int k = 0; k < 2; ++k) {
834 for (
int k = 0; k < 2; ++k) {
852 Kokkos::deep_copy(
CCExec(),
zf, 0.0);
853 Kokkos::deep_copy(
CCExec(),
zs, 0.0);
855 for (
int k = 0; k < 2; ++k) {
859 for (
int k = 0; k < 2; ++k) {
877 AFYs =
l0.AFYs, AFZs =
l0.AFZs, W =
l0.W, AW =
l0.AW, AE =
l0.AE, AS =
l0.AS,
878 AN =
l0.AN, AB =
l0.AB, AT =
l0.AT;
879 const bool band = adv_;
882 "peclet::flow::smg_apply_two",
C3{g, g, g},
C3{e.x - g, e.y - g, e.z - g},
884 const long sx = 1, sy = e.x, sz = (
long)e.x * e.y;
886 const smg::Nb w{i +
sx, i -
sx, i + sy, i - sy, i + sz, i - sz};
887 const double ax = band ?
AC(i) * x(i) +
smg::bandOff(x, AW, AE, AS, AN, AB, AT, i, w)
889 const double axs = ACs(i) * s(i) +
smg::faceOff(s, AFXs, AFYs, AFZs, i,
sx, sy, sz, w);
890 yf(i) = ax - W(i) * s(i);
891 ys(i) =
axs - W(i) * x(i);
902 applyCutcellOp(y,
CCConst(x),
l0.AC,
l0.AW,
l0.AE,
l0.AS,
l0.AN,
l0.AB,
l0.AT, e, g);
908 "peclet::flow::smg_apply",
C3{g, g, g},
C3{e.x - g, e.y - g, e.z - g},
910 const long sx = 1, sy = e.x, sz = (
long)e.x * e.y;
912 y(i) =
cutcellApplyFaceCell(
xx,
AC, AFX, AFY, AFZ, i,
sx, sy, sz, i +
sx, i -
sx, i + sy,
913 i - sy, i + sz, i - sz);
923 const C3 ext =
l0.ext, og =
l0.og;
927 "peclet::flow::smg_seed",
C3{g, g, g},
C3{ext.
x - g, ext.
y - g, ext.
z - g},
930 std::uint64_t h = (std::uint64_t)(og.x +
lx - g) * 0x9E3779B97F4A7C15ull ^
931 (std::uint64_t)(og.y +
ly - g) * 0xC2B2AE3D27D4EB4Full ^
932 (std::uint64_t)(og.z +
lz - g) * 0x165667B19E3779F9ull;
934 h *= 0xBF58476D1CE4E5B9ull;
936 e(i) = unk(i) > 0.5 ? (
double)(h >> 11) * (1.0 / 9007199254740992.0) - 0.5 : 0.0;
940 std::vector<double>
ratios;
941 double prev =
norm2(e);
942 for (
int k = 0; k < iters; ++k) {
948 "peclet::flow::smg_err_update",
C3{g, g, g},
C3{ext.
x - g, ext.
y - g, ext.
z - g},
956 ratios.push_back(prev > 0.0 ?
cur / prev : 0.0);
960 const double s = 1.0 /
cur;
962 "peclet::flow::smg_err_scale",
C3{g, g, g},
C3{ext.
x - g, ext.
y - g, ext.
z - g},
975 if (L + 1 == nUse_) {
976 for (
int k = 0; k <
kBottom / 2; ++k)
978 for (
int k = 0; k <
kBottom / 2; ++k)
984 for (
int k = 0; k <
kPre; ++k)
992 lv.AB, lv.AT, lv.ext, lv.g, lv.inner,
wr < 0 ? 0 :
wr);
995 lv.ext, lv.g, lv.inner,
wr < 0 ? 0 :
wr);
996 Level& cs = lv_[L + 1];
1002 prolongAdd(lv.x,
CCConst(cs.x), lv.ext, cs.ext, lv.g, cs.g, lv.inner, lv.ratio);
1004 for (
int k = 0; k <
kPost; ++k)
1013 if (L + 1 == nUse_) {
1014 for (
int k = 0; k <
kBottom / 2; ++k)
1016 for (
int k = 0; k <
kBottom / 2; ++k)
1022 for (
int k = 0; k <
kPre; ++k)
1029 const int w =
wr < 0 ? 0 :
wr;
1031 smg::residualTwo<true>(lv.res,
CCConst(lv.x),
CCConst(lv.rhs), lv.AC, lv.AFX, lv.AFY, lv.AFZ,
1032 lv.AW, lv.AE, lv.AS, lv.AN, lv.AB, lv.AT, lv.ress,
CCConst(lv.xs),
1033 CCConst(lv.rhss), lv.ACs, lv.AFXs, lv.AFYs, lv.AFZs, lv.W, lv.ext,
1036 smg::residualTwo<false>(lv.res,
CCConst(lv.x),
CCConst(lv.rhs), lv.AC, lv.AFX, lv.AFY, lv.AFZ,
1037 lv.AW, lv.AE, lv.AS, lv.AN, lv.AB, lv.AT, lv.ress,
CCConst(lv.xs),
1038 CCConst(lv.rhss), lv.ACs, lv.AFXs, lv.AFYs, lv.AFZs, lv.W, lv.ext,
1040 Level& cs = lv_[L + 1];
1048 prolongAdd(lv.x,
CCConst(cs.x), lv.ext, cs.ext, lv.g, cs.g, lv.inner, lv.ratio);
1049 prolongAdd(lv.xs,
CCConst(cs.xs), lv.ext, cs.ext, lv.g, cs.g, lv.inner, lv.ratio);
1052 for (
int k = 0; k <
kPost; ++k)
1062 if (!distributed_) {
1067 Kokkos::deep_copy(
CCExec(), cx, 0.0);
1077 const int w =
ws < 0 ? 0 :
ws;
1080 const int kc = (rb + 3 * lv.g) & 1;
1085 smg::sweepColorTwo<true>(lv.x, lv.rhs, lv.AC, lv.AFX, lv.AFY, lv.AFZ, lv.AW, lv.AE, lv.AS,
1086 lv.AN, lv.AB, lv.AT, lv.xs, lv.rhss, lv.ACs, lv.AFXs, lv.AFYs,
1087 lv.AFZs, lv.W,
unkOf(L), lv.ext, lv.og, lv.g, kc, lv.inner, w);
1089 smg::sweepColorTwo<false>(lv.x, lv.rhs, lv.AC, lv.AFX, lv.AFY, lv.AFZ, lv.AW, lv.AE, lv.AS,
1090 lv.AN, lv.AB, lv.AT, lv.xs, lv.rhss, lv.ACs, lv.AFXs, lv.AFYs,
1091 lv.AFZs, lv.W,
unkOf(L), lv.ext, lv.og, lv.g, kc, lv.inner, w);
1096 unkOf(L), lv.ext, lv.og, lv.g, kc, lv.inner, w);
1117#ifdef PECLET_FLOW_MPI
1119 lv.dev->exchange(f);
1139 if constexpr (std::is_same_v<typename CCExec::memory_space, Kokkos::HostSpace>)
1144 return fill0Wraps_ ? 7 : -1;
1145 return (per_[0] ? 1 : 0) | (per_[1] ? 2 : 0) | (per_[2] ? 4 : 0);
1150 const Level& lv = lv_[L];
1151 if (lv.inner.x % 2 != 0 || lv.inner.y % 2 != 0 || lv.inner.z % 2 != 0)
1158 const int a = f / 2;
1159 const int o = (a == 0) ? lv.og.x : (a == 1) ? lv.og.y : lv.og.z;
1160 const int n = (a == 0) ? lv.inner.x : (a == 1) ? lv.inner.y : lv.inner.z;
1161 const int gn = (a == 0) ? lv.gdim.x : (a == 1) ? lv.gdim.y : lv.gdim.z;
1162 return (f % 2 == 0) ? (o == 0) : (o + n == gn);
1169 static bool can(
int d) {
return (d % 2 == 0) && (d / 2 >= 2); }
1171 const double H[3] = {hp_[0] * (
double)cf.x, hp_[1] * (
double)cf.y, hp_[2] * (
double)cf.z};
1178 v.res =
CCField(
"smg_res0", v.n);
1179 v.AFX =
CCField(
"smg_afx0", v.n);
1180 v.AFY =
CCField(
"smg_afy0", v.n);
1181 v.AFZ =
CCField(
"smg_afz0", v.n);
1185 v.rhs =
CCField(
"smg_rhs", v.n);
1186 v.res =
CCField(
"smg_res", v.n);
1187 v.AC =
CCField(
"smg_ac", v.n);
1188 v.AFX =
CCField(
"smg_afx", v.n);
1189 v.AFY =
CCField(
"smg_afy", v.n);
1190 v.AFZ =
CCField(
"smg_afz", v.n);
1191 v.unk =
CCField(
"smg_unk", v.n);
1192 v.mass =
CCField(
"smg_mass", v.n);
1193 v.px =
CCField(
"smg_px", v.n);
1194 v.py =
CCField(
"smg_py", v.n);
1195 v.pz =
CCField(
"smg_pz", v.n);
1204 if (v.ress.extent(0) == v.n)
1206 v.ress =
CCField(
"smg_ress", v.n);
1207 v.AFXs =
CCField(
"smg_afxs", v.n);
1208 v.AFYs =
CCField(
"smg_afys", v.n);
1209 v.AFZs =
CCField(
"smg_afzs", v.n);
1212 v.xs =
CCField(
"smg_xs", v.n);
1213 v.rhss =
CCField(
"smg_rhss", v.n);
1214 v.ACs =
CCField(
"smg_acs", v.n);
1215 v.unks =
CCField(
"smg_unks", v.n);
1216 v.masss =
CCField(
"smg_masss", v.n);
1217 v.pxs =
CCField(
"smg_pxs", v.n);
1218 v.pys =
CCField(
"smg_pys", v.n);
1219 v.pzs =
CCField(
"smg_pzs", v.n);
1224 const Level& lv = lv_[L];
1225 const C3 e = lv.ext;
1230 "peclet::flow::smg_count_s",
C3{g, g, g},
C3{e.x - g, e.y - g, e.z - g},
1232 const long i = (
long)
lx + (
long)
ly * e.x + (
long)
lz * (
long)e.x * e.y;
1233 acc += unk(i) > 0.5 ? 1.0 : 0.0;
1235 Kokkos::Sum<double>(s));
1243 const Level& lv = lv_[L];
1244 const double nTot = lv.nUnk + lv.nUnkS;
1247 const C3 e = lv.ext;
1252 "peclet::flow::smg_mean_two",
C3{g, g, g},
C3{e.x - g, e.y - g, e.z - g},
1254 const long i = (
long)
lx + (
long)
ly * e.x + (
long)
lz * (
long)e.x * e.y;
1255 acc += (
uf(i) > 0.5 ? f(i) : 0.0) + (us(i) > 0.5 ? s(i) : 0.0);
1257 Kokkos::Sum<double>(sum));
1261 "peclet::flow::smg_mean_two_sub",
C3{g, g, g},
C3{e.x - g, e.y - g, e.z - g},
1263 const long i = (
long)
lx + (
long)
ly * e.x + (
long)
lz * (
long)e.x * e.y;
1276 const C3 e =
l0.ext;
1277 const double wx = w_[0],
wy = w_[1],
wz = w_[2];
1281 "peclet::flow::smg_faces0_solid",
C3{0, 0, 0}, e,
KOKKOS_LAMBDA(
int x,
int y,
int z) {
1282 const long i = (
long)x + (
long)y * e.x + (
long)z * (
long)e.x * e.y;
1283 AFX(i) = -(
wx * px(i));
1284 AFY(i) = -(
wy * py(i));
1285 AFZ(i) = -(
wz * pz(i));
1295 const C3 ratio =
fin.ratio;
1302 CCField px = c.pxs, py = c.pys, pz = c.pzs;
1304 const int gc = c.g,
gf =
fin.g;
1309 "peclet::flow::smg_coarsen_faces_solid",
C3{0, 0, 0}, c.inner,
1311 coarsenOpenAvgCell(px, py, pz,
fx,
fy,
fz,
ce,
fe, gc,
gf, ratio,
icx,
icy,
icz);
1316 const CCField P[3] = {c.pxs, c.pys, c.pzs};
1317 for (
int a = 0; a < 3; ++a)
1318 if (!per_[a] &&
touches(c, 2 * a))
1323 const C3 e = c.ext,
ci = c.inner, cf = c.cfac;
1325 const double wx = w_[0] / ((
double)cf.x * cf.x),
wy = w_[1] / ((
double)cf.y * cf.y),
1326 wz = w_[2] / ((
double)cf.z * cf.z);
1327 CCField AC = c.ACs, AFX = c.AFXs, AFY = c.AFYs, AFZ = c.AFZs, W = c.W;
1328 CCConst px = c.pxs, py = c.pys, pz = c.pzs, unk = c.unks, mass = c.masss;
1330 "peclet::flow::smg_coarse_faces_solid",
C3{0, 0, 0}, e,
KOKKOS_LAMBDA(
int x,
int y,
int z) {
1331 const long i = (
long)x + (
long)y * e.x + (
long)z * (
long)e.x * e.y;
1332 AFX(i) = -(
wx * px(i));
1333 AFY(i) = -(
wy * py(i));
1334 AFZ(i) = -(
wz * pz(i));
1337 "peclet::flow::smg_coarse_diag_solid",
C3{0, 0, 0},
ci,
1339 const long sx = 1, sy = e.x, sz = (
long)e.x * e.y;
1341 if (!(unk(i) > 0.5)) {
1343 AFX(i) = AFY(i) = AFZ(i) = 0.0;
1347 const double tw =
wx * px(i),
te =
wx * px(i +
sx);
1348 const double ts =
wy * py(i),
tn =
wy * py(i + sy);
1349 const double tb =
wz * pz(i),
tt =
wz * pz(i + sz);
1350 AC(i) = (mass(i) + (((
tw +
te) + (ts +
tn)) + (tb +
tt))) + W(i);
1355 const int ext[3] = {lv.ext.x, lv.ext.y, lv.ext.z};
1356 for (
int a = 0; a < 3; ++a) {
1368#ifdef PECLET_FLOW_MPI
1370 std::vector<double> o((std::size_t)k);
1372 for (
int j = 0; j < k; ++j)
1373 v[j] = o[(std::size_t)j];
1380 const Level& lv = lv_[L];
1381 const C3 e = lv.ext;
1386 "peclet::flow::smg_count",
C3{g, g, g},
C3{e.x - g, e.y - g, e.z - g},
1388 const long i = (
long)
lx + (
long)
ly * e.x + (
long)
lz * (
long)e.x * e.y;
1389 acc += unk(i) > 0.5 ? 1.0 : 0.0;
1391 Kokkos::Sum<double>(s));
1397 const Level& lv = lv_[L];
1398 if (!(lv.nUnk > 0.0))
1400 const C3 e = lv.ext;
1405 "peclet::flow::smg_mean",
C3{g, g, g},
C3{e.x - g, e.y - g, e.z - g},
1407 const long i = (
long)
lx + (
long)
ly * e.x + (
long)
lz * (
long)e.x * e.y;
1411 Kokkos::Sum<double>(s));
1413 const double mean = s / lv.nUnk;
1415 "peclet::flow::smg_mean_sub",
C3{g, g, g},
C3{e.x - g, e.y - g, e.z - g},
1417 const long i = (
long)
lx + (
long)
ly * e.x + (
long)
lz * (
long)e.x * e.y;
1423 const Level& lv = lv_[0];
1424 const C3 e = lv.ext;
1428 "peclet::flow::smg_norm",
C3{g, g, g},
C3{e.x - g, e.y - g, e.z - g},
1430 const long i = (
long)
lx + (
long)
ly * e.x + (
long)
lz * (
long)e.x * e.y;
1433 Kokkos::Sum<double>(s));
1435 return std::sqrt(s);
1444 const C3 e =
l0.ext;
1445 const double lx =
in.lam * w_[0],
ly =
in.lam * w_[1],
lz =
in.lam * w_[2];
1449 "peclet::flow::smg_faces0",
C3{0, 0, 0}, e,
KOKKOS_LAMBDA(
int x,
int y,
int z) {
1450 const long sx = 1, sy = e.x, sz = (
long)e.x * e.y;
1451 const long i = (
long)x + (
long)y * sy + (
long)z * sz;
1452 const bool u = unk(i) > 0.5;
1453 const double tw = (x > 0 && u && unk(i -
sx) > 0.5) ?
lx * sax(i) : 0.0;
1454 const double ts = (y > 0 && u && unk(i - sy) > 0.5) ?
ly * say(i) : 0.0;
1455 const double tb = (z > 0 && u && unk(i - sz) > 0.5) ?
lz * saz(i) : 0.0;
1471 const auto&
fo = *
in.fac;
1472 if (cutKey_ ==
fo.cutCell.data() && cutKeyN_ ==
fo.nCut && (
int)cutStart_.size() == nUse_)
1475 const C3 e0 =
l0.ext,
n0 =
l0.inner;
1476 const int g0 =
l0.g;
1477 auto hc = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(),
fo.cutCell);
1479 std::vector<std::pair<long, int>> rows;
1480 rows.reserve((std::size_t)
fo.nCut);
1481 for (
long r = 0; r <
fo.nCut; ++r) {
1482 const long i0 =
hc(r);
1483 const int x = (int)(i0 % e0.x) - g0, y = (int)((i0 / e0.x) % e0.y) - g0,
1484 z = (
int)(i0 / ((
long)e0.x * e0.y)) - g0;
1486 rows.emplace_back(i0, (
int)r);
1488 std::sort(rows.begin(), rows.end());
1489 cutStart_.assign((std::size_t)nUse_, Kokkos::View<int*, CCMem>());
1490 cutRows_.assign((std::size_t)nUse_, Kokkos::View<int*, CCMem>());
1491 for (
int L = 1; L < nUse_; ++L) {
1492 const Level& c = lv_[L];
1493 const C3 ci = c.inner, cf = c.cfac;
1495 std::vector<int>
start((std::size_t)nc + 1, 0), list(rows.size());
1497 const int x = (int)(i0 % e0.x) - g0, y = (int)((i0 / e0.x) % e0.y) - g0,
1498 z = (
int)(i0 / ((
long)e0.x * e0.y)) - g0;
1499 return (
long)(x / cf.x) + (
long)(y / cf.y) *
ci.x + (
long)(z / cf.z) *
ci.x *
ci.y;
1501 for (
const auto& q : rows)
1503 for (
long k = 0; k < nc; ++k)
1504 start[(std::size_t)k + 1] +=
start[(std::size_t)k];
1505 std::vector<int> pos(
start.begin(),
start.end() - 1);
1506 for (
const auto& q : rows)
1507 list[(std::size_t)pos[(std::size_t)
coarseOf(q.first)]++] = q.second;
1508 Kokkos::View<int*, CCMem>
ds(
"smg_cut_start", (std::size_t)nc + 1),
1509 dl(
"smg_cut_rows", std::max<std::size_t>(list.size(), 1));
1511 ds, Kokkos::View<
const int*, Kokkos::HostSpace, Kokkos::MemoryTraits<Kokkos::Unmanaged>>(
1515 Kokkos::subview(
dl, std::make_pair((std::size_t)0, list.size())),
1516 Kokkos::View<
const int*, Kokkos::HostSpace, Kokkos::MemoryTraits<Kokkos::Unmanaged>>(
1517 list.data(), list.size()));
1518 cutStart_[(std::size_t)L] =
ds;
1519 cutRows_[(std::size_t)L] =
dl;
1521 cutKey_ =
fo.cutCell.data();
1535 const auto&
fo = *
in.fac;
1536 const bool two =
in.twoPhase;
1537 bool keep = wallValid_ && wallTwo_ ==
two && wallUse_ == nUse_ &&
1538 wallUnk_ ==
in.unknown.data() && cwSnap_.extent(0) == (std::size_t)
fo.n &&
1539 (!
two || ccSnap_.extent(0) == (std::size_t)
fo.n);
1540 if (keep &&
fo.n > 0) {
1541 auto a =
in.facetW,
as = Kokkos::View<const double*, CCMem>(cwSnap_);
1542 auto c =
two ?
in.facetC :
in.facetW,
1543 cs =
two ? Kokkos::View<const double*, CCMem>(ccSnap_) :
as;
1545 Kokkos::parallel_reduce(
1546 "peclet::flow::smg_wall_same", Kokkos::RangePolicy<CCExec>(
CCExec(), 0,
fo.n),
1548 auto same = [](
double u,
double w) {
1549 return u == w && Kokkos::signbit(u) == Kokkos::signbit(w);
1558 if (cwSnap_.extent(0) != (std::size_t)
fo.n)
1559 cwSnap_ = Kokkos::View<double*, CCMem>(
"smg_cw_snap", (std::size_t)
fo.n);
1560 Kokkos::deep_copy(
CCExec(), cwSnap_,
in.facetW);
1562 if (ccSnap_.extent(0) != (std::size_t)
fo.n)
1563 ccSnap_ = Kokkos::View<double*, CCMem>(
"smg_cc_snap", (std::size_t)
fo.n);
1564 Kokkos::deep_copy(
CCExec(), ccSnap_,
in.facetC);
1569 wallUnk_ =
in.unknown.data();
1578 const int a = f / 2, side = f % 2;
1581 const int ext[3] = {
l0.ext.x,
l0.ext.y,
l0.ext.z};
1582 const long st[3] = {1, (
long)
l0.ext.x, (
long)
l0.ext.x *
l0.ext.y};
1584 const int n1 = ext[
t1] - 2 * g,
n2 = ext[
t2] - 2 * g;
1585 const long sa = st[a], s1 = st[
t1], s2 = st[
t2];
1586 const int aInner = side == 0 ? g : ext[a] - g - 1;
1587 if (
l0.plane[f].extent(0) != (std::size_t)
n1 *
n2)
1588 l0.plane[f] = Kokkos::View<double*, CCMem>(
"smg_plane0", (
long)
n1 *
n2);
1589 auto pl =
l0.plane[f];
1592 const double lam =
in.lam;
1594 Kokkos::parallel_for(
1597 const long i = (
long)
aInner *
sa + (
long)(j1 + g) * s1 + (
long)(
j2 + g) * s2;
1598 const double abf = side == 0 ?
saA(i) :
saA(i +
sa);
1599 pl((
long)j1 + (
long)
j2 *
n1) = unk(i) > 0.5 ? lam *
abf : 0.0;
1604 t1 = (a == 0) ? 1 : 0;
1605 t2 = (a == 2) ? 1 : 2;
1611 const C3 ratio =
fin.ratio;
1620 CCField px = c.px, py = c.py, pz = c.pz;
1622 const int gc = c.g,
gf =
fin.g;
1629 "peclet::flow::smg_coarsen_faces",
C3{0, 0, 0}, c.inner,
1631 coarsenOpenAvgCell(px, py, pz,
fx,
fy,
fz,
ce,
fe, gc,
gf, ratio,
icx,
icy,
icz);
1636 "peclet::flow::smg_coarsen_faces",
C3{0, 0, 0}, c.inner,
1638 coarsenOpenAvgCell(px, py, pz,
fx,
fy,
fz,
ce,
fe, gc,
gf, ratio,
icx,
icy,
icz);
1645 const CCField P[3] = {c.px, c.py, c.pz};
1646 for (
int a = 0; a < 3; ++a)
1647 if (!per_[a] &&
touches(c, 2 * a))
1654 const bool hasOut =
in.advective ||
in.hasOutflow;
1655 const CCConst out =
in.advective ?
in.omegaOpen : (
in.hasOutflow ?
in.outflow :
in.kappa);
1659 const int gc = c.g,
gf =
fin.g;
1661 "peclet::flow::smg_restrict_mass",
C3{0, 0, 0},
ci,
1669 for (
int f = 0; f < 6; ++f)
1670 if (dirFace_[f] &&
touches(c, f))
1675 Kokkos::deep_copy(
CCExec(), c.W, 0.0);
1695 if (c.qp[0].extent(0) != c.n)
1696 for (
int a = 0; a < 3; ++a) {
1697 c.qp[a] =
CCField(
"smg_qp", c.n);
1698 c.qm[a] =
CCField(
"smg_qm", c.n);
1701 const int gc = c.g,
gf =
fin.g;
1702 const double ir[3] = {1.0 / (
double)ratio.x, 1.0 / (
double)ratio.y, 1.0 / (
double)ratio.z};
1703 for (
int sgn = 0; sgn < 2; ++sgn) {
1704 CCField qx = sgn == 0 ? c.qp[0] : c.qm[0],
qy = sgn == 0 ? c.qp[1] : c.qm[1],
1705 qz = sgn == 0 ? c.qp[2] : c.qm[2];
1709 const double sg = sgn == 0 ? 1.0 : -1.0;
1714 "peclet::flow::smg_coarsen_adv",
C3{0, 0, 0}, c.inner,
1716 coarsenOpenAvgCell(
qx,
qy,
qz,
fx,
fy,
fz,
ce,
fe, gc,
gf, ratio,
icx,
icy,
icz);
1725 fz = sgn == 0 ?
fin.qp[2] :
fin.qm[2];
1727 "peclet::flow::smg_coarsen_adv",
C3{0, 0, 0}, c.inner,
1729 coarsenOpenAvgCell(
qx,
qy,
qz,
fx,
fy,
fz,
ce,
fe, gc,
gf, ratio,
icx,
icy,
icz);
1738 for (
int a = 0; a < 3; ++a) {
1742 for (
int a = 0; a < 3; ++a)
1743 if (!per_[a] &&
touches(c, 2 * a)) {
1755 if (c.AW.extent(0) != c.n) {
1756 c.AW =
CCField(
"smg_aw", c.n);
1757 c.AE =
CCField(
"smg_ae", c.n);
1758 c.AS =
CCField(
"smg_as", c.n);
1759 c.AN =
CCField(
"smg_an", c.n);
1760 c.AB =
CCField(
"smg_ab", c.n);
1761 c.AT =
CCField(
"smg_at", c.n);
1765 CCField AC = c.AC, AW = c.AW, AE = c.AE, AS = c.AS, AN = c.AN, AB = c.AB, AT = c.AT;
1766 CCConst AFX = c.AFX, AFY = c.AFY, AFZ = c.AFZ, unk = c.unk;
1769 Kokkos::deep_copy(
CCExec(), AW, 0.0);
1770 Kokkos::deep_copy(
CCExec(), AE, 0.0);
1771 Kokkos::deep_copy(
CCExec(), AS, 0.0);
1772 Kokkos::deep_copy(
CCExec(), AN, 0.0);
1773 Kokkos::deep_copy(
CCExec(), AB, 0.0);
1774 Kokkos::deep_copy(
CCExec(), AT, 0.0);
1776 "peclet::flow::smg_coarse_bands",
C3{g, g, g},
C3{e.x - g, e.y - g, e.z - g},
1778 const long sx = 1, sy = e.x, sz = (
long)e.x * e.y;
1780 if (!(unk(i) > 0.5))
1784 AW(i) = AFX(i) -
qpx(i);
1785 AE(i) = AFX(i +
sx) -
qmx(i +
sx);
1786 AS(i) = AFY(i) -
qpy(i);
1787 AN(i) = AFY(i + sy) -
qmy(i + sy);
1788 AB(i) = AFZ(i) -
qpz(i);
1789 AT(i) = AFZ(i + sz) -
qmz(i + sz);
1796 const int a = f / 2;
1799 const int cin[3] = {c.inner.x, c.inner.y, c.inner.z};
1800 const int fin3[3] = {
fin.inner.x,
fin.inner.y,
fin.inner.z};
1801 const int rr[3] = {
fin.ratio.x,
fin.ratio.y,
fin.ratio.z};
1803 const int r1 =
rr[
t1], r2 =
rr[
t2];
1804 if (c.plane[f].extent(0) != (std::size_t)
n1 *
n2)
1805 c.plane[f] = Kokkos::View<double*, CCMem>(
"smg_plane", (
long)
n1 *
n2);
1806 auto pc = c.plane[f];
1807 auto pf =
fin.plane[f];
1809 Kokkos::parallel_for(
1813 for (
int p = 0; p < r1; ++p)
1814 for (
int q = 0; q < r2; ++q)
1815 s += pf((
long)(r1 * j1 + p) + (
long)(r2 *
j2 + q) *
f1);
1825 const C3 e = c.ext,
ci = c.inner, cf = c.cfac;
1827 const double wx = w_[0] / ((
double)cf.x * cf.x),
wy = w_[1] / ((
double)cf.y * cf.y),
1828 wz = w_[2] / ((
double)cf.z * cf.z);
1829 CCField AC = c.AC, AFX = c.AFX, AFY = c.AFY, AFZ = c.AFZ;
1830 CCConst px = c.px, py = c.py, pz = c.pz;
1832 "peclet::flow::smg_coarse_faces",
C3{0, 0, 0}, e,
KOKKOS_LAMBDA(
int x,
int y,
int z) {
1833 const long i = (
long)x + (
long)y * e.x + (
long)z * (
long)e.x * e.y;
1834 AFX(i) = -(
wx * px(i));
1835 AFY(i) = -(
wy * py(i));
1836 AFZ(i) = -(
wz * pz(i));
1839 const double invN = 1.0 / ((
double)cf.x * cf.y * cf.z);
1840 auto cstart = cutStart_[(std::size_t)L];
1841 auto crows = cutRows_[(std::size_t)L];
1842 auto cutCell =
in.fac->cutCell;
1844 CCConst unk = c.unk, mass = c.mass;
1845 auto cfs =
in.fac->cellFacetStart;
1846 auto cw =
in.facetW;
1850 const bool two = two_;
1851 auto ccv =
in.facetC;
1853 if (c.wallG.extent(0) != c.n)
1854 c.wallG =
CCField(
"smg_wall_gathered", c.n);
1855 if (
two && c.wallCG.extent(0) != c.n)
1856 c.wallCG =
CCField(
"smg_wallc_gathered", c.n);
1858 const bool reuse = wallReuse_;
1861 const long sx = 1, sy = e.x, sz = (
long)e.x * e.y;
1863 if (!(unk(i) > 0.5)) {
1865 AFX(i) = AFY(i) = AFZ(i) = 0.0;
1868 double W = 0.0,
Wc = 0.0;
1877 const int row =
crows(k);
1878 if (!(
unk0(cutCell(row)) > 0.5))
1880 for (
int f =
cfs(row); f <
cfs(row + 1); ++f) {
1893 const double tw =
wx * px(i),
te =
wx * px(i +
sx);
1894 const double ts =
wy * py(i),
tn =
wy * py(i + sy);
1895 const double tb =
wz * pz(i),
tt =
wz * pz(i + sz);
1897 AC(i) = ((mass(i) + (((
tw +
te) + (ts +
tn)) + (tb +
tt))) + W) +
Wc;
1900 AC(i) = (mass(i) + (((
tw +
te) + (ts +
tn)) + (tb +
tt))) + W;
1905 for (
int f = 0; f < 6; ++f) {
1906 if (!(dirFace_[f] &&
touches(c, f)))
1908 const int a = f / 2, side = f % 2;
1911 const int ext[3] = {e.x, e.y, e.z};
1912 const long st[3] = {1, (
long)e.x, (
long)e.x * e.y};
1913 const int n1 = ext[
t1] - 2 * g,
n2 = ext[
t2] - 2 * g;
1914 const long sa = st[a], s1 = st[
t1], s2 = st[
t2];
1915 const int aInner = side == 0 ? g : ext[a] - g - 1;
1916 const double tw2 = 2.0 *
wa[a];
1917 auto pl = c.plane[f];
1919 Kokkos::parallel_for(
1922 const long i = (
long)
aInner *
sa + (
long)(j1 + g) * s1 + (
long)(
j2 + g) * s2;
1923 if (!(unk(i) > 0.5))
1925 AC(i) +=
tw2 * pl((
long)j1 + (
long)
j2 *
n1);
1931 std::vector<Level> lv_;
1933 bool fill0Wraps_ =
false;
1939 std::vector<Kokkos::View<int*, CCMem>> cutStart_, cutRows_;
1940 const void* cutKey_ =
nullptr;
1944 Kokkos::View<double*, CCMem> cwSnap_, ccSnap_;
1945 bool wallValid_ =
false, wallTwo_ =
false, wallReuse_ =
false;
1947 const void* wallUnk_ =
nullptr;
1948 double w_[3] = {1.0, 1.0, 1.0};
1949 double hp_[3] = {1.0, 1.0, 1.0};
1950 bool aniso_ =
false;
1951 bool per_[3] = {
true,
true,
true};
1952 bool dirFace_[6] = {
false,
false,
false,
false,
false,
false};
1953 bool singular_ =
false;
1956 bool distributed_ =
false;
1957#ifdef PECLET_FLOW_MPI
1958 MPI_Comm comm_ = MPI_COMM_NULL;
static C3 mgChooseRatio(const double H[3], const bool canA[3], bool aniso, double theta)
static constexpr int kPre
std::function< void(CCField)> Fill
void assembleCoarse(const Inputs &in, int L)
Coarse face form + diagonal: AF_a = -w_a(L) <Lam a> over the full extent; on unknown inner cells AC =...
static constexpr int GC
coarse levels
void buildLevel0SolidFaces(const Inputs &in)
WO-7, level 0: the solid faces AF_a(i) = -(w_a P_a(i)) from the operator's guarded products — the exp...
void sweep(int L, bool fwd)
One full red-black sweep: R -> B forward, B -> R backward.
void sweepColor(int L, int rb)
One colour of red-black Gauss-Seidel on level L: rb = 0 red (global (gx+gy+gz) even),...
void vcycleTwo(int L)
The two-field V-cycle (WO-7): vcycle's structure on both phases — the same sweeps (the 2x2 block upda...
static void tangents(int a, int &t1, int &t2)
void setMetric(const double w[3], const double hp[3])
Per-axis metric (VelocityMG::setMetric): w_a = 1/h_a'^2 and h_a'. Call before init.
void applySurrogateTwo(CCField yf, CCField ys, CCField xf, CCField xs)
y = S x for the two-field surrogate on level 0 (exchanges x first): y_f = S_ff x_f - W x_s,...
bool wallKeep(const Inputs &in)
The gathered coarse wall terms W (and the conjugate W_c) of the last build are reused when their inpu...
bool touches(const Level &lv, int f) const
Does this rank's block on level lv touch global face f (0..5 = -x,+x,-y,+y,-z,+z)?
void setPeriodic(const bool per[3])
Periodicity per axis (the flow's domain faces); a non-periodic axis' ghosts are pinned.
void assembleBands(int L)
A2: the coarse bands from the face form (assembleCoarse) and Q.
int wrapSmooth(int L) const
A red-black pass reads the wrapped neighbour safely only when it has the OTHER colour (so it still ho...
static constexpr int G0
level 0: the scalar's own block
void init(int nx, int ny, int nz, Fill fill0)
Single-rank level table (VelocityMG::init's rule, no depth cap).
void applySurrogate(CCField y, CCField x)
y = S x on level 0 (exchanges x first). For the contraction instrument and tests.
static constexpr int kMaxLevels
no depth cap in practice (the full table)
int levelsUsed() const
Levels the last build set up for the V-cycle (1: level 0 alone, the transient rule).
static constexpr int kBottom
const Level & level(int L) const
void fill(int L, CCField f)
Ghost exchange of a level-L field: level 0 the solver's own; coarse levels the per-level halo (or the...
void zeroNonPeriodicGhosts(Level &lv, CCField f)
C3 chooseRatio(const bool canA[3], C3 cf) const
std::vector< C3 > levelRatios() const
The per-level coarsening ratios (the level table; equal to VelocityMG's on the same grid).
double countUnknownS(int L) const
void coarsenAdvection(const Inputs &in, int L)
A2: Qp/Qm of level L from level L - 1 — level 1 from the guarded positive parts of the level-0 flux,...
CCConst unkSOf(int L) const
static constexpr int kPost
static constexpr double kFullTableKappa
The transient level rule (design §5.2, Amendment A3 + ruling D-WO9-3): kappa_A = 1 + 4 dt' D' sum_a w...
void build(const Inputs &in)
Rebuild the surrogate on every level from the current operator (the level table is kept).
static constexpr double kAspectTheta
void applyTwo(CCField zf, CCField zs, CCField rf, CCField rs)
The two-field z = M^-1 r (WO-7): one V-cycle on the coupled surrogate from z = 0 (level 0 alone: 2 + ...
void apply(CCField z, CCField r)
z = M^-1 r: one V-cycle on the surrogate (level 0 alone: 2 + 2 sweeps) from z = 0.
void restrictZeroX(Level &cs, Level &lv, CCField crhs, CCField fres, CCField cx)
The coarse rhs = restrictAvg of the fine residual, and the coarse iterate x = 0.
void allocate(Level &v, int L)
void removeMeanTwo(int L, CCField f, CCField s)
f, s -= their joint mean over both phases' unknown cells of level L (the singular two-field problem: ...
void buildPlane0(const Inputs &in, int f)
Level-0 Dirichlet boundary plane of face f: <Lam a_bf> = Lam a_bf on unknown boundary cells (sco::dir...
void coarsenPlane(int L, int f)
std::vector< double > contraction(int iters, CCField e, CCField t, CCField z)
Power estimate of the V-cycle's error contraction rho(I - M^-1 S) on the current surrogate (§11 G-ite...
bool buildCutLists(const Inputs &in)
Per coarse level L the overlay rows (cut cells) under each coarse inner cell, in increasing level-0 l...
double countUnknown(int L) const
void setLevel0Wrap(bool on)
Declare that the injected level-0 fill is the single-rank periodic wrap of ALL three axes (Solver::fi...
void allocateSolid(Level &v, int L)
WO-7: the solid-phase arrays of level L (and W on coarse levels), on the first two-field build.
int wrapReads(int L) const
WO-9b, the fused periodic-wrap reads (flow's A3): the axes (bit a) a level-L pass may read across the...
void buildLevel0Faces(const Inputs &in)
Level-0 faces: AF_a(i) = -(Lam w_a a_a(i)) when cells i - st_a and i are both unknowns — the same exp...
void allSum(double *v, int k) const
void buildCoarse(const Inputs &in, int L)
void removeMean(int L, CCField f)
f -= its mean over the unknown cells of level L (CutcellMG::removeMean's form, unweighted).
void buildCoarseSolid(const Inputs &in, int L)
WO-7: the coarse solid phase of level L (after assembleCoarse, which gathered W): pins = max of the c...
CCConst unkOf(int L) const
double norm2(CCField f) const
#define PECLET_FLOW_OMP_SIMD
flow — portable (Kokkos) geometric multigrid for the cut-cell (variable-openness) pressure Poisson.
flow — portable (Kokkos) cut-cell pressure operator + Chorin projection.
Nb nbrsOn(int lx, int ly, int lz, C3 n, int g, long i, long sy, long sz, int wrap)
nbrs in the kernels: on a host backend always the plain i +- s (ScalarMG::wrapReads never asks a host...
void sweepColorFace(CCField x, CCConst rhs, OpV AC, OpV AFX, OpV AFY, OpV AFZ, C3 e, C3 og, int g, int color, C3 n, int wrap)
One colour of the face-form red-black Gauss-Seidel: cutcellSmoothColorFace (mac_pressure....
void residualFace(CCField r, CCConst x, CCConst b, CCField AC, CCField AFX, CCField AFY, CCField AFZ, C3 e, int g, C3 n, int wrap)
r = b - A x (face form, double), inner cells.
void fillShell(CCField f, C3 e, C3 inner, int g, const bool per[3])
The single-rank ghost fill of a level in ONE launch (WO-9b; CutcellMG::fillWrap's form): a ghost cell...
void sweepColorTwo(CCField x, CCField rhs, CCField AC, CCField AFX, CCField AFY, CCField AFZ, CCField AW, CCField AE, CCField AS, CCField AN, CCField AB, CCField AT, CCField xs, CCField rhss, CCField ACs, CCField AFXs, CCField AFYs, CCField AFZs, CCField W, CCConst unkF, C3 e, C3 og, int g, int color, C3 n, int wrap)
One colour of the two-field red-black sweep (same launch forms as cutcellSmoothColorFace).
void zeroPlanes(CCField f, C3 e, int a, int lo, int hi)
f = 0 on the planes [lo, hi) of axis a (full transverse extent).
Nb nbrs(int lx, int ly, int lz, C3 n, int g, long i, long sy, long sz, int wrap)
void residualTwo(CCField r, CCConst x, CCConst b, CCField AC, CCField AFX, CCField AFY, CCField AFZ, CCField AW, CCField AE, CCField AS, CCField AN, CCField AB, CCField AT, CCField rs, CCConst xs, CCConst bs, CCField ACs, CCField AFXs, CCField AFYs, CCField AFZs, CCField W, C3 e, int g, C3 n, int wrap)
The two-field residual: r_f = rhs_f - (S_ff x_f + N_f) + W x_s (N_f from the bands or the faces),...
double bandOff(const XV &x, const BV &AW, const BV &AE, const BV &AS, const BV &AN, const BV &AB, const BV &AT, long i, const Nb &w)
The band-form off-diagonal sum of A2, in its fixed order: ((AW x_W + AE x_E) + (AS x_S + AN x_N)) + (...
double faceOff(const XV &x, const OV &AFX, const OV &AFY, const OV &AFZ, long i, long sx, long sy, long sz, const Nb &w)
The face-form neighbour sum of cutcellSmoothFaceCell, in its order; x's neighbours from nbrs (the coe...
void zeroPinned(CCField x, CCConst unk, C3 e, int g)
x(i) = 0 on the inner cells that are not unknowns (re-zero the pinned cells).
void residualBand(CCField r, CCConst x, CCConst b, CCField AC, CCField AW, CCField AE, CCField AS, CCField AN, CCField AB, CCField AT, C3 e, int g, C3 n, int wrap)
r = b - (AC x + bandOff) over the inner cells (A2's band-form residual).
void twoPhaseCell(const XV &x, const XV &rhs, const BV &AC, const BV &AFX, const BV &AFY, const BV &AFZ, const BV &AW, const BV &AE, const BV &AS, const BV &AN, const BV &AB, const BV &AT, const XV &xs, const XV &rhss, const BV &ACs, const BV &AFXs, const BV &AFYs, const BV &AFZs, const BV &W, const CCConst &unkF, long i, long sx, long sy, long sz, const Nb &w)
The two-field cell update of the conjugate V-cycle (§5.2, WO-7; design A2's WO-7 note): where both ph...
void sweepColorBand(CCField x, CCConst rhs, BV AC, BV AW, BV AE, BV AS, BV AN, BV AB, BV AT, UV unk, C3 e, C3 og, int g, int color, C3 n, int wrap)
One colour of the band-form Gauss-Seidel (A2), on the unknown inner cells of the colour: x_i <- (rhs_...
void restrictMax(CCField coarse, const FV &fine, C3 cext, C3 fext, int gc, int gf, C3 cinner, C3 ratio)
coarse flag = max of the children's flags (1 unknown, 0 pinned), over the coarse inner cells.
void restrictAvgZeroX(CCField coarse, CCField coarseX, CCConst fine, C3 cext, C3 fext, int gc, int gf, C3 cinner, C3 ratio)
void cutcellResidualFaceCell(const RV &r, const XV &x, const BV &b, const OpV &AC, const OpV &AFX, const OpV &AFY, const OpV &AFZ, long i, long sx, long sy, long sz, long xp, long xm, long yp, long ym, long zp, long zm)
void prolongAdd(CCField fine, CCConst coarse, C3 fext, C3 cext, int gf, int gc, C3 finner, C3 ratio)
void cutcellSmoothFaceCell(const PV &phi, const BV &b, const OpV &AC, const OpV &AFX, const OpV &AFY, const OpV &AFZ, long i, long sx, long sy, long sz, long xp, long xm, long yp, long ym, long zp, long zm)
void ccReduce3(const char *name, C3 lo, C3 hi, F f, R &&... reducers)
void applyCutcellOp(CCField y, CCConst x, OpV AC, OpV AW, OpV AE, OpV AS, OpV AN, OpV AB, OpV AT, C3 e, int g)
void ccFor3(const char *name, C3 lo, C3 hi, F f)
Kokkos::View< double *, CCMem > CCField
void restrictAvg(CCField coarse, CCConst fine, C3 cext, C3 fext, int gc, int gf, C3 cinner, C3 ratio)
void coarsenOpenAvgCell(const CV &oxc, const CV &oyc, const CV &ozc, const FV &oxf, const FV &oyf, const FV &ozf, C3 cext, C3 fext, int gc, int gf, C3 ratio, int icx, int icy, int icz)
Kokkos::DefaultExecutionSpace CCExec
bool hostRunSerial(long cells)
double cutcellApplyFaceCell(const XV &x, const OpV &AC, const OpV &AFX, const OpV &AFY, const OpV &AFZ, long i, long sx, long sy, long sz, long xp, long xm, long yp, long ym, long zp, long zm)
void restrictAvgCell(const CV &coarse, const FV &fine, C3 cext, C3 fext, int gc, int gf, C3 ratio, int icx, int icy, int icz)
Kokkos::View< const double *, CCMem > CCConst
flow — the multidimensional iteration policies: iteration order follows storage.
C3 gdim
GLOBAL dims of this level.
double nUnkS
global count of solid unknowns
CCField pz
coarse levels: m and the face products <Lam a>
CCField xs
Conjugate (WO-7): the solid phase in face form (level 0: x/rhs the caller's, AC = SAC_s,...
CCField AFZ
the surrogate, face form (level 0: AC = the caller's SAC)
CCField wallG
WO-9b: the gathered wall term W (and the conjugate W_c) of this coarse level, per cell,...
CCField unk
1 unknown / 0 pinned (level 0: the geometry's flag)
CCField AW
Advective path (A2): the 7 bands (level 0: the operator's AW..AT, AC = SAC; coarse: owned,...
CCField res
level 0: x / rhs are the caller's (z, r); res owned
C3 og
block inner origin (global red-black parity); {0,0,0} single-rank
Kokkos::View< double *, CCMem > plane[6]
Dirichlet domain faces: <Lam a_bf> (owning rank)
double nUnk
global count of unknown cells (the singular mean)
The facet overlay (§3.5): SoA, facet index fastest. Offsets are LINEAR offsets on the block.
Level-0 guarded positive part of the signed low-face flux: P(i) = max(sgn phi_a(i),...
double operator()(long i) const
Level-0 guarded face product Lam a (no metric weight): the face between cell i - st and cell i carrie...
double operator()(long i) const
Level-0 mass m = kappa idt on unknowns, 0 elsewhere (read by restrictAvgCell).
double operator()(long i) const
Neighbour indices of an inner cell for the fused periodic-wrap reads (WO-9b; flow's A3,...
static constexpr double AC
Kokkos::View< double *, peclet::flow::CCMem > OpV
Kokkos::View< float *, IMem > FV
static bool same(double a, double b)
static double mean(const std::vector< double > &a)