14#ifndef PECLET_FLOW_MAC_CUTCELL_MG_HPP
15#define PECLET_FLOW_MAC_CUTCELL_MG_HPP
21#include <Kokkos_Core.hpp>
32#include "peclet/core/solver/graph_amg.hpp"
43#include "peclet/core/decomp/block_decomposer.hpp"
44#include "peclet/core/decomp/grid_redistribute.hpp"
45#include "peclet/core/decomp/redistribute_topology.hpp"
46#include "peclet/core/decomp/stage_comm.hpp"
47#include "peclet/core/decomp/stage_target.hpp"
48#include "peclet/core/halo/grid_halo.hpp"
49#include "peclet/core/halo/grid_halo_topology.hpp"
56using peclet::core::halo::GridHalo;
57using peclet::core::halo::GridHaloTopology;
75#ifdef PECLET_FLOW_OPERATOR_DOUBLE
80using FPV = Kokkos::View<MReal*, CCMem>;
81using FPC = Kokkos::View<const MReal*, CCMem>;
86template <
class CV,
class FV>
91 const int rx = ratio.x,
ry = ratio.y,
rz = ratio.z;
94 auto F = [&](
const FV& T,
int x,
int y,
int z) {
95 return T((
long)x + (
long)y *
fsy + (
long)z *
fsz);
97 double sx = 0, sy = 0, sz = 0;
98 for (
int a = 0; a <
ry; ++a)
99 for (
int b = 0; b <
rz; ++b)
101 for (
int a = 0; a <
rx; ++a)
102 for (
int b = 0; b <
rz; ++b)
104 for (
int a = 0; a <
rx; ++a)
105 for (
int b = 0; b <
ry; ++b)
118 coarsenOpenAvgCell(
oxc,
oyc,
ozc,
oxf,
oyf,
ozf,
cext,
fext, gc,
gf, ratio,
icx,
icy,
icz);
141 int dims[3] = {e.x, e.y, e.z};
142 long st[3] = {1, (
long)e.x, (
long)e.x * e.y};
143 const int b = (a + 1) % 3, c = (a + 2) % 3;
144 const long sa = st[a],
sb = st[b], sc = st[c];
145 const int bf = (s == 0) ? g : (
dims[a] - g);
146 const int db =
dims[b];
148 Kokkos::parallel_for(
149 "peclet::flow::mg_save_face_plane",
MD(
space, {0, 0}, {
dims[b],
dims[c]}),
151 dst((
long)p0 + (
long)
p1 * db) = src((
long)p0 *
sb + (
long)
p1 * sc + (
long)
bf *
sa);
156 int dims[3] = {e.x, e.y, e.z};
157 long st[3] = {1, (
long)e.x, (
long)e.x * e.y};
158 const int b = (a + 1) % 3, c = (a + 2) % 3;
159 const long sa = st[a],
sb = st[b], sc = st[c];
160 const int bf = (s == 0) ? g : (
dims[a] - g);
161 const int db =
dims[b];
163 Kokkos::parallel_for(
164 "peclet::flow::mg_restore_face_plane",
MD(
space, {0, 0}, {
dims[b],
dims[c]}),
166 dst((
long)p0 *
sb + (
long)
p1 * sc + (
long)
bf *
sa) = src((
long)p0 + (
long)
p1 * db);
177 C3 ratio,
int a,
int s) {
183 const int b = (a + 1) % 3, c = (a + 2) % 3;
186 const int cbf = (s == 0) ? gc : (cd[a] - gc);
187 const int fbf = (s == 0) ?
gf : (
fd[a] -
gf);
188 const int rb =
rt[b], rc =
rt[c];
190 const double inv = 1.0 / (
double)(rb * rc);
192 Kokkos::parallel_for(
193 "peclet::flow::mg_coarsen_face_plane",
MD(
space, {0, 0}, {cd[b], cd[c]}),
195 const int ib = p0 - gc,
ic =
p1 - gc;
197 for (
int q0 = 0;
q0 < rb; ++
q0)
198 for (
int q1 = 0;
q1 < rc; ++
q1) {
201 fc = fc < 0 ? 0 : (fc >=
fdc ?
fdc - 1 : fc);
210template <
class RV,
class XV,
class BV,
class OpV>
213 const OpV& AS,
const OpV& AN,
const OpV& AB,
214 const OpV& AT,
long i,
long xp,
long xm,
long yp,
215 long ym,
long zp,
long zm) {
224 "peclet::flow::cc_residual",
C3{g, g, g},
C3{e.x - g, e.y - g, e.z - g},
226 const long sx = 1, sy = e.x, sz = (
long)e.x * e.y;
228 cutcellResidualCell(r, x, b,
AC, AW, AE, AS, AN, AB, AT, i, i +
sx, i -
sx, i + sy, i - sy,
243 const long sx = 1, sy = e.x, sz = (
long)e.x * e.y;
245 cutcellResidualCell(r, x, b,
AC, AW, AE, AS, AN, AB, AT, i, i +
sx, i -
sx, i + sy, i - sy,
254 "peclet::flow::cc_residual",
C3{g, g, g},
C3{e.x - g, e.y - g, e.z - g},
256 const long sx = 1, sy = e.x, sz = (
long)e.x * e.y;
258 cutcellResidualFaceCell(r, x, b,
AC, AFX, AFY, AFZ, i,
sx, sy, sz, i +
sx, i -
sx, i + sy,
259 i - sy, i + sz, i - sz);
270 const long sx = 1, sy = e.x, sz = (
long)e.x * e.y;
272 cutcellResidualFaceCell(r, x, b,
AC, AFX, AFY, AFZ, i,
sx, sy, sz, i +
sx, i -
sx, i + sy,
273 i - sy, i + sz, i - sz);
297 w.xm = (
lx == g) ? i + (
long)(n.x - 1) : i - 1;
298 w.xp = (
lx == g + n.x - 1) ? i - (
long)(n.x - 1) : i + 1;
299 w.ym = (
ly == g) ? i + (
long)(n.y - 1) * sy : i - sy;
300 w.yp = (
ly == g + n.y - 1) ? i - (
long)(n.y - 1) * sy : i + sy;
301 w.zm = (
lz == g) ? i + (
long)(n.z - 1) * sz : i - sz;
302 w.zp = (
lz == g + n.z - 1) ? i - (
long)(n.z - 1) * sz : i + sz;
309 if constexpr (std::is_same_v<typename CCExec::memory_space, Kokkos::HostSpace>) {
310 const int nyi = e.y - 2 * g,
nzi = e.z - 2 * g;
311 const long cells = (
long)
nyi *
nzi * (e.x - 2 * g);
313 const int ly = g + (int)(t %
nyi),
lz = g + (int)(t /
nyi);
314 const long sx = 1, sy = e.x, sz = (
long)e.x * e.y;
315 const int P = (
color + og.x + og.y +
ly + og.z +
lz) & 1;
317 for (
int lx = g + ((P ^ (g & 1)) & 1);
lx < e.x - g;
lx += 2) {
320 cutcellSmoothFaceCell(phi, b,
AC, AFX, AFY, AFZ, i,
sx, sy, sz, w.xp, w.xm, w.yp, w.ym,
329 Kokkos::parallel_for(
"peclet::flow::cc_smooth",
334 Kokkos::parallel_for(
335 "peclet::flow::cc_smooth",
MD(
space, {g, g, g}, {e.x - g, e.y - g, e.z - g}),
337 if (((og.x +
lx + og.y +
ly + og.z +
lz) & 1) !=
color)
339 const long sx = 1, sy = e.x, sz = (
long)e.x * e.y;
342 cutcellSmoothFaceCell(phi, b,
AC, AFX, AFY, AFZ, i,
sx, sy, sz, w.xp, w.xm, w.yp, w.ym,
350 "peclet::flow::cc_residual",
C3{g, g, g},
C3{e.x - g, e.y - g, e.z - g},
352 const long sx = 1, sy = e.x, sz = (
long)e.x * e.y;
355 cutcellResidualFaceCell(r, x, b,
AC, AFX, AFY, AFZ, i,
sx, sy, sz, w.xp, w.xm, w.yp, w.ym,
363 "peclet::flow::cc_apply",
C3{g, g, g},
C3{e.x - g, e.y - g, e.z - g},
365 const long sx = 1, sy = e.x, sz = (
long)e.x * e.y;
368 y(i) =
cutcellApplyFaceCell(x,
AC, AFX, AFY, AFZ, i,
sx, sy, sz, w.xp, w.xm, w.yp, w.ym,
375 "peclet::flow::cc_apply_exact",
C3{g, g, g},
C3{e.x - g, e.y - g, e.z - g},
377 const long sx = 1, sy = e.x, sz = (
long)e.x * e.y;
380 y(i) =
cutcellApplyExactCell(x, ox, oy, oz, i,
sx, sy, sz, w.xp, w.xm, w.yp, w.ym, w.zp,
389template <
class CV,
class FV>
394 for (
int dz = 0; dz < ratio.z; ++dz)
395 for (
int dy = 0; dy < ratio.y; ++dy)
396 for (
int dx = 0; dx < ratio.x; ++dx) {
397 const int fx = ratio.x *
icx + dx +
gf,
fy = ratio.y *
icy + dy +
gf,
405template <
class FV,
class CV>
416 const int x0 = (ratio.x == 2) ? (
ifx >> 1) + gc - 1 + (
ifx & 1) :
ifx + gc;
417 const int y0 = (ratio.y == 2) ? (
ify >> 1) + gc - 1 + (
ify & 1) :
ify + gc;
418 const int z0 = (ratio.z == 2) ? (
ifz >> 1) + gc - 1 + (
ifz & 1) :
ifz + gc;
419 const double wx = (ratio.x == 2) ? ((
ifx & 1) ? 0.25 : 0.75) : 0.0;
420 const double wy = (ratio.y == 2) ? ((
ify & 1) ? 0.25 : 0.75) : 0.0;
421 const double wz = (ratio.z == 2) ? ((
ifz & 1) ? 0.25 : 0.75) : 0.0;
423 auto C = [&](
int xx,
int yy,
int zz) {
return coarse((
long)
xx + (
long)
yy * sy + (
long)
zz * sz); };
424 const double c00 = C(x0, y0,
z0) * (1 -
wx) + C(x0 + 1, y0,
z0) *
wx;
425 const double c10 = C(x0, y0 + 1,
z0) * (1 -
wx) + C(x0 + 1, y0 + 1,
z0) *
wx;
426 const double c01 = C(x0, y0,
z0 + 1) * (1 -
wx) + C(x0 + 1, y0,
z0 + 1) *
wx;
427 const double c11 = C(x0, y0 + 1,
z0 + 1) * (1 -
wx) + C(x0 + 1, y0 + 1,
z0 + 1) *
wx;
434template <
class FV,
class AV>
513 using Member = Kokkos::TeamPolicy<CCExec>::member_type;
517 Kokkos::View<long*, CCMem>
cnt;
518 Kokkos::View<int*, CCMem>
nc;
522 Kokkos::View<int*, CCMem>
kc;
523 Kokkos::View<double*, CCMem>
aug;
525 const int t = q /
inner.
x;
534 const Kokkos::View<int*, CCMem>& dst)
const {
538 Kokkos::parallel_reduce(
539 Kokkos::TeamThreadRange(t,
ninner),
540 [&](
int q,
int& acc) {
546 const long nb[6] = {w.xp, w.xm, w.yp, w.ym, w.zp, w.zm};
549 for (
int f = 0; f < 6; ++f) {
550 const int lj = src(nb[f]);
551 if (cf[f] < 0.0 &&
lj >= 0 &&
lj < m)
564 Kokkos::parallel_for(Kokkos::TeamThreadRange(t,
ninner), [&](
int q) {
567 comp(i) = (
AC(i) > 1e-30f) ? (
int)i : -1;
578 Kokkos::single(Kokkos::PerTeam(t), [&]() {
580 for (
int q = 0; q <
ninner; ++q) {
583 if (
comp(i) == (
int)i)
588 for (
int q = 0; q <
ninner; ++q) {
606 for (
int q = 0; q <
ninner; ++q) {
609 const int c =
comp(i);
613 if (
pass == 0 && k >
kc(c))
615 else if (
pass == 1 && k ==
kc(c))
619 aug(c) = m[c] > 0 ? 1.0 / (
double)m[c] : 0.0;
628template <
class FacReal =
float>
630 using Member = Kokkos::TeamPolicy<CCExec>::member_type;
639 Kokkos::View<const int*, CCMem>
comp;
640 Kokkos::View<const long*, CCMem>
ccnt;
642 Kokkos::View<double*, CCMem>
ks;
644 Kokkos::View<int*, CCMem>
info;
657 const int t = q / nx;
658 lx = 1 + (q - t * nx);
660 ly = 1 + (t -
lz * ny);
669 static constexpr bool kHostLane = std::is_same_v<CCMem, Kokkos::HostSpace>;
679 for (
int q = 0; q < n; ++q)
685 Kokkos::parallel_reduce(Kokkos::TeamThreadRange(t, n), f, s);
698 for (
int q = 0; q < n; ++q)
704 Kokkos::parallel_reduce(Kokkos::TeamThreadRange(t, n), f, Kokkos::Max<double>(s));
713 if (l.
AC(i) > 1e-30f)
721 for (
int c = 0; c <
nc; ++c) {
722 const double s =
teamSum(t,
nInner(l), [&](
int q,
double& acc) {
728 Kokkos::single(Kokkos::PerTeam(t), [&]() {
cmean(c) = s / (
double)
ccnt(c); });
731 Kokkos::parallel_for(Kokkos::TeamThreadRange(t,
nInner(l)), [&](
int q) {
734 const int c =
comp(i);
744 dir.apply(t, R, Z,
comp, keep);
754 const long sy =
B0.ext.x, sz = (
long)
B0.ext.x *
B0.ext.y;
755 Kokkos::parallel_for(Kokkos::TeamThreadRange(t,
nExt(
B0)), [&](
int i) {
x(i) = 0.0; });
757 Kokkos::parallel_for(Kokkos::TeamThreadRange(t,
nInner(
B0)), [&](
int q) {
760 r(i) = (
comp(i) >= 0) ?
b(i) : 0.0;
765 auto rmax = [&](
int q,
double& acc) {
768 if (
B0.AC(i) > 1e-30f) {
769 const double v = Kokkos::fabs(
r(i));
778 if (r0 != 0.0 && !
fail) {
780 Kokkos::parallel_for(Kokkos::TeamThreadRange(t,
nExt(
B0)), [&](
int i) {
p(i) =
z(i); });
786 Kokkos::parallel_for(Kokkos::TeamThreadRange(t,
nInner(
B0)), [&](
int q) {
790 ap(i) =
cutcellApplyFaceCell(
p,
B0.AC,
B0.AFX,
B0.AFY,
B0.AFZ, i, 1L, sy, sz, w.xp, w.xm,
791 w.yp, w.ym, w.zp, w.zm);
795 if (!(Kokkos::isfinite(
pAp) &&
pAp > 1e-300)) {
799 const double a =
rz /
pAp;
802 Kokkos::parallel_for(Kokkos::TeamThreadRange(t,
nInner(
B0)), [&](
int q) {
820 if (
B0.AC(i) > 1e-30f)
821 acc +=
r(i) * (
z(i) -
zp(i));
823 const double beta =
rzc /
rz;
825 if (!(Kokkos::isfinite(
rz) && Kokkos::isfinite(beta))) {
829 Kokkos::parallel_for(Kokkos::TeamThreadRange(t,
nInner(
B0)), [&](
int q) {
832 p(i) =
z(i) + beta *
p(i);
838 Kokkos::parallel_for(Kokkos::TeamThreadRange(t,
nExt(
B0)), [&](
int i) {
x(i) = 0.0; });
839 Kokkos::single(Kokkos::PerTeam(t), [&]() {
ks(
flagSlot) = 1.0; });
844 Kokkos::single(Kokkos::PerTeam(t), [&]() {
info(0) = it; });
853 static const int lv = [] {
854 const char* e = std::getenv(
"PECLET_FLOW_MG_DEBUG");
855 return e ? std::atoi(e) : 0;
860 static const int n = [] {
861 const char* e = std::getenv(
"PECLET_FLOW_MG_DEBUG_SOLVES");
862 return e ? std::atoi(e) : 3;
878#ifdef PECLET_FLOW_MPI
895#ifdef PECLET_FLOW_MPI
896 std::shared_ptr<GridHaloTopology<3>> halo;
897 std::shared_ptr<GridHalo<double>> dev;
906 std::shared_ptr<Telescope>
tele;
909#ifdef PECLET_FLOW_MPI
933 peclet::core::decomp::StageComm comm;
934 bool root()
const {
return comm.active; }
935 bool repartition()
const {
return comm.kind == peclet::core::decomp::StageKind::Repartition; }
938 C3 mInner{}, mOg{}, mExt{};
940 std::vector<C3> memO, memS;
942 peclet::core::decomp::RedistributeTopology<3, double> move;
945 peclet::core::decomp::BlockDecomposer<3> srcDec, dstDec;
946 std::vector<double> back;
950 static constexpr int G = 1;
956 return C3{lv.og.x - lv.g + 1, lv.og.y - lv.g + 1, lv.og.z - lv.g + 1};
989 for (
int a = 0; a < 3; ++a)
996 const double lim = theta *
hmin;
1012 for (
int a = 0; a < 3; ++a)
1014 aniso_ = !(hp_[0] == 1.0 && hp_[1] == 1.0 && hp_[2] == 1.0);
1019 r.reserve(lv_.size());
1020 for (
const auto& v : lv_)
1021 r.push_back(v.ratio);
1034 C3 inner{nx, ny, nz}, cf{1, 1, 1};
1035 for (
int L = 0; L <
nLevels; ++L) {
1039 v.ext =
C3{inner.x + 2 *
G, inner.y + 2 *
G, inner.z + 2 *
G};
1041 v.n = (std::size_t)v.ext.x * v.ext.y * v.ext.z;
1042 auto can = [&](
int d) {
return (d % 2 == 0) && (d / 2 >= 2); };
1048 const bool canA[3] = {can(inner.x), can(inner.y), can(inner.z)};
1049 const double H[3] = {hp_[0] * (
double)cf.x, hp_[1] * (
double)cf.y, hp_[2] * (
double)cf.z};
1052 next.
x = inner.x / 2;
1054 next.
y = inner.y / 2;
1056 next.
z = inner.z / 2;
1060 v.rhs =
CCField(
"mg_rhs", v.n);
1061 v.res =
CCField(
"mg_res", v.n);
1065 for (
FPV* p : {&v.AC, &v.AFX, &v.AFY, &v.AFZ})
1066 *p =
FPV(
"mg_A", v.n);
1068 if (next.
x == inner.x && next.
y == inner.y && next.
z == inner.z)
1071 cf =
C3{cf.x * ratio.x, cf.y * ratio.y, cf.z * ratio.z};
1075 printf(
"[mg] init %dx%dx%d single-rank -> %d levels (requested %d)\n", nx, ny, nz,
1077 for (
int L = 0; L < (int)lv_.size(); ++L)
1078 printf(
"[mg] L%d dims %4dx%4dx%4d ratio(%d,%d,%d)\n", L, lv_[L].inner.x, lv_[L].inner.y,
1079 lv_[L].inner.z, lv_[L].ratio.x, lv_[L].ratio.y, lv_[L].ratio.z);
1083#ifdef PECLET_FLOW_MPI
1111 static bool teleLiftable(
const peclet::core::decomp::BlockDecomposer<3>& d,
C3 gs) {
1112 auto can = [](
int e) {
return (e % 2 == 0) && (e / 2 >= 2); };
1113 const int ga[3] = {gs.x, gs.y, gs.z};
1114 for (
int ax = 0; ax < 3; ++ax) {
1117 for (std::size_t b = 0; b < d.sizes().size(); ++b)
1118 if ((d.origins()[b][ax] % 2) || (d.sizes()[b][ax] % 2))
1129 static peclet::core::IVec<3> coarsenAlignment(
int gnx,
int gny,
int gnz) {
1130 auto can = [](
int d) {
return (d % 2 == 0) && (
d / 2 >= 2); };
1131 C3
gs{gnx, gny, gnz};
1132 peclet::core::IVec<3>
a{1, 1, 1};
1133 for (
bool any =
true; any;) {
1159 for (
int k = 0;
k < 3; ++
k)
1175 static peclet::core::IVec<3> refineFactor(
int gnx,
int gny,
int gnz,
int levels) {
1176 auto can = [](
int d) {
return (d % 2 == 0) && (
d / 2 >= 2); };
1177 C3
gs{gnx, gny, gnz};
1178 peclet::core::IVec<3>
r{1, 1, 1};
1179 for (
int L = 1;
L < levels; ++
L) {
1198 static peclet::core::decomp::BlockDecomposer<3> decomposition(std::size_t numBlocks,
int gnx,
1199 int gny,
int gnz,
int levels = 0,
1200 double maxImbalance = 1.05) {
1202 return peclet::core::decomp::BlockDecomposer<3>(
1203 numBlocks, peclet::core::IVec<3>{gnx, gny, gnz}, coarsenAlignment(gnx, gny, gnz));
1210 if (!(maxImbalance > 1.0))
1211 maxImbalance = 1.05;
1212 auto imbalanceOf = [](
const peclet::core::decomp::BlockDecomposer<3>&
d) {
1213 std::size_t hi = 0,
lo = std::numeric_limits<std::size_t>::max();
1214 for (
const auto& s :
d.sizes()) {
1215 const std::size_t
n =
static_cast<std::size_t
>(
s[0]) *
static_cast<std::size_t
>(s[1]) *
1216 static_cast<std::size_t
>(
s[2]);
1217 hi =
n > hi ?
n : hi;
1220 return lo ?
static_cast<double>(hi) /
static_cast<double>(lo)
1221 : std::numeric_limits<double>::infinity();
1223 for (
int L = levels;
L >= 2; --
L) {
1224 const peclet::core::IVec<3>
r = refineFactor(gnx, gny, gnz, L);
1225 if (r[0] == 1 && r[1] == 1 && r[2] == 1)
1227 const std::size_t cells =
static_cast<std::size_t
>(gnx /
r[0]) *
1228 static_cast<std::size_t
>(gny / r[1]) *
1229 static_cast<std::size_t
>(gnz /
r[2]);
1230 if (cells < numBlocks)
1235 peclet::core::decomp::BlockDecomposer<3> coarse;
1236 coarse.init(numBlocks, peclet::core::IVec<3>{gnx /
r[0], gny /
r[1], gnz /
r[2]},
1237 peclet::core::IVec<3>{1, 1, 1},
r);
1238 peclet::core::decomp::BlockDecomposer<3> fine = coarse.refined(r);
1239 if (imbalanceOf(fine) <= maxImbalance) {
1241 printf(
"[mg] decomposition: coarse-first depth %d (refine %dx%dx%d, imbalance %.3f)\n", L,
1242 r[0], r[1], r[2], imbalanceOf(fine));
1246 return peclet::core::decomp::BlockDecomposer<3>(numBlocks, peclet::core::IVec<3>{gnx, gny, gnz},
1247 coarsenAlignment(gnx, gny, gnz));
1250 void initMpi(
int gnx,
int gny,
int gnz,
int nLevels, MPI_Comm comm,
1251 const peclet::core::decomp::BlockDecomposer<3>* dec0 =
nullptr) {
1255 distributed_ =
true;
1261 int rank = 0, size = 1;
1262 MPI_Comm_rank(comm, &rank);
1263 MPI_Comm_size(comm, &size);
1264 std::array<bool, 3> per{
true,
true,
true};
1265 C3
gs{gnx, gny, gnz},
cf{1, 1, 1};
1266 auto can = [&](
int d) {
return (d % 2 == 0) && (
d / 2 >= 2); };
1272 peclet::core::decomp::BlockDecomposer<3> curDec;
1276 curDec = decomposition(
static_cast<std::size_t
>(size),
gs.x,
gs.y,
gs.z);
1281 MPI_Comm curComm =
comm;
1286 const peclet::core::Index maxBlockCells =
1287 repartition_ ? peclet::core::decomp::largestBlockCells(curDec) : 0;
1288 auto evenOn = [](
const peclet::core::decomp::BlockDecomposer<3>&
d,
int ax) {
1289 for (std::size_t b = 0;
b <
d.sizes().size(); ++
b)
1290 if ((
d.origins()[b][ax] % 2) || (
d.sizes()[
b][
ax] % 2))
1296 auto minBlockExtent = [](
const peclet::core::decomp::BlockDecomposer<3>&
d) {
1297 long m = std::numeric_limits<long>::max();
1298 for (
const auto& s :
d.sizes())
1299 for (
int k = 0;
k < 3; ++
k)
1300 m = std::min(m, (
long)
s[
k]);
1305 v.halo = std::make_shared<GridHaloTopology<3>>();
1306 const peclet::core::decomp::BlockDecomposer<3>& dec = curDec;
1314 v.g = (
L > 0 && !hasBC_ && (caMode_ &
kCaMg) && minBlockExtent(dec) >= 4) ? 2 : 1;
1315 v.caOk = (
v.g == 2);
1316 v.halo->buildTopology(dec, curRank,
v.g, per, curComm);
1318 v.dev = std::make_shared<GridHalo<double>>();
1319 v.dev->init(*
v.halo);
1320 v.dev->setLabel(
"mg L" + std::to_string(L) +
" g" + std::to_string(
v.g) +
" ranks" +
1321 std::to_string(curDec.numBlocks()));
1322 const auto& idx =
v.halo->indexer();
1323 const auto eg = idx.sizeInclGhost(), ino = idx.sizeInner(), oig = idx.originInclGhost();
1324 v.ext = {(
int)eg[0], (
int)
eg[1], (
int)eg[2]};
1325 v.inner = {(
int)ino[0], (
int)ino[1], (
int)ino[2]};
1326 v.og = {(
int)oig[0] +
v.g, (
int)oig[1] +
v.g,
1330 v.n = idx.numCellsInclGhost();
1332 bool idleBelow =
false;
1339 namespace cdec = peclet::core::decomp;
1340 const bool canAny = can(
gs.x) || can(
gs.y) || can(
gs.z);
1342 auto liftable = [&](
const cdec::BlockDecomposer<3>&
d) {
return teleLiftable(d, gs); };
1343 const bool blocked = !liftable(curDec);
1344 const bool tooSmall = teleMinExtent_ > 0 &&
1345 cdec::minBlockExtent(curDec) < (peclet::core::Index)teleMinExtent_;
1346 const bool doTele = canAny && curDec.numBlocks() > 1 &&
1347 ((telescope_ && (blocked || tooSmall)) || teleForce_ ==
L);
1357 std::optional<cdec::StageTarget<3>>
t;
1358 if (teleForce_ == L) {
1359 if (
auto m = cdec::shallowestLiftableMerge(curDec, liftable, teleMinExtent_)) {
1361 t->kind = cdec::StageKind::SiblingMerge;
1362 t->dec = std::move(
m->dec);
1363 t->ownerOf = std::move(
m->ownerOf);
1364 t->groupOf = std::move(
m->groupOf);
1367 t = cdec::chooseStageTarget(curDec, peclet::core::IVec<3>{
gs.x,
gs.y,
gs.z}, liftable,
1368 teleMinExtent_, maxBlockCells);
1369 if (
t->kind != cdec::StageKind::SiblingMerge &&
t->kind != cdec::StageKind::Repartition)
1370 throw std::logic_error(
1371 "CutcellMG::initMpi: the telescope search found no merge (depth 0 always lifts "
1372 "under flow's rule)");
1375 auto T = std::make_shared<Telescope>();
1376 T->comm = cdec::makeStageComm(curComm, *t);
1381 const int tb =
T->repartition() ?
T->comm.myTargetBlock :
T->comm.myGroup;
1383 const auto mb =
t->dec.block((std::size_t)tb);
1384 T->mOg = C3{(
int)mb.origin[0], (
int)mb.origin[1], (
int)mb.origin[2]};
1385 T->mInner = C3{(
int)mb.size[0], (
int)mb.size[1], (
int)mb.size[2]};
1386 T->mExt = C3{
T->mInner.x + 2 *
T->g,
T->mInner.y + 2 *
T->g,
T->mInner.z + 2 *
T->g};
1388 if (
T->repartition()) {
1393 for (
const int b :
T->
comm.members) {
1394 const auto fb = curDec.block((std::size_t)b);
1395 T->memO.push_back(C3{(
int)fb.origin[0], (
int)fb.origin[1], (
int)fb.origin[2]});
1396 T->memS.push_back(C3{(
int)fb.size[0], (
int)fb.size[1], (
int)fb.size[2]});
1398 T->nMembers = (
int)
T->comm.members.size();
1403 const C3
lo =
v.og, le =
v.ext, mo =
T->mOg, me =
T->mExt;
1404 const int lg =
v.g, mg =
T->g;
1405 using peclet::core::Index;
1406 auto srcIdx = [&](
const peclet::core::IVec<3>&
c) {
1407 return (Index)(
c[0] -
lo.x + lg) + (Index)(
c[1] -
lo.y + lg) * le.x +
1408 (Index)(
c[2] -
lo.z + lg) * (Index)le.x * le.y;
1410 auto dstIdx = [&](
const peclet::core::IVec<3>&
c) {
1411 return (Index)(
c[0] - mo.x + mg) + (Index)(
c[1] - mo.y + mg) * me.x +
1412 (Index)(
c[2] - mo.z + mg) * (Index)me.x * me.y;
1414 T->move.build(curDec, *t,
T->comm, srcIdx, dstIdx, L);
1415 T->back.assign(
v.n, 0.0);
1418 const std::size_t mn = (std::size_t)
T->mExt.x *
T->mExt.y *
T->mExt.z;
1428 curComm =
T->comm.sub;
1429 MPI_Comm_rank(curComm, &curRank);
1435 auto evenBlocks = [&](
int ax) {
return evenOn(curDec, ax); };
1442 const bool canA[3] = {can(
gs.x) && evenBlocks(0), can(
gs.y) && evenBlocks(1),
1443 can(
gs.z) && evenBlocks(2)};
1444 const double H[3] = {hp_[0] * (double)
cf.x, hp_[1] * (
double)
cf.y, hp_[2] * (double)
cf.z};
1460 for (
FPV* p : {&
v.AC, &
v.AFX, &
v.AFY, &
v.AFZ})
1461 *p =
FPV(
"mg_A",
v.n);
1464 teleActive_ =
false;
1467 if (next.x ==
gs.x && next.y ==
gs.y && next.z ==
gs.z)
1473 curDec = curDec.coarsened(peclet::core::IVec<3>{
ratio.x,
ratio.y,
ratio.z});
1476 printf(
"[mg] initMpi %dx%dx%d np=%d -> %d levels (requested %d)%s\n", gnx, gny, gnz, size,
1477 (
int)lv_.size(),
nLevels, telescope_ ?
" [telescope ON]" :
"");
1478 C3
g{gnx, gny, gnz};
1480 for (
int L = 0;
L < (
int)lv_.size(); ++
L) {
1482 "[mg] L%d global %4dx%4dx%4d ranks %5d rank0 block %4dx%4dx%4d ratio(%d,%d,%d)%s\n",
1483 L,
g.x,
g.y,
g.z, ranks, lv_[L].inner.x, lv_[L].inner.y, lv_[L].inner.z, lv_[L].ratio.x,
1484 lv_[L].ratio.y, lv_[L].ratio.z,
1485 lv_[L].tele ? (lv_[L].tele->repartition() ?
" -> REPARTITION" :
" -> TELESCOPE")
1489 MPI_Comm_size(lv_[L].tele->comm.sub, &sub);
1492 g = C3{
g.x / lv_[
L].ratio.x,
g.y / lv_[
L].ratio.y,
g.z / lv_[
L].ratio.z};
1506 hasOutflow_ =
false;
1507 for (
int i = 0; i < 6; ++i) {
1523 B3 e{lv.ext.x, lv.ext.y, lv.ext.z};
1524 for (
int a = 0; a < 3; ++a)
1525 for (
int s = 0; s < 2; ++s)
1551 if (!hasBC_ || !bcGhost_)
1553 B3 e{lv.ext.x, lv.ext.y, lv.ext.z};
1554 for (
int a = 0; a < 3; ++a)
1555 for (
int s = 0; s < 2; ++s) {
1556 const int t = bc_[2 * a + s];
1564 const int a = f / 2;
1565 const int o = (a == 0) ? lv.og.x : (a == 1) ? lv.og.y : lv.og.z;
1566 const int n = (a == 0) ? lv.inner.x : (a == 1) ? lv.inner.y : lv.inner.z;
1567 const int gd = (a == 0) ? lv.gdim.x : (a == 1) ? lv.gdim.y : lv.gdim.z;
1568 return (f % 2 == 0) ? (o == 0) : (o + n ==
gd);
1589 B3 e{lv.ext.x, lv.ext.y, lv.ext.z};
1591 for (
int a = 0; a < 3; ++a)
1592 for (
int s = 0; s < 2; ++s) {
1593 const int t = bc_[2 * a + s];
1596 if (t == 1 || t == 2 || t == 4) {
1598 }
else if (t == 3) {
1599 if (!outflowCoeff_) {
1609 if (
fo ==
nullptr) {
1622 int dims[3] = {lv.ext.x, lv.ext.y, lv.ext.z};
1623 for (
int a = 0; a < 3; ++a) {
1626 const int b = (a + 1) % 3, c = (a + 2) % 3;
1627 const std::size_t
np = (std::size_t)
dims[b] *
dims[c];
1628 if (bcPlane_[a].extent(0) !=
np)
1630 Kokkos::view_alloc(
"peclet::flow::mg_bcplane", Kokkos::WithoutInitializing),
np);
1645 Kokkos::deep_copy(f.ox, ox);
1646 Kokkos::deep_copy(f.oy, oy);
1647 Kokkos::deep_copy(f.oz, oz);
1658#ifdef PECLET_FLOW_MPI
1669 for (
int L = 1; L < (int)lv_.size(); ++L) {
1672#ifdef PECLET_FLOW_MPI
1680 c.g, T.g, c.inner,
fin.ratio);
1687 fin.ext, c.g,
fin.g, c.inner,
fin.ratio);
1690#ifdef PECLET_FLOW_MPI
1693 const double sx = 1.0 / (
double)(c.cfac.x * c.cfac.x),
1694 sy = 1.0 / (
double)(c.cfac.y * c.cfac.y),
1695 sz = 1.0 / (
double)(c.cfac.z * c.cfac.z);
1702 c.ext, c.g == 2 ? c.g - 1 : c.g,
idx2 *
sx,
idy2 * sy,
idz2 * sz);
1703#ifdef PECLET_FLOW_MPI
1724 if (!bottomConnKnown_ && bottomStore_)
1738 return solvePCGResident(b, x, r, p, z,
Ap, maxit, rtol,
pre,
post,
bottom,
star,
nStar,
1740 solveFailed_ =
false;
1741 OverlayScope
overlay(overlaySolve_,
star !=
nullptr);
1746 Kokkos::deep_copy(
CCExec(),
l0.x, x);
1754 Kokkos::deep_copy(
CCExec(), r, b);
1757 const double r0 =
maxabs(l0, r);
1761 const double rref = stopRef_ > 0.0 ? stopRef_ : r0;
1767#ifdef PECLET_FLOW_MPI
1769 MPI_Comm_rank(comm_, &dbgRank);
1773 printf(
"[mg] solve %d: r0=%.6e rtol=%.1e (pre=%d post=%d bottom=%d)\n", dbgSolve_, r0, rtol,
1779 if (r0 > 0.0 && std::isfinite(r0)) {
1781 Kokkos::deep_copy(
CCExec(), p, z);
1782 double rz =
dot(l0, r, z);
1783 if (!std::isfinite(rz)) {
1788 solveFailed_ =
true;
1790 "peclet::flow CutcellMG::solvePCG: preconditioner produced non-finite z; "
1791 "returning zero correction (reported as %d/%d iterations, i.e. a CAPPED solve)\n",
1793 Kokkos::deep_copy(
CCExec(), x, 0.0);
1794 Kokkos::deep_copy(
CCExec(), l0.
x, x);
1795 if (strictPressure_)
1796 throw std::runtime_error(
1797 "peclet::flow CutcellMG::solvePCG: preconditioner produced non-finite z "
1798 "(set_pressure_strict)");
1801 for (;
it < maxit; ++
it) {
1803 if (meanRemovalAll_)
1805 const double pAp =
dot(l0, p, Ap);
1806 if (!std::isfinite(pAp) || pAp <= 1e-300) {
1807 if (!std::isfinite(pAp))
1808 solveFailed_ =
true;
1811 const double alpha = rz / pAp;
1813 axpy(r, -alpha, Ap);
1815 const double rn =
maxabs(l0, r);
1817 printf(
"[mg] it %3d |r|inf=%.6e r/r0=%.4e\n", it + 1, rn, rn / r0);
1818 if (rn < rtol * rref) {
1823 const double rznew =
dot(l0, r, z), beta = rznew / rz;
1824 if (!std::isfinite(rznew)) {
1825 solveFailed_ =
true;
1832 Kokkos::deep_copy(
CCExec(), l0.
x, x);
1834 Kokkos::deep_copy(
CCExec(), x, l0.
x);
1867 Kokkos::View<double*, CCMem>
ks_;
1869 Kokkos::View<double*, Kokkos::HostSpace>
kpk_;
1873 ks_ = Kokkos::View<double*, CCMem>(
"peclet::flow::mg_krylov_scalars",
kNSlots);
1874 kcnt_ = Kokkos::View<long, CCMem>(
"peclet::flow::mg_mean_count");
1875 kpk_ = Kokkos::View<double*, Kokkos::HostSpace>(
"peclet::flow::mg_krylov_packet",
kPacket);
1877 Kokkos::View<double, CCMem>
slot(
int k)
const {
1878 return Kokkos::View<double, CCMem>(
ks_.data() + k);
1882 Kokkos::deep_copy(v,
slot(k));
1894 "mgdot",
C3{g, g, g},
C3{e.x - g, e.y - g, e.z - g},
1896 const long i = (
long)x + (
long)y * e.x + (
long)z * (
long)e.x * e.y;
1898 acc +=
aa(i) *
bb(i);
1908 "mgmax",
C3{g, g, g},
C3{e.x - g, e.y - g, e.z - g},
1910 const long i = (
long)x + (
long)y * e.x + (
long)z * (
long)e.x * e.y;
1911 if (
ac(i) > 1e-30f) {
1912 const double v = Kokkos::fabs(
aa(i));
1917 Kokkos::Max<double, CCMem>(
slot(k)));
1923 dbgBrkWhich_ = which;
1929 solveFailed_ =
false;
1936 Kokkos::deep_copy(
CCExec(),
l0.x, x);
1944 Kokkos::deep_copy(
CCExec(), r, b);
1951 printf(
"[mg] solve %d: r0=%.6e rtol=%.1e (pre=%d post=%d bottom=%d)\n", dbgSolve_, r0, rtol,
1954 if (r0 > 0.0 && std::isfinite(r0)) {
1956 Kokkos::deep_copy(
CCExec(), p, z);
1959 if (!std::isfinite(
rz0)) {
1960 solveFailed_ =
true;
1962 "peclet::flow CutcellMG::solvePCG: preconditioner produced non-finite z; "
1963 "returning zero correction (reported as %d/%d iterations, i.e. a CAPPED solve)\n",
1965 Kokkos::deep_copy(
CCExec(), x, 0.0);
1966 Kokkos::deep_copy(
CCExec(),
l0.x, x);
1967 if (strictPressure_)
1968 throw std::runtime_error(
1969 "peclet::flow CutcellMG::solvePCG: preconditioner produced non-finite z "
1970 "(set_pressure_strict)");
1975 const std::size_t n = x.extent(0);
1979 for (; it < maxit; ++it) {
1981 if (meanRemovalAll_)
1984 if (dbgBrkWhich_ == 1 && it == dbgBrkIter_)
1985 setSlot(
kPAp, std::numeric_limits<double>::quiet_NaN());
1986 Kokkos::parallel_for(
1989 if (ks(
kStop) == 0.0) {
1990 if (Kokkos::isfinite(
pAp) &&
pAp > 1e-300)
1996 Kokkos::parallel_for(
1997 "mgpcg_update", Kokkos::RangePolicy<CCExec>(
CCExec(), 0, n),
1999 if (ks(
kStop) != 0.0)
2001 const double a = ks(
kAlpha), na = -a;
2003 rr(i) += na *
aa(i);
2007 Kokkos::deep_copy(
kpk_, Kokkos::subview(
ks_, std::make_pair(0,
kPacket)));
2011 solveFailed_ =
true;
2017 if (!std::isfinite(
pAp))
2018 solveFailed_ =
true;
2023 printf(
"[mg] it %3d |r|inf=%.6e r/r0=%.4e\n", it + 1,
rn,
rn / r0);
2024 if (
rn < rtol * r0) {
2031 if (dbgBrkWhich_ == 2 && it == dbgBrkIter_)
2033 Kokkos::parallel_for(
2036 if (Kokkos::isfinite(
rznew)) {
2043 Kokkos::parallel_for(
2045 if (ks(
kStop) != 0.0)
2051 solveFailed_ =
true;
2055 Kokkos::deep_copy(
CCExec(),
l0.x, x);
2057 Kokkos::deep_copy(
CCExec(), x,
l0.x);
2084 solveFailed_ =
false;
2085 OverlayScope
overlay(overlaySolve_,
star !=
nullptr);
2090 Kokkos::deep_copy(
CCExec(),
l0.x, x);
2098 Kokkos::deep_copy(
CCExec(), r, b);
2101 const double r0 =
maxabs(l0, r);
2105 const double rref = stopRef_ > 0.0 ? stopRef_ : r0;
2108#ifdef PECLET_FLOW_MPI
2110 MPI_Comm_rank(comm_, &dbgRank);
2114 printf(
"[mg] fcg solve %d: r0=%.6e rtol=%.1e (pre=%d post=%d bottom=%d)\n", dbgSolve_, r0,
2115 rtol, pre, post, bottom);
2117 if (r0 > 0.0 && std::isfinite(r0)) {
2119 Kokkos::deep_copy(
CCExec(), p, z);
2120 double rz =
dot(l0, r, z);
2121 if (!std::isfinite(rz)) {
2122 solveFailed_ =
true;
2124 "peclet::flow CutcellMG::solveFCG: preconditioner produced non-finite z; "
2125 "returning zero correction (reported as %d/%d iterations, i.e. a CAPPED solve)\n",
2127 Kokkos::deep_copy(
CCExec(), x, 0.0);
2128 Kokkos::deep_copy(
CCExec(), l0.
x, x);
2129 if (strictPressure_)
2130 throw std::runtime_error(
2131 "peclet::flow CutcellMG::solveFCG: preconditioner produced non-finite z "
2132 "(set_pressure_strict)");
2135 for (;
it < maxit; ++
it) {
2137 if (meanRemovalAll_)
2139 const double pAp =
dot(l0, p, Ap);
2140 if (!std::isfinite(pAp) || pAp <= 1e-300) {
2141 if (!std::isfinite(pAp))
2142 solveFailed_ =
true;
2145 const double alpha = rz / pAp;
2147 axpy(r, -alpha, Ap);
2149 const double rn =
maxabs(l0, r);
2151 printf(
"[mg] it %3d |r|inf=%.6e r/r0=%.4e\n", it + 1, rn, rn / r0);
2152 if (rn < rtol * rref) {
2156 Kokkos::deep_copy(
CCExec(), zp, z);
2158 const double rznew =
dot(l0, r, z), rzcross =
dot(l0, r, zp);
2159 if (!std::isfinite(rznew) || !std::isfinite(rzcross)) {
2160 solveFailed_ =
true;
2163 const double beta = (rznew - rzcross) / rz;
2171 printf(
"[mg] it %3d beta=%.6e pr=%.3e (|r^T z_k| / |r^T z_{k+1}|)\n", it + 1, beta,
2172 rznew != 0.0 ? std::abs(rzcross / rznew) : 0.0);
2177 Kokkos::deep_copy(
CCExec(), l0.
x, x);
2179 Kokkos::deep_copy(
CCExec(), x, l0.
x);
2205 solveFailed_ =
false;
2206 OverlayScope
overlay(overlaySolve_,
true);
2212#ifdef PECLET_FLOW_MPI
2213 if (distributed_ && h2) {
2231 Kokkos::deep_copy(
CCExec(), l0.
rhs, rr);
2232 Kokkos::deep_copy(
CCExec(), l0.
x, 0.0);
2234 Kokkos::deep_copy(
CCExec(), zz, l0.
x);
2237 Kokkos::deep_copy(
CCExec(), r, b);
2240 Kokkos::deep_copy(
CCExec(), rh, r);
2241 const double r0n =
maxabs(l0, r);
2243 if (r0n > 0.0 && std::isfinite(r0n)) {
2244 double rho = 1.0,
alpha = 1.0, omega = 1.0;
2246 int lastImprove = 0;
2247 Kokkos::deep_copy(
CCExec(), p, 0.0);
2248 Kokkos::deep_copy(
CCExec(), v, 0.0);
2249 for (;
it < maxit; ++
it) {
2250 const double rhoNew =
dot(l0, rh, r);
2251 if (!std::isfinite(rhoNew) || std::fabs(rhoNew) < 1e-300)
2253 const double beta = (rhoNew /
rho) * (alpha / omega);
2260 const double rhv =
dot(l0, rh, v);
2261 if (!std::isfinite(rhv) || std::fabs(rhv) < 1e-300)
2266 double rn =
maxabs(l0, r);
2267 if (!std::isfinite(rn))
2269 if (rn < rtol * r0n) {
2277 const double tt =
dot(l0, t, t);
2278 if (!std::isfinite(tt) || tt < 1e-300) {
2282 omega =
dot(l0, t, r) / tt;
2283 if (!std::isfinite(omega) || std::fabs(omega) < 1e-300) {
2292 if (!std::isfinite(rn))
2294 if (rn < rtol * r0n) {
2298 if (rn < 0.999 * best) {
2301 }
else if (it - lastImprove > 30) {
2307 Kokkos::deep_copy(
CCExec(), l0.
x, x);
2309 Kokkos::deep_copy(
CCExec(), x, l0.
x);
2318#ifdef PECLET_FLOW_MPI
2327 Telescope&
T = *
lv.tele;
2328 auto hs = Kokkos::create_mirror_view(src);
2329 Kokkos::deep_copy(hs, src);
2331 T.move.forward({hs.data()}, {});
2334 auto hd = Kokkos::create_mirror_view(dst);
2335 T.move.forward({hs.data()}, {hd.data()});
2336 Kokkos::deep_copy(dst, hd);
2343 void teleGatherOutflowPlanes(
const Level& lv) {
2348 for (
int a = 0;
a < 3; ++
a)
2349 if (bc_[2 * a + 1] == 3) {
2350 if (
lv.tele->repartition())
2351 teleGatherPlaneRepartition(lv, src[a], dst[a], a);
2353 teleGatherPlane(lv, src[a], dst[a], a);
2364 Telescope&
T = *
lv.tele;
2365 using Blk = peclet::core::decomp::Block<3>;
2366 const int b = (
a + 1) % 3, c = (a + 2) % 3;
2367 const long gd[3] = {
lv.gdim.x,
lv.gdim.y,
lv.gdim.z};
2368 int rank = 0, np = 1;
2369 MPI_Comm_rank(
T.comm.parent, &rank);
2370 MPI_Comm_size(
T.comm.parent, &np);
2371 auto touches = [&](
const Blk&
k) {
return (
long)(
k.origin[
a] +
k.size[
a]) == gd[a]; };
2373 auto overlap = [&](
const Blk&
p,
const Blk&
q,
long lo[2],
long hi[2]) {
2374 const int ax[2] = {
b,
c};
2375 for (
int e = 0;
e < 2; ++
e) {
2376 lo[
e] = (long)std::max(
p.origin[ax[e]],
q.origin[ax[e]]);
2377 hi[
e] = (long)std::min(
p.origin[ax[e]] +
p.size[ax[e]],
q.origin[ax[e]] +
q.size[ax[e]]);
2383 std::vector<int>
sc((std::size_t)np, 0), sd((std::size_t)np, 0), rc((std::size_t)np, 0),
2384 rd((std::size_t)np, 0);
2385 std::vector<double> sb,
rb;
2386 const Blk me =
T.srcDec.block((std::size_t)rank);
2389 const int ext[3] = {
lv.ext.x,
lv.ext.y,
lv.ext.z};
2390 const long st[3] = {1, (long)
lv.ext.x, (
long)
lv.ext.x *
lv.ext.y};
2391 auto hs = Kokkos::create_mirror_view(src);
2392 Kokkos::deep_copy(hs, src);
2393 const long pa = (long)(ext[a] - g) *
st[
a];
2394 for (std::size_t t = 0;
t <
T.dstDec.numBlocks(); ++
t) {
2396 const Blk
tb =
T.dstDec.block(t);
2397 if (!touches(tb) || !overlap(me, tb, lo, hi))
2399 sd[
t] = (
int)sb.size();
2400 for (
long k = lo[1];
k < hi[1]; ++
k)
2401 for (
long j = lo[0];
j < hi[0]; ++
j)
2402 sb.push_back(hs(pa + (j - (
long)me.origin[b] + g) *
st[
b] +
2403 (
k - (long)me.origin[c] + g) *
st[
c]));
2404 sc[
t] = (
int)sb.size() - sd[
t];
2407 const bool land =
T.root() && touches(
T.dstDec.block((std::size_t)rank));
2409 const Blk
tb =
T.dstDec.block((std::size_t)rank);
2411 for (std::size_t s2 = 0;
s2 <
T.srcDec.numBlocks(); ++
s2) {
2413 const Blk sk =
T.srcDec.block(s2);
2414 if (!touches(sk) || !overlap(sk, tb, lo, hi))
2417 rc[
s2] = (
int)((hi[0] - lo[0]) * (hi[1] -
lo[1]));
2420 rb.resize((std::size_t)
n);
2422 MPI_Alltoallv(sb.data(),
sc.data(), sd.data(), MPI_DOUBLE,
rb.data(), rc.data(), rd.data(),
2423 MPI_DOUBLE,
T.comm.parent);
2426 const Blk
tb =
T.dstDec.block((std::size_t)rank);
2427 const long mst[3] = {1, (long)
T.mExt.x, (
long)
T.mExt.x *
T.mExt.y};
2428 const int mext[3] = {
T.mExt.x,
T.mExt.y,
T.mExt.z};
2429 auto hd = Kokkos::create_mirror_view(dst);
2430 Kokkos::deep_copy(hd, dst);
2431 const long pa = (long)(mext[a] -
T.g) * mst[
a];
2432 for (std::size_t s2 = 0;
s2 <
T.srcDec.numBlocks(); ++
s2) {
2436 overlap(
T.srcDec.block(s2), tb, lo, hi);
2437 const double*
q =
rb.data() + rd[
s2];
2438 for (
long k = lo[1];
k < hi[1]; ++
k)
2439 for (
long j = lo[0];
j < hi[0]; ++
j)
2440 hd(pa + (j - (
long)
tb.origin[b] +
T.g) * mst[b] +
2441 (k - (
long)
tb.origin[c] +
T.g) * mst[c]) = *
q++;
2443 Kokkos::deep_copy(dst, hd);
2446 Telescope&
T = *
lv.tele;
2447 const int g =
lv.g,
b = (
a + 1) % 3, c = (a + 2) % 3;
2448 const int ext[3] = {
lv.ext.x,
lv.ext.y,
lv.ext.z},
2449 inn[3] = {
lv.inner.x,
lv.inner.y,
lv.inner.z};
2450 const long st[3] = {1, (long)
lv.ext.x, (
long)
lv.ext.x *
lv.ext.y};
2453 std::vector<double> sb;
2455 auto hs = Kokkos::create_mirror_view(src);
2456 Kokkos::deep_copy(hs, src);
2457 sb.resize((std::size_t)inn[b] * inn[c]);
2458 const long pa = (long)(ext[a] - g) *
st[
a];
2459 for (
int k = 0;
k < inn[
c]; ++
k)
2460 for (
int j = 0;
j < inn[
b]; ++
j)
2461 sb[(std::size_t)
j + (std::size_t)k * inn[b]] =
2462 hs(pa + (
long)(
j +
g) * st[b] + (
long)(
k +
g) * st[c]);
2465 std::vector<int> counts, displs;
2466 std::vector<double>
rb;
2467 const int gd[3] = {
lv.gdim.x,
lv.gdim.y,
lv.gdim.z};
2470 for (
int m = 0;
m <
T.nMembers; ++
m) {
2471 const int mo[3] = {
T.memO[
m].x,
T.memO[
m].y,
T.memO[
m].z};
2472 const int ms[3] = {
T.memS[
m].x,
T.memS[
m].y,
T.memS[
m].z};
2473 const int n = (mo[
a] + ms[
a] == gd[
a]) ? ms[b] * ms[c] : 0;
2474 counts.push_back(n);
2475 displs.push_back(disp);
2478 rb.resize((std::size_t)disp);
2480 MPI_Gatherv(sb.data(), (
int)sb.size(), MPI_DOUBLE,
T.root() ?
rb.data() : nullptr,
2481 T.root() ? counts.data() : nullptr,
T.root() ? displs.data() : nullptr, MPI_DOUBLE,
2485 const int mext[3] = {
T.mExt.x,
T.mExt.y,
T.mExt.z};
2486 const long mst[3] = {1, (long)
T.mExt.x, (
long)
T.mExt.x *
T.mExt.y};
2487 const int mog[3] = {
T.mOg.x,
T.mOg.y,
T.mOg.z};
2488 auto hd = Kokkos::create_mirror_view(dst);
2489 Kokkos::deep_copy(hd, dst);
2490 const long pa = (long)(mext[a] -
T.g) * mst[
a];
2491 for (
int m = 0;
m <
T.nMembers; ++
m) {
2494 const int mo[3] = {
T.memO[
m].x,
T.memO[
m].y,
T.memO[
m].z};
2495 const int ms[3] = {
T.memS[
m].x,
T.memS[
m].y,
T.memS[
m].z};
2496 const double*
q =
rb.data() + displs[
m];
2497 for (
int k = 0;
k < ms[
c]; ++
k)
2498 for (
int j = 0;
j < ms[
b]; ++
j)
2499 hd(pa + (
long)(mo[
b] - mog[
b] +
j +
T.g) * mst[b] +
2500 (
long)(mo[
c] - mog[
c] +
k +
T.g) * mst[c]) =
2501 q[(std::size_t)j + (std::size_t)
k * ms[
b]];
2503 Kokkos::deep_copy(dst, hd);
2506 Telescope&
T = *
lv.tele;
2509 auto hs = Kokkos::create_mirror_view(src);
2510 Kokkos::deep_copy(hs, src);
2511 T.move.backward({hs.data()}, {
T.back.data()});
2513 T.move.backward({}, {
T.back.data()});
2515 auto hd = Kokkos::create_mirror_view(dst);
2516 Kokkos::deep_copy(hd, dst);
2517 for (
int k = 0;
k <
lv.inner.z; ++
k)
2518 for (
int j = 0;
j <
lv.inner.y; ++
j)
2519 for (
int i = 0;
i <
lv.inner.x; ++
i) {
2521 (long)(i + g) + (long)(j + g) *
lv.ext.x + (long)(k + g) * (long)
lv.ext.x *
lv.ext.y;
2522 hd(
id) +=
T.back[(std::size_t)
id];
2524 Kokkos::deep_copy(dst, hd);
2534 assert(!
l0.caOk &&
"A5: level 0 must not exchange (write) its rhs");
2542 } restore{
l0,
l0.rhs,
l0.x};
2545 Kokkos::deep_copy(
CCExec(),
zz, 0.0);
2550 const auto t0 = std::chrono::steady_clock::now();
2552 lvTime_.resize(lv_.size(), 0.0);
2553 lvTime_[L] += std::chrono::duration<double>(std::chrono::steady_clock::now() - t0).count();
2554 if (L == 0 && ++lvCycles_ % 50 == 0) {
2556 for (std::size_t i = 0; i < lvTime_.size(); ++i)
2557 tot += (i + 1 < lvTime_.size() ? lvTime_[i] - lvTime_[i + 1] : lvTime_[i]);
2558 printf(
"[mg] level times over %d V-cycles (total %.3f s):\n", lvCycles_, tot);
2559 for (std::size_t i = 0; i < lvTime_.size(); ++i) {
2560 const double self = (i + 1 < lvTime_.size() ? lvTime_[i] - lvTime_[i + 1] : lvTime_[i]);
2561 printf(
"[mg] L%zu %5dx%5dx%5d self %7.3f s (%5.1f%%)\n", i, lv_[i].inner.x,
2562 lv_[i].inner.y, lv_[i].inner.z,
self, 100.0 *
self / (tot + 1e-30));
2575 bool isBottom = (L + 1 == (int)lv_.size());
2576#ifdef PECLET_FLOW_MPI
2583 throw std::runtime_error(
2584 std::string(
"set_pressure_bottom_solver('direct'): not eligible here: ") +
why);
2593 smooth(lv, bottom_,
false);
2594 if (meanRemovalAll_)
2609 FPC(lv.AFY),
FPC(lv.AFZ), lv.ext, lv.g);
2611#ifdef PECLET_FLOW_MPI
2614 const C3 lo{g + 1, g + 1, g + 1};
2615 const C3 hi{lv.ext.x - g - 1, lv.ext.y - g - 1, lv.ext.z - g - 1};
2616 lv.dev->exchangeBegin(lv.x);
2618 FPC(lv.AFY),
FPC(lv.AFZ), lv.ext, lo, hi,
C3{0, 0, 0},
C3{0, 0, 0});
2619 lv.dev->exchangeEnd(lv.x);
2622 FPC(lv.AFY),
FPC(lv.AFZ), lv.ext,
C3{g, g, g},
2623 C3{lv.ext.x - g, lv.ext.y - g, lv.ext.z - g}, lo, hi);
2629 FPC(lv.AFY),
FPC(lv.AFZ), lv.ext, lv.inner, lv.g);
2636#ifdef PECLET_FLOW_MPI
2644 Level& cs = lv_[L + 1];
2646 Kokkos::deep_copy(
CCExec(), cs.x, 0.0);
2651 Kokkos::deep_copy(
CCExec(), T.x, 0.0);
2658 Level& cs = lv_[L + 1];
2659 if (!distributed_) {
2663 restrictAvg(cs.rhs,
CCConst(lv.res), cs.ext, lv.ext, cs.g, lv.g, cs.inner, lv.ratio);
2664 Kokkos::deep_copy(
CCExec(), cs.x, 0.0);
2673 prolongAdd(lv.x,
CCConst(cs.x), lv.ext, cs.ext, lv.g, cs.g, lv.inner, lv.ratio);
2676 if (meanRemovalAll_ || L == 0)
2682 bool caSmooth(
const Level& lv)
const {
return distributed_ && lv.caOk && !hasBC_; }
2688 bool fusedWrapReads()
const {
return !distributed_ && !hasOutflow_ && !overlaySolve_; }
2690 return fusedWrapReads() && lv.inner.x % 2 == 0 && lv.inner.y % 2 == 0 && lv.inner.z % 2 == 0;
2701#ifdef PECLET_FLOW_MPI
2712 const C3 lo{g + 1, g + 1, g + 1};
2713 const C3 hi{lv.ext.x - g - 1, lv.ext.y - g - 1, lv.ext.z - g - 1};
2714 const C3 rlo{g - 1, g - 1, g - 1};
2715 const C3 rhi{lv.ext.x - g + 1, lv.ext.y - g + 1, lv.ext.z - g + 1};
2716 lv.dev->exchange(lv.rhs);
2717 for (
int k = 0; k < sweeps; ++k) {
2718 const int c0 = reverse ? 1 : 0, c1 = 1 - c0;
2719 lv.dev->exchangeBegin(lv.x);
2721 FPC(lv.AFZ), lv.ext, og, c0, lo, hi,
C3{0, 0, 0},
C3{0, 0, 0});
2722 lv.dev->exchangeEnd(lv.x);
2724 FPC(lv.AFZ), lv.ext, og, c0,
rlo,
rhi, lo, hi);
2726 FPC(lv.AFZ), lv.ext, og, g, c1);
2731 for (
int k = 0; k < sweeps; ++k)
2732 for (
int s = 0; s < 2; ++s) {
2733 const int color = reverse ? (1 - s) : s;
2734#ifdef PECLET_FLOW_MPI
2742 const C3 lo{g + 1, g + 1, g + 1};
2743 const C3 hi{lv.ext.x - g - 1, lv.ext.y - g - 1, lv.ext.z - g - 1};
2744 lv.dev->exchangeBegin(lv.x);
2746 FPC(lv.AFZ), lv.ext, og,
color, lo, hi,
C3{0, 0, 0},
2748 lv.dev->exchangeEnd(lv.x);
2752 C3{lv.ext.x - g, lv.ext.y - g, lv.ext.z - g}, lo, hi);
2758 FPC(lv.AFZ), lv.ext, lv.inner, og, lv.g,
color);
2764 FPC(lv.AFZ), lv.ext, og, lv.g,
color);
2791 if (agglomMode_ == 0)
2793 if (agglomMode_ == 1)
2811 const int thresh = agglomExtent_ > 0 ? agglomExtent_ : 4;
2813 long gx = gnxF_,
gy = gnyF_,
gz = gnzF_;
2814 for (
int L = 0; L + 1 < (int)lv_.size(); ++L) {
2815 gx /= lv_[L].ratio.x;
2816 gy /= lv_[L].ratio.y;
2817 gz /= lv_[L].ratio.z;
2824 std::is_same_v<typename CCExec::memory_space, Kokkos::HostSpace>;
2832 return "the solve is distributed (multi-rank)";
2833 if (!removeMean_ || hasOutflow_)
2834 return "the operator is not the singular one (an outflow face is present)";
2836 return "the bottom is not agglomerated (set_pressure_bottom / set_pressure_bottom_extent)";
2838 (
long)lv_.back().inner.x * lv_.back().inner.y * lv_.back().inner.z >
kBottomMaxCells)
2839 return "the bottom level has more than 8192 inner cells";
2840 if (!bottomConnKnown_)
2841 return "the pressure operator has not been built (no setOpenness since the hierarchy)";
2842 if (!bottomConnected_)
2843 return "the bottom level has no fluid cell or more than 64 fluid components (B1b)";
2844 if (!dirPlanes_.
valid())
2845 return "no axis gives planes of at most 192 cells (the direct factor's cap)";
2853 bottomConnKnown_ =
false;
2854 bottomConnected_ =
false;
2855 bottomStore_ =
false;
2857 if (distributed_ || lv_.empty())
2862 bottomStore_ =
true;
2863 for (
CCField* f : {&bottomR_, &bottomP_, &bottomZ_, &bottomZp_, &bottomAp_})
2865 bottomInfo_ = Kokkos::View<int*, CCMem>(
"peclet::flow::mg_bottom_info", 1);
2866 bottomComp_ = Kokkos::View<int*, CCMem>(
"peclet::flow::mg_bottom_comp",
bt.n);
2867 bottomLabTmp_ = Kokkos::View<int*, CCMem>(
"peclet::flow::mg_bottom_labtmp",
bt.n);
2885 bottomConnKnown_ =
true;
2886 bottomConnected_ =
false;
2898 k.comp = bottomComp_;
2899 k.tmp = bottomLabTmp_;
2900 k.cnt = bottomCompCnt_;
2902 k.slow = dirPlanes_.
s;
2905 Kokkos::TeamPolicy<CCExec> probe(
CCExec(), 1, 1);
2906 const int T = std::min(1024, probe.team_size_max(k, Kokkos::ParallelForTag()));
2907 Kokkos::parallel_for(
"peclet::flow::mg_bottom_label",
2908 Kokkos::TeamPolicy<CCExec>(
CCExec(), 1, T), k);
2909 Kokkos::deep_copy(bottomNComp_, Kokkos::subview(bottomInfo_, 0));
2912 printf(
"[mg] device bottom: %d fluid component(s) on the %dx%dx%d bottom%s\n", bottomNComp_,
2913 bt.inner.x,
bt.inner.y,
bt.inner.z,
2914 bottomConnected_ ?
"" :
" -> GraphAMG (more than 64, or none)");
2916 template <
class FR =
float>
2922 k.lv.inner =
bt.inner;
2934 k.precondOnly = precondOnly ? 1 : 0;
2937 k.info = bottomInfo_;
2938 k.nc = bottomNComp_;
2939 k.comp = bottomComp_;
2940 k.ccnt = bottomCompCnt_;
2941 k.cmean = bottomCompMean_;
2948 bottomFlagPending_ =
false;
2950 solveFailed_ =
true;
2961 if (
team > 0 && !probe)
2963 Kokkos::TeamPolicy<CCExec>
pol(
CCExec(), 1, 1);
2965 pol.set_scratch_size(0, Kokkos::PerTeam(
scratch));
2966 const int tmax = std::min(1024,
pol.team_size_max(k, Kokkos::ParallelForTag()));
2976 bool teamAlgorithm =
false) {
2983 k.comp = bottomComp_;
2986 k.tauPiv0 = tauPiv0;
2987 k.teamAlgorithm = teamAlgorithm ? 1 : 0;
2990 Kokkos::parallel_for(
2991 "peclet::flow::mg_bottom_factor",
3003 k.noMeanForTest =
noMean ? 1 : 0;
3009 Kokkos::parallel_for(
"peclet::flow::mg_bottom_direct",
3010 Kokkos::TeamPolicy<CCExec>(
CCExec(), 1, T), k);
3017 const bool first = dirTeam_ == 0;
3022 auto h = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), dir_.
stat);
3023 printf(
"[mg] direct bottom factor: ok %d, restarts %d (team %d)\n", h(0), h(1),
3031 "[mg] direct bottom: %dx%dx%d, slow axis %d, P = %d, b = %d, border %d; team sizes "
3032 "factor %d, solve %d\n",
3033 lv_.back().inner.x, lv_.back().inner.y, lv_.back().inner.z, dirPlanes_.
s, dirPlanes_.
P,
3034 dirPlanes_.
b, dirPlanes_.
border, dirFacTeam_, dirTeam_);
3037 bottomFlagPending_ =
true;
3040 Kokkos::deep_copy(it, Kokkos::subview(bottomInfo_, 0));
3041 printf(
"[mg] direct bottom: %d inner iterations (team %d)\n", it, dirTeam_);
3050 int gbx = gnxF_,
gby = gnyF_,
3052 for (
int L = 0; L + 1 < (int)lv_.size(); ++L) {
3053 gbx /= lv_[L].ratio.x;
3054 gby /= lv_[L].ratio.y;
3055 gbz /= lv_[L].ratio.z;
3058 const int nx = lv.inner.x, ny = lv.inner.y, nz = lv.inner.z, ex = lv.ext.x, ey = lv.ext.y;
3060 auto h = Kokkos::create_mirror_view(v);
3061 Kokkos::deep_copy(h, v);
3068 std::vector<std::uint8_t>
lsolid;
3069 amgGlobalOfLocal_.clear();
3070 const int band[6][3] = {{-1, 0, 0}, {1, 0, 0}, {0, -1, 0}, {0, 1, 0}, {0, 0, -1}, {0, 0, 1}};
3072 for (
int k = 0; k < nz; ++k)
3073 for (
int j = 0; j < ny; ++j)
3074 for (
int i = 0; i < nx; ++i) {
3075 const long p = (
long)(i + g) + (
long)(j + g) * ex + (
long)(k + g) * ex * ey;
3076 const int gx = lv.og.x + i,
gy = lv.og.y + j,
gz = lv.og.z + k;
3078 amgGlobalOfLocal_.push_back(
gid);
3084 lsolid.push_back(
dc == 0.0 ? 1 : 0);
3089 for (
int d = 0; d < 6; ++d) {
3097 const int rx =
gx + band[d][0],
ry =
gy + band[d][1],
rz =
gz + band[d][2];
3098 const int axis = d / 2;
3109 lval.push_back(bc[d]);
3117#ifdef PECLET_FLOW_MPI
3127 amgSolid_.assign((std::size_t)amgGlobalN_, 0);
3128 for (std::size_t r = 0; r <
ggid.size(); ++r)
3133 std::vector<int>
parent((std::size_t)amgGlobalN_);
3134 for (
int i = 0; i < amgGlobalN_; ++i)
3135 parent[(std::size_t)i] = i;
3136 auto find = [&](
int a) {
3137 while (
parent[(std::size_t)a] != a)
3141 for (std::size_t e = 0; e <
grow.size(); ++e) {
3146 amgComp_.assign((std::size_t)amgGlobalN_, -1);
3147 std::vector<int>
remap((std::size_t)amgGlobalN_, -1);
3149 for (
int i = 0; i < amgGlobalN_; ++i) {
3150 if (amgSolid_[(std::size_t)i])
3152 const int r =
find(i);
3153 if (
remap[(std::size_t)r] < 0)
3154 remap[(std::size_t)r] = amgNComp_++;
3155 amgComp_[(std::size_t)i] =
remap[(std::size_t)r];
3159 std::vector<long>
csize((std::size_t)amgNComp_, 0);
3160 for (
int i = 0; i < amgGlobalN_; ++i)
3161 if (amgComp_[(std::size_t)i] >= 0)
3162 ++
csize[(std::size_t)amgComp_[(std::size_t)i]];
3163 printf(
"[agmg-build] fluid components=%d sizes:", amgNComp_);
3164 for (
int c = 0; c < std::min(amgNComp_, 12); ++c)
3166 printf(amgNComp_ > 12 ?
" ...\n" :
"\n");
3171 for (std::size_t r = 0; r <
ggid.size(); ++r) {
3176 const double ad = std::fabs(
gdiag[r]);
3183 "[agmg-build] n=%d solid=%ld fluid=%ld fluid|diag| min=%.3e max=%.3e "
3184 "tiny<1e-30=%ld <1e-12=%ld\n",
3191 std::vector<double>
rowsum((std::size_t)amgGlobalN_, 0.0);
3192 for (std::size_t r = 0; r <
ggid.size(); ++r)
3194 for (std::size_t e = 0; e <
grow.size(); ++e)
3195 if (!amgSolid_[(std::size_t)
grow[e]])
3198 for (std::size_t r = 0; r <
ggid.size(); ++r)
3200 const double d = std::fabs(
rowsum[(std::size_t)
ggid[r]]);
3208 peclet::core::solver::HostCsrOp A;
3210 A.diag.assign((std::size_t)amgGlobalN_, 0.0);
3211 for (std::size_t r = 0; r <
ggid.size(); ++r)
3212 A.diag[(std::size_t)
ggid[r]] =
gdiag[r];
3213 std::vector<std::vector<std::pair<int, double>>> rows((std::size_t)amgGlobalN_);
3214 for (std::size_t e = 0; e <
grow.size(); ++e)
3215 rows[(std::size_t)
grow[e]].push_back({
gcol[e], gval[e]});
3216 A.start.assign((std::size_t)amgGlobalN_ + 1, 0);
3217 for (
int r = 0; r < amgGlobalN_; ++r)
3218 A.start[(std::size_t)r + 1] = A.start[(std::size_t)r] + (
long)rows[(std::size_t)r].size();
3219 A.nbr.reserve(
grow.size());
3220 A.coef.reserve(
grow.size());
3221 for (
int r = 0; r < amgGlobalN_; ++r)
3222 for (
auto& [c, v] : rows[(std::size_t)r]) {
3224 A.coef.push_back(v);
3233 for (
int r = 0; r < amgGlobalN_; ++r)
3234 if (!amgSolid_[(std::size_t)r] && !rows[(std::size_t)r].empty()) {
3236 for (
auto& [c, v] : rows[(std::size_t)r])
3238 A.diag[(std::size_t)r] = -s;
3241 amg_ = std::make_shared<peclet::core::solver::GraphAMG>();
3250 if (!amg_ && !distributed_)
3252#ifdef PECLET_FLOW_MPI
3253 if (distributed_ && amgGlobalN_ == 0)
3256 const int nx = lv.inner.x, ny = lv.inner.y, nz = lv.inner.z, ex = lv.ext.x, ey = lv.ext.y;
3258 auto hrhs = Kokkos::create_mirror_view(lv.rhs);
3259 Kokkos::deep_copy(
hrhs, lv.rhs);
3260 std::vector<double>
lb;
3261 lb.reserve(amgGlobalOfLocal_.size());
3262 for (
int k = 0; k < nz; ++k)
3263 for (
int j = 0; j < ny; ++j)
3264 for (
int i = 0; i < nx; ++i)
3265 lb.push_back((
double)
hrhs((
long)(i + g) + (
long)(j + g) * ex + (
long)(k + g) * ex * ey));
3267 std::vector<double> z((std::size_t)std::max(amgGlobalN_, 1), 0.0);
3268#ifdef PECLET_FLOW_MPI
3270 std::vector<int>
ggid;
3271 std::vector<double>
gb;
3274 std::vector<double> b((std::size_t)amgGlobalN_, 0.0);
3275 for (std::size_t r = 0; r <
ggid.size(); ++r)
3276 b[(std::size_t)
ggid[r]] =
gb[r];
3281 std::vector<double> b(
lb.begin(),
lb.end());
3285 auto hx = Kokkos::create_mirror_view(lv.x);
3286 Kokkos::deep_copy(
hx, 0.0);
3288 for (
int k = 0; k < nz; ++k)
3289 for (
int j = 0; j < ny; ++j)
3290 for (
int i = 0; i < nx; ++i)
3291 hx((
long)(i + g) + (
long)(j + g) * ex + (
long)(k + g) * ex * ey) =
3292 z[(std::size_t)amgGlobalOfLocal_[c++]];
3293 Kokkos::deep_copy(lv.x,
hx);
3294 if (agmgDebug() && !distributed_) {
3301 FPC(lv.AFY),
FPC(lv.AFZ), lv.ext, lv.g);
3302 const double rn =
maxabs(lv, lv.res);
3303 auto hb = Kokkos::create_mirror_view(lv.rhs);
3304 Kokkos::deep_copy(hb, lv.rhs);
3306 for (std::size_t i = 0; i < hb.size(); ++i)
3307 bn = std::max(
bn, std::fabs((
double)hb(i)));
3308 printf(
"[agmg] vcycle-op residual of CSR solution: max|b-Ax|=%.3e max|b|=%.3e rel=%.3e\n",
3319 void pcgAmg(std::vector<double>& b, std::vector<double>& x) {
3320 const std::size_t n = (std::size_t)amgGlobalN_;
3321 const int dbg = agmgDebug();
3325 double sf = 0,
sa = 0;
3326 for (std::size_t i = 0; i < n; ++i) {
3328 if (i < amgSolid_.size() && amgSolid_[i])
3340 if (
dbg && amgNComp_ > 1) {
3341 std::vector<double> cs((std::size_t)amgNComp_, 0.0);
3342 for (std::size_t i = 0; i < n; ++i)
3343 if (amgComp_[i] >= 0)
3344 cs[(std::size_t)amgComp_[i]] += b[i];
3349 auto meanZero = [&](std::vector<double>& v) {
3361 std::vector<double> m((std::size_t)amgNComp_, 0.0);
3362 std::vector<long> cnt((std::size_t)amgNComp_, 0);
3363 for (std::size_t i = 0; i < n; ++i)
3364 if (amgComp_[i] >= 0) {
3365 m[(std::size_t)amgComp_[i]] += v[i];
3366 ++cnt[(std::size_t)amgComp_[i]];
3368 for (std::size_t c = 0; c < m.size(); ++c)
3369 m[c] = cnt[c] ? m[c] / (
double)cnt[c] : 0.0;
3370 for (std::size_t i = 0; i < n; ++i)
3371 if (amgComp_[i] >= 0)
3372 v[i] -= m[(std::size_t)amgComp_[i]];
3376 std::vector<double> r = b, z(n), p(n),
Ap(n);
3380 auto dot = [&](
const std::vector<double>& a,
const std::vector<double>& c) {
3382 for (std::size_t i = 0; i < n; ++i)
3386 double rz =
dot(r, z), r0 = std::sqrt(
dot(r, r));
3404 const double a =
rz /
pAp;
3405 for (std::size_t i = 0; i < n; ++i) {
3409 if (std::sqrt(
dot(r, r)) <= 1e-8 * r0)
3413 const double rzn =
dot(r, z);
3414 const double beta =
rzn /
rz;
3416 for (std::size_t i = 0; i < n; ++i)
3417 p[i] = z[i] + beta * p[i];
3422 for (std::size_t i = 0; i < n; ++i)
3423 if (i < amgSolid_.size() && amgSolid_[i])
3427 const double rn = std::sqrt(
dot(r, r));
3431 "[agmg] call=%ld iters=%d relres=%.2e |b|sol=%.3e |b|fl=%.3e "
3432 "mean(b) fl=%.3e all=%.3e compat=%.2e |x|sol=%.3e |x|fl=%.3e%s\n",
3439#ifdef PECLET_FLOW_MPI
3447 const int lbytes = (int)(
local.size() *
sizeof(T));
3448 std::vector<int> bc(size),
bd(size, 0);
3451 for (
int r = 0; r < size; ++r) {
3455 all.resize((std::size_t)tot /
sizeof(T));
3456 MPI_Allgatherv(local.data(), lbytes, MPI_BYTE, all.data(), bc.data(), bd.data(), MPI_BYTE, c);
3465 const bool ex = exactResidual_;
3466#ifdef PECLET_FLOW_MPI
3468 const C3 lo{
G + 1,
G + 1,
G + 1};
3469 const C3 hi{
l0.ext.x -
G - 1,
l0.ext.y -
G - 1,
l0.ext.z -
G - 1};
3470 l0.dev->exchangeBegin(v);
3473 l0.ext, lo, hi,
C3{0, 0, 0},
C3{0, 0, 0}, gfx_, gfy_, gfz_);
3476 l0.ext, lo, hi,
C3{0, 0, 0},
C3{0, 0, 0});
3477 l0.dev->exchangeEnd(v);
3481 l0.ext,
C3{G, G, G},
C3{l0.ext.x - G, l0.ext.y - G, l0.ext.z - G},
3482 lo, hi, gfx_, gfy_, gfz_);
3485 l0.ext,
C3{G, G, G},
C3{l0.ext.x - G, l0.ext.y - G, l0.ext.z - G}, lo,
3493 l0.ext,
l0.inner,
G, gfx_, gfy_, gfz_);
3496 l0.ext,
l0.inner,
G);
3511#ifdef PECLET_FLOW_MPI
3513 lv.dev->exchange(f);
3517 if (lv.inner.x < lv.g || lv.inner.y < lv.g || lv.inner.z < lv.g) {
3537 const C3 e = lv.ext;
3539 const int nx = lv.inner.x, ny = lv.inner.y, nz = lv.inner.z;
3540 const long exy = (
long)e.x * e.y;
3543 const long nZ = 2L *
G *
exy,
nY = (
long)nz * 2 *
G * e.x,
nX = (
long)nz * ny * 2 *
G;
3544 if constexpr (std::is_same_v<typename CCExec::memory_space, Kokkos::HostSpace>) {
3551 const long rZ = 2L *
G * e.y,
rY = (
long)nz * 2 *
G,
rX = (
long)nz * ny;
3553 auto copyRow = [=](
long dst,
long src) {
3554 for (
int x = 0; x <
G; ++x)
3555 ff(dst + x) =
ff(src + x + nx);
3556 for (
int x =
G; x <
G + nx; ++x)
3557 ff(dst + x) =
ff(src + x);
3558 for (
int x =
G + nx; x < e.x; ++x)
3559 ff(dst + x) =
ff(src + x - nx);
3561 auto row = [=](
long t) {
3563 const int l = (int)(t / e.y), y = (int)(t - (
long)l * e.y);
3564 const int z = l <
G ? l : nz + l;
3565 const int sy = y <
G ? y + ny : (y >=
G + ny ? y - ny : y);
3566 const int sz = z <
G ? z + nz : z - nz;
3567 copyRow((
long)y * e.x + (
long)z *
exy, (
long)sy * e.x + (
long)sz *
exy);
3568 }
else if (t <
rZ +
rY) {
3569 const long t2 = t -
rZ;
3570 const int zi = (int)(
t2 / (2 *
G)), l = (
int)(
t2 - (
long)
zi * 2 *
G);
3571 const int y = l <
G ? l : ny + l, z =
G +
zi;
3572 const int sy = y <
G ? y + ny : y - ny;
3573 copyRow((
long)y * e.x + (
long)z *
exy, (
long)sy * e.x + (
long)z *
exy);
3575 const long t3 = t -
rZ -
rY;
3576 const int zi = (int)(
t3 / ny),
yi = (int)(
t3 - (
long)
zi * ny);
3578 for (
int x = 0; x <
G; ++x)
3579 ff(base + x) =
ff(base + x + nx);
3580 for (
int x =
G + nx; x < e.x; ++x)
3581 ff(base + x) =
ff(base + x - nx);
3585 for (
long t = 0; t <
rZ +
rY +
rX; ++t)
3589 Kokkos::parallel_for(
"peclet::flow::mg_pfill3",
3590 Kokkos::RangePolicy<CCExec>(
space, 0,
rZ +
rY +
rX), row);
3594 Kokkos::parallel_for(
3595 "peclet::flow::mg_pfill3", Kokkos::RangePolicy<CCExec>(
space, 0,
nZ +
nY +
nX),
3599 const int l = (int)(t /
exy);
3600 const long r = t - (
long)l *
exy;
3603 z = l <
G ? l : nz + l;
3604 }
else if (t <
nZ +
nY) {
3605 const long t2 = t -
nZ, row = 2L *
G * e.x;
3606 const long zi =
t2 / row, r =
t2 -
zi * row;
3607 const int l = (int)(r / e.x);
3609 y = l <
G ? l : ny + l;
3612 const long t3 = t -
nZ -
nY, row = (
long)ny * 2 *
G;
3613 const long zi =
t3 / row, r =
t3 -
zi * row;
3614 const int l = (int)(r % (2 *
G));
3615 x = l <
G ? l : nx + l;
3616 y =
G + (int)(r / (2 *
G));
3619 const int sx = x <
G ? x + nx : (x >=
G + nx ? x - nx : x);
3620 const int sy = y <
G ? y + ny : (y >=
G + ny ? y - ny : y);
3621 const int sz = z <
G ? z + nz : (z >=
G + nz ? z - nz : z);
3622 ff((
long)x + (
long)y * e.x + (
long)z *
exy) =
3623 ff((
long)
sx + (
long)sy * e.x + (
long)sz *
exy);
3635 int N3[3] = {lv.inner.x, lv.inner.y, lv.inner.z};
3636 int dims[3] = {e.x, e.y, e.z};
3637 long st[3] = {1, e.x, (
long)e.x * e.y};
3638 const int a = axis, b = (axis + 1) % 3, c = (axis + 2) % 3;
3639 const long sa = st[a],
sb = st[b], sc = st[c];
3640 const int N =
N3[a];
3642 Kokkos::parallel_for(
3645 const long base = (
long)p0 *
sb + (
long)
p1 * sc;
3646 for (
int gl = 0;
gl <
G; ++
gl) {
3652#ifdef PECLET_FLOW_MPI
3659 Kokkos::parallel_for(
3661 KOKKOS_LAMBDA(
int x,
int y,
int z) {
3662 dst((
long)(x + 2) + (
long)(y + 2) * ext2.
x + (
long)(z + 2) * (
long)ext2.
x * ext2.
y) =
3663 src((
long)(x +
G) + (
long)(y +
G) * e1.
x + (
long)(z +
G) * (
long)e1.
x * e1.
y);
3670 const C3 e1 = l0.ext;
3671 const C3 ext2{e1.
x + 2, e1.y + 2, e1.z + 2};
3673 Kokkos::parallel_for(
3674 "peclet::flow::gp_unstage_g2", MDRange3<CCExec>(space, {0, 0, 0}, {e1.x, e1.y, e1.z}),
3675 KOKKOS_LAMBDA(
int x,
int y,
int z) {
3676 dst((
long)x + (
long)y * e1.x + (
long)z * (
long)e1.x * e1.y) =
3677 src((
long)(x + 1) + (
long)(y + 1) * ext2.
x + (
long)(z + 1) * (
long)ext2.
x * ext2.
y);
3684 std::size_t n = y.extent(0);
3685 Kokkos::parallel_for(
3686 "mgaxpy", Kokkos::RangePolicy<CCExec>(
space, 0, n),
3692 std::size_t n = y.extent(0);
3693 Kokkos::parallel_for(
3694 "mgaypx", Kokkos::RangePolicy<CCExec>(
space, 0, n),
3700 std::size_t n = y.extent(0);
3701 Kokkos::parallel_for(
3702 "mgscale", Kokkos::RangePolicy<CCExec>(
space, 0, n),
3708 std::size_t n = out.extent(0);
3709 Kokkos::parallel_for(
3710 "mglin", Kokkos::RangePolicy<CCExec>(
space, 0, n),
3718 std::size_t n = f.extent(0);
3719 Kokkos::parallel_for(
3720 "mgmasksolid", Kokkos::RangePolicy<CCExec>(
space, 0, n),
KOKKOS_LAMBDA(std::size_t i) {
3721 if (!(
ac(i) > 1e-30f))
3732 if (v.extent(0) != n)
3735 Kokkos::deep_copy(
CCExec(), v, 0.0);
3748 return eigWarm_ && !lv_.empty() && eigVmax_.extent(0) == lv_[0].n &&
3749 eigVmin_.extent(0) == lv_[0].n;
3767 solveFailed_ =
false;
3772 const std::size_t n =
l0.n;
3787 double nr = std::sqrt(
dot(
l0, x, x));
3793 Kokkos::deep_copy(
CCExec(), x, src);
3799 if (dst.extent(0) != n)
3801 Kokkos::deep_copy(
CCExec(), dst, x);
3805 for (
int k = 0; k < iters; ++k) {
3808 Kokkos::deep_copy(
CCExec(), v, z);
3812 keepf(eigVmax_, v,
"ev_vmax");
3815 for (
int k = 0; k < iters; ++k) {
3819 Kokkos::deep_copy(
CCExec(), v, z);
3823 keepf(eigVmin_, v,
"ev_vmin");
3828 nrMax > 0.0 &&
nrMin > 0.0 && std::isfinite(
lmax) &&
lmax > 0.0 && std::isfinite(mu);
3845 double a,
double bnd,
bool guard3 =
false) {
3846 solveFailed_ =
false;
3847 chebGuardTripped_ =
false;
3852 const std::size_t n =
l0.n;
3864 const double theta = 0.5 * (
bnd + a), delta = 0.5 * (
bnd - a),
sigma1 = theta / delta;
3865 double rho = 1.0 /
sigma1;
3867 Kokkos::deep_copy(
CCExec(), r, b);
3874 const double rref = stopRef_ > 0.0 ? stopRef_ : r0;
3879 lin(d, 1.0 / theta, z, 0.0, z);
3881 for (
int i = 1; i < maxit; ++i) {
3888 if (
guard3 && i == 3 && !(
rn <= r0)) {
3889 chebGuardTripped_ =
true;
3912 Kokkos::deep_copy(
scratch, b);
3920 Kokkos::deep_copy(r, b);
3934 "mgdot",
C3{g, g, g},
C3{e.x - g, e.y - g, e.z - g},
3936 const long i = (
long)x + (
long)y * e.x + (
long)z * (
long)e.x * e.y;
3938 acc +=
aa(i) *
bb(i);
3941#ifdef PECLET_FLOW_MPI
3942 return allreduce(s, MPI_SUM_, lv.comm);
3944 return allreduce(s, MPI_SUM_);
3955 "mgmax",
C3{g, g, g},
C3{e.x - g, e.y - g, e.z - g},
3957 const long i = (
long)x + (
long)y * e.x + (
long)z * (
long)e.x * e.y;
3958 if (
ac(i) > 1e-30f) {
3959 const double v = Kokkos::fabs(
aa(i));
3964 Kokkos::Max<double>(m));
3965#ifdef PECLET_FLOW_MPI
3966 return allreduce(m, MPI_MAX_, lv.comm);
3968 return allreduce(m, MPI_MAX_);
3985 auto body =
KOKKOS_LAMBDA(
int x,
int y,
int z,
double& s,
long& k) {
3986 const long i = (
long)x + (
long)y * e.x + (
long)z * (
long)e.x * e.y;
3987 if (
ac(i) > 1e-30f) {
3992 if (!distributed_) {
3994 ccReduce3(
"mgmeanr",
C3{g, g, g},
C3{e.x - g, e.y - g, e.z - g}, body,
3995 Kokkos::Sum<double, CCMem>(
slot(
kMsum)), Kokkos::Sum<long, CCMem>(
kcnt_));
4000 "mgmeans",
C3{g, g, g},
C3{e.x - g, e.y - g, e.z - g},
4002 const long cnt = kc();
4006 const long i = (
long)x + (
long)y * e.x + (
long)z * (
long)e.x * e.y;
4013 ccReduce3(
"mgmeanr",
C3{g, g, g},
C3{e.x - g, e.y - g, e.z - g}, body, sum, cnt);
4015 allreduceSum2(sum,
dcnt
4026 "mgmeans",
C3{g, g, g},
C3{e.x - g, e.y - g, e.z - g},
KOKKOS_LAMBDA(
int x,
int y,
int z) {
4027 const long i = (
long)x + (
long)y * e.x + (
long)z * (
long)e.x * e.y;
4052 if (bottomFlagPending_) {
4057 return solveFailed_;
4060 allreduceTime_ = 0.0;
4061 allreduceCount_ = 0;
4065 enum AllOp { kSum, kMax };
4066#ifdef PECLET_FLOW_MPI
4067 using MPI_Comm_or_void = MPI_Comm;
4068 static MPI_Comm nullptr_comm() {
return MPI_COMM_NULL; }
4070 using MPI_Comm_or_void =
void*;
4071 static void* nullptr_comm() {
return nullptr; }
4075 double allreduce(
double v, AllOp op, MPI_Comm_or_void c = nullptr_comm()) {
4076#ifdef PECLET_FLOW_MPI
4078 const auto t0 = std::chrono::steady_clock::now();
4080 MPI_Allreduce(&v, &g, 1, MPI_DOUBLE, op == kSum ? MPI_SUM : MPI_MAX,
4081 c == nullptr_comm() ? comm_ :
c);
4083 std::chrono::duration<double>(std::chrono::steady_clock::now() - t0).count();
4093 void allreduceSum2(
double& a,
double& b, MPI_Comm_or_void c = nullptr_comm()) {
4094#ifdef PECLET_FLOW_MPI
4096 const auto t0 = std::chrono::steady_clock::now();
4097 double v[2] = {
a,
b},
g[2] = {0.0, 0.0};
4098 MPI_Allreduce(v, g, 2, MPI_DOUBLE, MPI_SUM, c == nullptr_comm() ? comm_ :
c);
4100 std::chrono::duration<double>(std::chrono::steady_clock::now() - t0).count();
4107 static constexpr AllOp MPI_SUM_ = kSum, MPI_MAX_ = kMax;
4109 std::vector<Level> lv_;
4114 bool eigWarm_ =
false;
4115 bool chebGuardTripped_ =
false;
4119 double hp_[3] = {1.0, 1.0, 1.0};
4120 bool aniso_ =
false;
4121 int pre_ = 2, post_ = 2, bottom_ = 4;
4122 int bc_[6] = {0, 0, 0, 0, 0, 0};
4123 bool hasBC_ =
false, removeMean_ =
true, hasOutflow_ =
false;
4128 bool outflowCoeff_ =
false;
4133 bool meanRemovalAll_ =
false;
4134 bool distributed_ =
false;
4135 bool overlaySolve_ =
false;
4136 int dbgBrkWhich_ = 0, dbgBrkIter_ = -1;
4141 bool solveFailed_ =
false;
4145 CCField bottomR_, bottomP_, bottomZ_, bottomZp_, bottomAp_;
4146 Kokkos::View<int*, CCMem> bottomInfo_;
4147 bool bottomConnKnown_ =
false, bottomConnected_ =
false;
4148 int bottomNComp_ = 0;
4149 Kokkos::View<int*, CCMem> bottomComp_, bottomLabTmp_;
4150 Kokkos::View<long*, CCMem> bottomCompCnt_;
4152 bool bottomFlagPending_ =
false;
4153 int bottomSolver_ = 0;
4158 bool bottomStore_ =
false;
4159 BottomPlanes dirPlanes_;
4160 BottomDirect<float> dir_;
4161 Kokkos::View<int*, CCMem> dirKc_;
4162 Kokkos::View<double*, CCMem> dirAug_;
4163 bool facStale_ =
true;
4164 int dirTeam_ = 0, dirFacTeam_ = 0;
4166 double stopRef_ = -1.0;
4167 std::vector<double> lvTime_;
4174 bool bcGhost_ =
true;
4178 double aspectTheta_ = 2.0;
4186 bool strictPressure_ =
false;
4189 bool exactResidual_ =
false;
4192 int agglomExtent_ = 4;
4193 double allreduceTime_ = 0.0;
4194 long allreduceCount_ = 0;
4203 int agglomMode_ = 0;
4208 bool telescope_ =
true;
4209 int teleForce_ = -1;
4210 bool teleActive_ =
true;
4215 int teleMinExtent_ = 4;
4220 bool repartition_ =
false;
4221 int gnxF_ = 0, gnyF_ = 0, gnzF_ = 0;
4224 double gfx_ = 1.0, gfy_ = 1.0, gfz_ = 1.0;
4225 mutable std::shared_ptr<peclet::core::solver::GraphAMG>
4227 mutable peclet::core::solver::HostCsrOp
4229 mutable std::vector<int> amgOwnerCount_;
4230 mutable std::vector<int>
4232 mutable int amgGlobalN_ = 0;
4234 mutable std::vector<std::uint8_t> amgSolid_;
4235 mutable std::vector<int> amgComp_;
4236 mutable int amgNComp_ = 0;
4237 mutable long agmgCalls_ = 0;
4238 static int agmgDebug() {
4239 static const int v = [] {
4240 const char*
e = std::getenv(
"PECLET_FLOW_AGMG_DEBUG");
4241 return e ? std::atoi(e) : 0;
4245#ifdef PECLET_FLOW_MPI
4246 MPI_Comm comm_ = MPI_COMM_NULL;
4281#ifdef PECLET_FLOW_MPI
4282 for (
const Level& l : lv_)
4283 n += l.tele ? 1 : 0;
4290#ifdef PECLET_FLOW_MPI
4291 for (
const Level& l : lv_)
4292 n += (l.tele && l.tele->repartition()) ? 1 : 0;
4297 C3 g{gnxF_, gnyF_, gnzF_};
4298 for (std::size_t L = 0; L + 1 < lv_.size(); ++L)
4299 g =
C3{g.x / lv_[L].ratio.x, g.y / lv_[L].ratio.y, g.z / lv_[L].ratio.z};
4302#ifdef PECLET_FLOW_MPI
4317 bool repartition =
false;
4333 static std::vector<PlanRow> predict(
int gnx,
int gny,
int gnz,
int np,
int nLevels,
4334 bool telescope,
int minExtent = 4,
int decompLevels = 0,
4335 double maxImbalance = 1.05,
4336 const std::vector<peclet::core::Real>*
weights =
nullptr,
4337 int* align =
nullptr) {
4338 using Dec = peclet::core::decomp::BlockDecomposer<3>;
4339 std::vector<PlanRow> rows;
4340 auto can = [](
int d) {
return (d % 2 == 0) && (d / 2 >= 2); };
4341 auto evenOn = [](
const Dec& d,
int ax) {
4342 for (std::size_t b = 0; b < d.sizes().size(); ++b)
4343 if ((d.origins()[b][ax] % 2) || (d.sizes()[b][ax] % 2))
4348 peclet::core::Index maxBlockCells = 0;
4350 if (
weights->size() != (std::size_t)gnx * (std::size_t)gny * (std::size_t)gnz)
4351 throw std::invalid_argument(
4352 "predict: weights must hold one value per global cell (gnx*gny*gnz, x-fastest)");
4353 auto c = peclet::core::decomp::chooseAlignedWeighted(
4354 static_cast<std::size_t
>(np), peclet::core::IVec<3>{gnx, gny, gnz}, *
weights);
4355 cur = std::move(
c.dec);
4356 maxBlockCells = peclet::core::decomp::largestBlockCells(cur);
4360 cur = decomposition(
static_cast<std::size_t
>(np), gnx, gny, gnz, decompLevels, maxImbalance);
4364 C3
gs{gnx, gny, gnz};
4368 r.ranks = (
int)cur.numBlocks();
4369 r.block = C3{(
int)cur.sizes()[0][0], (
int)cur.sizes()[0][1], (
int)cur.sizes()[0][2]};
4371 r.ratio = C3{1, 1, 1};
4374 const bool canAny = can(
gs.x) || can(
gs.y) || can(
gs.z);
4375 auto liftable = [&](
const Dec&
d) {
return teleLiftable(d, gs); };
4376 const bool blocked = !liftable(cur);
4377 const bool tooSmall = minExtent > 0 && peclet::core::decomp::minBlockExtent(cur) <
4378 (peclet::core::Index)minExtent;
4381 if (
telescope && (blocked || tooSmall) && canAny && cur.numBlocks() > 1) {
4382 auto t = peclet::core::decomp::chooseStageTarget(
4383 cur, peclet::core::IVec<3>{
gs.x,
gs.y,
gs.z}, liftable, minExtent, maxBlockCells);
4384 if (
t.kind == peclet::core::decomp::StageKind::SiblingMerge ||
4385 t.kind == peclet::core::decomp::StageKind::Repartition) {
4386 r.repartition =
t.kind == peclet::core::decomp::StageKind::Repartition;
4387 cur = std::move(
t.dec);
4391 if (can(
gs.x) && evenOn(cur, 0)) {
4395 if (can(
gs.y) && evenOn(cur, 1)) {
4399 if (can(
gs.z) && evenOn(cur, 2)) {
4405 if (next.x ==
gs.x && next.y ==
gs.y && next.z ==
gs.z)
4408 cur = cur.coarsened(peclet::core::IVec<3>{
r.ratio.x,
r.ratio.y,
r.ratio.z});
4415 std::vector<PlanRow> builtPlan()
const {
4416 std::vector<PlanRow>
rows;
4417 for (
const Level& l : lv_) {
4423 if (l.comm != MPI_COMM_NULL)
4424 MPI_Comm_size(l.comm, &
r.ranks);
4425 r.tele = (bool)l.tele;
4426 r.repartition = l.tele && l.tele->repartition();
4443 agglomMode_ =
on ? 1 : 0;
4459 if (!bottomStore_) {
4474 int*
Tused =
nullptr,
bool teamAlgorithm =
false) {
4492 Kokkos::deep_copy(it, Kokkos::subview(bottomInfo_, 0));
4501 bottomFlagPending_ =
true;
4509 Kokkos::deep_copy(it, Kokkos::subview(bottomInfo_, 0));
4513 auto h = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), dir_.stat);
CCField eigenWarmMax() const
int solveBiCGStab(CCField b, CCField x, CCField r, CCField rh, CCField p, CCField v, CCField t, CCField z, CCField z2, int maxit, double rtol, int pre, int post, int bottom, const GpOverlayReal< MReal > &ov, int nOv, C3 nn)
int launchDirectFactor(const BottomDirect< FR > &D, int team, double tauPiv0, bool probe=true, bool teamAlgorithm=false)
bool chebyshevGuardTripped() const
void applyNeumannGhost(const Level &lv, CCField x, int g=G)
void axpy(CCField y, double a, CCField x)
static int directTeam(const K &k, int team, bool probe, std::size_t scratch=0, int hostTeam=kBottomHostTeam)
double residualNorm(CCField b, CCField x, CCField r, CCField Ap)
const BottomPlanes & directPlanes() const
void applyOutflowGhost(const Level &lv, CCField x, int g=G)
void setStrictPressure(bool on)
Throw instead of reporting when the preconditioner returns a non-finite correction.
bool fusedWrapSmooth(const Level &lv) const
void removeMean(Level &lv, CCField f, bool stopGuard=false)
void scale(CCField y, double a)
void setDebugBreakdown(int which, int iter)
void directMarkStaleForTest()
int solvePCGResident(CCField b, CCField x, CCField r, CCField p, CCField z, CCField Ap, int maxit, double rtol, int pre, int post, int bottom, const StarOverlay *star, int nStar, C3 nnStar)
double readSlot(int k) const
void aypx(CCField y, double a, CCField x)
void fillAxis(Level &lv, CCField f, int axis)
int directSolveTeamForTest(int team, int *Tused=nullptr)
void fillOpenness(Level &lv)
int solvePCG(CCField b, CCField x, CCField r, CCField p, CCField z, CCField Ap, int maxit, double rtol, int pre, int post, int bottom, const StarOverlay *star=nullptr, int nStar=0, C3 nnStar=C3{0, 0, 0})
int directApplyForTest(const BottomDirect< FR > &D, CCField z, CCField r, int team, bool noMean=false)
int bottomComponents() const
Kokkos::View< double *, Kokkos::HostSpace > kpk_
void matvecOverlap(Level &l0, CCField y, CCField v)
const char * directBottomIneligible() const
void setOutflowCoefficient(bool on)
void resetAllreduceCounters()
void setMetric(const double hp[3])
Per-axis cell spacings h_a' = h_a / hRef of the physical domain (doc/anisotropic_metric....
bool eigenWarmReady() const
CCField workVector(int slot, std::size_t n, const char *label)
void setGraphAmgBottom(bool on)
static bool touchesGlobalFace(const Level &lv, int f)
std::vector< C3 > levelRatios() const
The per-level coarsening ratio actually chosen (the level table of §5, gate §8.5).
static C3 parityOg(const Level &lv)
void directPivotTolForTest(double t)
void vcycle(int L, bool sym)
static constexpr double kBrkPAp
Kokkos::View< double, CCMem > slot(int k) const
void setOpenness(CCConst ox, CCConst oy, CCConst oz, double idx2, double idy2, double idz2)
void pcgAmg(std::vector< double > &b, std::vector< double > &x)
void noteBottomFlag(double v)
void bottomForceForTest()
void setBoundaryConditions(const int bc[6])
long allreduceCount() const
double maxabs(Level &lv, CCField a)
void vcycleImpl(int L, bool sym)
void applyBoundaryOpenness(Level &lv, Level *fine=nullptr)
void dotTo(Level &lv, CCField a, CCField b, int k)
void setTelescope(bool on)
void setCoarseGhost(bool on)
Neumann coarse ghost on wall/inflow faces before the prolongation (see bcGhost_).
Kokkos::View< long, CCMem > kcnt_
void setAspectThreshold(double theta)
Anisotropic-coarsening aspect threshold theta (see aspectTheta_).
void setRepartition(bool on)
Repartition stages for a weighted level-0 decomposition (see repartition_); before initMpi.
void precondVcycle(CCField zz, CCField rr)
int solveChebyshev(CCField b, CCField x, int maxit, double rtol, int pre, int post, int bottom, double a, double bnd, bool guard3=false)
void maskSolid(Level &lv, CCField f)
int repartitionCount() const
void applyBoundaryOpennessFrom(Level &lv, const CCField *fo, C3 fext, int fg, C3 fratio)
static constexpr int kNSlots
bool exactResidual() const
double aspectThreshold() const
bool bottomConnected() const
void lin(CCField out, double a, CCField x, double b, CCField y)
CCField eigenWarmMin() const
int telescopeCount() const
double dot(Level &lv, CCField a, CCField b)
void fill(Level &lv, CCField f)
void setTelescopeMinExtent(int e)
void setStopReference(double ref)
void setSlot(int k, double v)
double allreduceSeconds() const
Kokkos::View< double *, CCMem > ks_
int agglomerationMode() const
void graphAmgSolveBottom(Level &lv)
static C3 mgChooseRatio(const double H[3], const bool canA[3], bool aniso, double theta)
int launchDirectKernel(const BottomDirect< FR > &D, bool precondOnly, int team, bool noMean, CCField zOut=CCField(), CCField rIn=CCField(), bool probe=true)
void estimateEigenvalues(CCConst seed, double &lmin, double &lmax, int iters, int pre, int post, int bottom, EigenStart start=EigenStart::Cold)
void setAgglomerationMode(int mode)
void smooth(Level &lv, int sweeps, bool reverse)
void setTelescopeForceLevel(int L)
int directRestarts() const
void setAgglomerationExtent(int cells)
bool outflowCoefficient() const
static constexpr bool kHostMemory
void fillWrap(Level &lv, CCField f)
bool strictPressure() const
void maxabsTo(Level &lv, CCField a, int k)
void saveOutflowPlanes(Level &lv)
C3 coarsestGlobal() const
void setExactResidual(bool on)
The exact double flux-form level-0 apply in the residual and the Krylov matvec.
void init(int nx, int ny, int nz, int nLevels)
void setEigenWarm(CCField vmax, CCField vmin)
bool caSmooth(const Level &lv) const
int solveFCG(CCField b, CCField x, CCField r, CCField p, CCField z, CCField zp, CCField Ap, int maxit, double rtol, int pre, int post, int bottom, const StarOverlay *star=nullptr, int nStar=0, C3 nnStar=C3{0, 0, 0})
void setMeanRemovalScope(bool all)
static constexpr int kPacket
bool fusedWrapReads() const
static constexpr double kBrkRz
double rhsNorm(CCField b, CCField scratch)
void evalBottomComponents()
int agglomerationExtent() const
void setCommAvoiding(int mask)
Communication-avoiding smoothing mask (kCaMomentum | kCaMg); only kCaMg is read here.
BottomDirect< FR > directFactorForTest(int team, double tauPiv0=kBottomPivotTol, int *Tused=nullptr, bool teamAlgorithm=false)
void setBottomSolver(int m)
bool agglomerateBottom() const
BottomKernel< FR > makeBottomKernel(bool precondOnly)
flow — directional ghost-cell IBM projection overlay (experimental second staggered IBM).
flow — portable (Kokkos) native per-face domain boundary conditions for the MAC grid.
#define PECLET_FLOW_OMP_SIMD
flow — portable (Kokkos) cut-cell pressure operator + Chorin projection.
flow — the device pressure bottom's direct preconditioner (doc/vof_step_performance_ design....
precond(levels, singular, nu)
constexpr int kBottomHostFactorTeam
double cutcellApplyExactCell(const XV &x, const OV &ox, const OV &oy, const OV &oz, long i, long sx, long sy, long sz, long xp, long xm, long yp, long ym, long zp, long zm, double gfx, double gfy, double gfz)
void mgSaveFacePlane(CCField dst, CCConst src, C3 e, int g, int a, int s)
void residualCutcellFace(CCField r, CCConst x, CCConst b, FPC AC, FPC AFX, FPC AFY, FPC AFZ, C3 e, int g)
void applyCutcellOpFaceWrap(CCField y, CCConst x, FPC AC, FPC AFX, FPC AFY, FPC AFZ, C3 e, C3 n, int g)
void applyCutcellOpFace(CCField y, CCConst x, OpV AC, OpV AFX, OpV AFY, OpV AFZ, C3 e, int g)
void mgRestoreFacePlane(CCField dst, CCConst src, C3 e, int g, int a, int s)
void bcSetOpenness(BField oa, B3 ext, int g, int a, int s, double val)
Kokkos::View< const MReal *, CCMem > FPC
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)
constexpr int kBottomHostTeam
void bcZeroPressureGhost(BField phi, B3 ext, int g, int a, int s)
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 prolongAddCell(const FV &fine, const CV &coarse, C3 fext, C3 cext, int gf, int gc, C3 ratio, int ifx, int ify, int ifz)
void coarsenOpenAvg(CCField oxc, CCField oyc, CCField ozc, CCConst oxf, CCConst oyf, CCConst ozf, C3 cext, C3 fext, int gc, int gf, C3 cinner, C3 ratio)
void ccReduce3(const char *name, C3 lo, C3 hi, F f, R &&... reducers)
void residualCutcell(CCField r, CCConst x, CCConst b, FPC AC, FPC AW, FPC AE, FPC AS, FPC AN, FPC AB, FPC AT, C3 e, int g)
void mgCoarsenFacePlane(CCField oc, CCConst of, C3 cext, C3 fext, int gc, int gf, C3 cinner, C3 ratio, int a, int s)
BottomPlanes bottomChoosePlanes(C3 inner, const int bc[6])
void applyCutcellOpExact(CCField y, CCConst x, CCConst ox, CCConst oy, CCConst oz, C3 e, int g, double gfx, double gfy, double gfz)
void gpApplyDelta(CCField y, CCConst x, const GpOverlayReal< Real > &ov, int nOv, C3 nn, C3 extY, int gbY, C3 extX, int gbX, bool useGhost=false)
Overlay matvec correction: y(r) = rho_r * (y(r) + closure-face phi terms), where y currently holds th...
void cutcellResidualCell(const RV &r, const XV &x, const BV &b, const OpV &AC, const OpV &AW, const OpV &AE, const OpV &AS, const OpV &AN, const OpV &AB, const OpV &AT, long i, long xp, long xm, long yp, long ym, long zp, long zm)
constexpr double kBottomTau
void cutcellSmoothColorBoxFace(CCField phi, CCConst b, OpV AC, OpV AFX, OpV AFY, OpV AFZ, C3 e, C3 og, int color, C3 rlo, C3 rhi, C3 slo, C3 shi)
void buildCutcellOpFace(OpV AC, OpV AFX, OpV AFY, OpV AFZ, CCConst ox, CCConst oy, CCConst oz, C3 e, int gb, double gfx, double gfy, double gfz)
void applyCutcellOpExactWrap(CCField y, CCConst x, CCConst ox, CCConst oy, CCConst oz, C3 e, C3 n, int g, double gfx, double gfy, double gfz)
void residualCutcellFaceWrap(CCField r, CCConst x, CCConst b, FPC AC, FPC AFX, FPC AFY, FPC AFZ, C3 e, C3 n, int g)
CcNbrs ccWrapNbrs(int lx, int ly, int lz, C3 n, int g, long i, long sy, long sz)
void cutcellSmoothColorFace(CCField phi, CCConst b, OpV AC, OpV AFX, OpV AFY, OpV AFZ, C3 e, C3 og, int g, int color)
void applyCutcellOpBoxFace(CCField y, CCConst x, OpV AC, OpV AFX, OpV AFY, OpV AFZ, C3 e, C3 rlo, C3 rhi, C3 slo, C3 shi)
void residualCutcellBox(CCField r, CCConst x, CCConst b, FPC AC, FPC AW, FPC AE, FPC AS, FPC AN, FPC AB, FPC AT, C3 e, C3 rlo, C3 rhi, C3 slo, C3 shi)
void ccFor3(const char *name, C3 lo, C3 hi, F f)
Kokkos::View< double *, CCMem > CCField
void meanSubtractCell(const FV &f, const AV &ac, long i, double mean)
constexpr long kBottomMaxCells
void restrictAvg(CCField coarse, CCConst fine, C3 cext, C3 fext, int gc, int gf, C3 cinner, C3 ratio)
constexpr int kBottomMaxComponents
Kokkos::View< MReal *, CCMem > FPV
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)
constexpr double kBottomPivotTol
Kokkos::DefaultExecutionSpace CCExec
void cutcellSmoothColorFaceWrap(CCField phi, CCConst b, FPC AC, FPC AFX, FPC AFY, FPC AFZ, C3 e, C3 n, C3 og, int g, int color)
void applyCutcellOpExactBox(CCField y, CCConst x, CCConst ox, CCConst oy, CCConst oz, C3 e, C3 rlo, C3 rhi, C3 slo, C3 shi, double gfx, double gfy, double gfz)
void residualCutcellBoxFace(CCField r, CCConst x, CCConst b, FPC AC, FPC AFX, FPC AFY, FPC AFZ, C3 e, C3 rlo, C3 rhi, C3 slo, C3 shi)
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 bcNeumannGhost(BField f, B3 ext, int g, int a, int s)
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
void starApplyDelta(CCField y, CCConst x, const StarOverlay &ov, int nOv, C3 nn, C3 extY, int gY, C3 extX, int gX, bool exact)
y += S_star x over the inner cells of the (extY, gY) block, x read from the (extX,...
flow — the multidimensional iteration policies: iteration order follows storage.
flow — Design B of the fluid-only collocated constraint (route 2b): Kron (star-mesh) elimination of s...
static BottomDirect allocate(const BottomPlanes &p, C3 ext)
Kokkos::View< int *, CCMem > stat
static std::size_t scratchBytes(int b)
static double teamMax(const Member &t, int n, const F &f)
static double teamSum(const Member &t, int n, const F &f)
void removeMeanBottom(const Member &t, const CCField &f) const
Kokkos::View< const int *, CCMem > comp
Kokkos::View< int *, CCMem > info
void operator()(const Member &t) const
void applyM(const Member &t, const CCField &R, const CCField &Z, const CCField *keep) const
static int nInner(const BottomLevel &l)
static constexpr bool kHostLane
double dot(const Member &t, const BottomLevel &l, const CCField &a, const CCField &c) const
BottomDirect< FacReal > dir
Kokkos::View< double *, CCMem > ks
Kokkos::View< double *, CCMem > cmean
Kokkos::TeamPolicy< CCExec >::member_type Member
Kokkos::View< const long *, CCMem > ccnt
static long cell(const BottomLevel &l, int q, int &lx, int &ly, int &lz)
static int nExt(const BottomLevel &l)
Kokkos::View< int *, CCMem > kc
Kokkos::View< long *, CCMem > cnt
Kokkos::TeamPolicy< CCExec >::member_type Member
Kokkos::View< double *, CCMem > aug
Kokkos::View< int *, CCMem > nc
void operator()(const Member &t) const
Kokkos::View< int *, CCMem > tmp
long cell(int q, int &lx, int &ly, int &lz) const
Kokkos::View< int *, CCMem > comp
int sweep(const Member &t, const Kokkos::View< int *, CCMem > &src, const Kokkos::View< int *, CCMem > &dst) const
OverlayScope(bool &f, bool on)
One entry per eliminated solid-centered cell: packed INNER flat index + the apertures of its (up to 6...
static constexpr double AC
Kokkos::View< double *, peclet::flow::CCMem > OpV
static constexpr double F
Kokkos::View< float *, IMem > FV
static std::vector< peclet::core::Real > weights(int kind, Grid3 g)
static double mean(const std::vector< double > &a)