14#ifndef PECLET_FLOW_MAC_CUTCELL_MG_HPP
15#define PECLET_FLOW_MAC_CUTCELL_MG_HPP
21#include <Kokkos_Core.hpp>
30#include "peclet/core/solver/graph_amg.hpp"
38#include "peclet/core/decomp/block_decomposer.hpp"
39#include "peclet/core/decomp/grid_redistribute.hpp"
40#include "peclet/core/halo/grid_halo.hpp"
41#include "peclet/core/halo/grid_halo_topology.hpp"
47using peclet::core::halo::GridHalo;
48using peclet::core::halo::GridHaloTopology;
52using FPV = Kokkos::View<MReal*, CCMem>;
53using FPC = Kokkos::View<const MReal*, CCMem>;
61 using MD = Kokkos::MDRangePolicy<CCExec, Kokkos::Rank<3>>;
65 const int rx = ratio.
x,
ry = ratio.
y,
rz = ratio.
z;
68 auto F = [&](
CCConst T,
int x,
int y,
int z) {
69 return T((
long)x + (
long)y *
fsy + (
long)z *
fsz);
71 double sx = 0,
sy = 0,
sz = 0;
72 for (
int a = 0; a <
ry; ++a)
73 for (
int b = 0; b <
rz; ++b)
75 for (
int a = 0; a <
rx; ++a)
76 for (
int b = 0; b <
rz; ++b)
78 for (
int a = 0; a <
rx; ++a)
79 for (
int b = 0; b <
ry; ++b)
93 "peclet::flow::cc_residual",
C3{g, g, g},
C3{e.x - g, e.y - g, e.z - g},
95 const long sx = 1,
sy = e.x,
sz = (
long)e.x * e.y;
115 const long sx = 1,
sy = e.x,
sz = (
long)e.x * e.y;
135 for (
int dz = 0;
dz < ratio.
z; ++
dz)
136 for (
int dy = 0;
dy < ratio.
y; ++
dy)
137 for (
int dx = 0;
dx < ratio.
x; ++
dx) {
154 const double cx = (ratio.
x == 2) ? 0.5 *
ifx - 0.25 + gc :
ifx + gc;
155 const double cy = (ratio.
y == 2) ? 0.5 *
ify - 0.25 + gc :
ify + gc;
156 const double cz = (ratio.
z == 2) ? 0.5 *
ifz - 0.25 + gc :
ifz + gc;
157 const double fxw = Kokkos::floor(
cx),
fyw = Kokkos::floor(
cy),
fzw = Kokkos::floor(
cz);
161 auto C = [&](
int xx,
int yy,
int zz) {
180 static const int lv = [] {
181 const char* e = std::getenv(
"PECLET_FLOW_MG_DEBUG");
182 return e ? std::atoi(e) : 0;
187 static const int n = [] {
188 const char* e = std::getenv(
"PECLET_FLOW_MG_DEBUG_SOLVES");
189 return e ? std::atoi(e) : 3;
203 static const int v = [] {
204 const char* e = std::getenv(
"PECLET_FLOW_CA");
207 const std::string s(e);
208 if (s ==
"mom" || s ==
"momentum")
229#ifdef PECLET_FLOW_MPI
230 std::shared_ptr<GridHaloTopology<3>> halo;
231 std::shared_ptr<GridHalo<double>> dev;
234 static constexpr int G = 1;
240 return C3{lv.
og.
x - lv.
g + 1, lv.
og.
y - lv.
g + 1, lv.
og.
z - lv.
g + 1};
251 C3 inner{nx, ny, nz}, cf{1, 1, 1};
255 v.
ext =
C3{inner.
x + 2 *
G, inner.y + 2 *
G, inner.z + 2 *
G};
258 auto can = [&](
int d) {
return (d % 2 == 0) && (d / 2 >= 2); };
264 next.x = inner.x / 2;
268 next.y = inner.y / 2;
272 next.z = inner.z / 2;
283 *p =
FPV(
"mg_A", v.
n);
285 if (
next.x == inner.x &&
next.y == inner.y &&
next.z == inner.z)
288 cf =
C3{cf.x * ratio.x, cf.y * ratio.y, cf.z * ratio.z};
291 printf(
"[mg] init %dx%dx%d single-rank -> %d levels (requested %d)\n", nx, ny, nz,
293 for (
int L = 0;
L < (int)lv_.size(); ++
L)
294 printf(
"[mg] L%d dims %4dx%4dx%4d ratio(%d,%d,%d)\n",
L, lv_[
L].inner.x, lv_[
L].inner.y,
295 lv_[
L].inner.z, lv_[
L].ratio.x, lv_[
L].ratio.y, lv_[
L].ratio.z);
299#ifdef PECLET_FLOW_MPI
318 auto can = [](
int d) {
return (d % 2 == 0) && (d / 2 >= 2); };
320 peclet::core::IVec<3> a{1, 1, 1};
321 for (
bool any =
true;
any;) {
323 if (
can(
gs.x)) {
a[0] *= 2;
gs.x /= 2;
any =
true; }
324 if (
can(
gs.y)) {
a[1] *= 2;
gs.y /= 2;
any =
true; }
325 if (
can(
gs.z)) {
a[2] *= 2;
gs.z /= 2;
any =
true; }
335 for (
int k = 0;
k < 3; ++
k)
351 const char*
e = std::getenv(
"PECLET_FLOW_DECOMP_LEVELS");
352 return e ? std::atoi(e) : 0;
362 auto can = [](
int d) {
return (d % 2 == 0) && (
d / 2 >= 2); };
364 peclet::core::IVec<3>
r{1, 1, 1};
366 if (
can(
gs.x)) {
r[0] *= 2;
gs.x /= 2; }
367 if (
can(
gs.y)) {
r[1] *= 2;
gs.y /= 2; }
368 if (
can(
gs.z)) {
r[2] *= 2;
gs.z /= 2; }
379 return peclet::core::decomp::BlockDecomposer<3>(
numBlocks, peclet::core::IVec<3>{
gnx,
gny,
gnz},
388 const char*
e = std::getenv(
"PECLET_FLOW_DECOMP_MAX_IMBALANCE");
389 const double v =
e ? std::atof(e) : 1.05;
390 return v > 1.0 ? v : 1.05;
392 auto imbalanceOf = [](
const peclet::core::decomp::BlockDecomposer<3>&
d) {
393 std::size_t
hi = 0,
lo = std::numeric_limits<std::size_t>::max();
394 for (
const auto& s :
d.
sizes()) {
395 const std::size_t
n =
static_cast<std::size_t
>(
s[0]) *
static_cast<std::size_t
>(s[1]) *
396 static_cast<std::size_t
>(
s[2]);
400 return lo ?
static_cast<double>(
hi) /
static_cast<double>(
lo)
405 if (r[0] == 1 && r[1] == 1 && r[2] == 1)
407 const std::size_t
cells =
static_cast<std::size_t
>(
gnx /
r[0]) *
408 static_cast<std::size_t
>(
gny / r[1]) *
409 static_cast<std::size_t
>(
gnz /
r[2]);
415 peclet::core::decomp::BlockDecomposer<3>
coarse;
417 peclet::core::IVec<3>{1, 1, 1},
r);
418 peclet::core::decomp::BlockDecomposer<3>
fine =
coarse.refined(r);
421 printf(
"[mg] decomposition: coarse-first depth %d (refine %dx%dx%d, imbalance %.3f)\n",
L,
426 return peclet::core::decomp::BlockDecomposer<3>(
numBlocks, peclet::core::IVec<3>{
gnx,
gny,
gnz},
431 const peclet::core::decomp::BlockDecomposer<3>*
dec0 =
nullptr) {
439 int rank = 0, size = 1;
442 std::array<bool, 3>
per{
true,
true,
true};
444 auto can = [&](
int d) {
return (d % 2 == 0) && (
d / 2 >= 2); };
450 peclet::core::decomp::BlockDecomposer<3>
curDec;
458 auto minBlockExtent = [](
const peclet::core::decomp::BlockDecomposer<3>&
d) {
459 long m = std::numeric_limits<long>::max();
460 for (
const auto& s :
d.
sizes())
462 m = std::min(m, (
long)
s[
k]);
467 v.
halo = std::make_shared<GridHaloTopology<3>>();
468 const peclet::core::decomp::BlockDecomposer<3>&
dec =
curDec;
478 v.
halo->buildTopology(
dec, rank, v.g,
per, comm);
479 v.
dev = std::make_shared<GridHalo<double>>();
481 const auto&
idx = v.
halo->indexer();
482 const auto eg =
idx.sizeInclGhost(),
ino =
idx.sizeInner(),
oig =
idx.originInclGhost();
483 v.ext = {(
int)eg[0], (
int)
eg[1], (
int)eg[2]};
488 v.
n =
idx.numCellsInclGhost();
489 C3
next =
gs, ratio{1, 1, 1};
496 for (std::size_t b = 0; b <
curDec.sizes().size(); ++b)
522 *p =
FPV(
"mg_A", v.
n);
527 cf = C3{
cf.x * ratio.x,
cf.y * ratio.y,
cf.z * ratio.z};
529 curDec =
curDec.coarsened(peclet::core::IVec<3>{ratio.x, ratio.y, ratio.z});
532 printf(
"[mg] initMpi %dx%dx%d np=%d -> %d levels (requested %d)\n",
gnx,
gny,
gnz, size,
535 for (
int L = 0;
L < (
int)lv_.size(); ++
L) {
536 printf(
"[mg] L%d global %4dx%4dx%4d rank0 block %4dx%4dx%4d ratio(%d,%d,%d)\n",
L,
g.x,
537 g.y,
g.z, lv_[
L].inner.x, lv_[
L].inner.y, lv_[
L].inner.z, lv_[
L].ratio.x,
538 lv_[
L].ratio.y, lv_[
L].ratio.z);
539 g = C3{
g.x / lv_[
L].ratio.x,
g.y / lv_[
L].ratio.y,
g.z / lv_[
L].ratio.z};
545 int nLevels()
const {
return (
int)lv_.size(); }
554 for (
int i = 0;
i < 6; ++
i) {
570 B3 e{ext.
x, ext.
y, ext.
z};
571 for (
int a = 0; a < 3; ++a)
572 for (
int s = 0; s < 2; ++s)
573 if (bc_[2 * a + s] == 3)
584 for (
int a = 0; a < 3; ++a)
585 for (
int s = 0; s < 2; ++s) {
586 const int t = bc_[2 * a + s];
587 if (
t == 1 ||
t == 2)
598 Kokkos::deep_copy(f.ox, ox);
599 Kokkos::deep_copy(f.oy, oy);
600 Kokkos::deep_copy(f.oz, oz);
608 for (
int L = 1;
L < (int)lv_.size(); ++
L) {
612 fin.ext, c.g,
fin.g, c.inner,
fin.ratio);
615 const double sx = 1.0 / (
double)(c.cfac.x * c.cfac.x),
616 sy = 1.0 / (
double)(c.cfac.y * c.cfac.y),
617 sz = 1.0 / (
double)(c.cfac.z * c.cfac.z);
651 Kokkos::deep_copy(
l0.x, x);
658 Kokkos::deep_copy(
l0.rhs,
rr);
659 Kokkos::deep_copy(
l0.x, 0.0);
661 Kokkos::deep_copy(
zz,
l0.x);
664 Kokkos::deep_copy(r, b);
673#ifdef PECLET_FLOW_MPI
679 printf(
"[mg] solve %d: r0=%.6e rtol=%.1e (pre=%d post=%d bottom=%d)\n", dbgSolve_,
r0,
rtol,
685 if (
r0 > 0.0 && std::isfinite(
r0)) {
687 Kokkos::deep_copy(p, z);
689 if (!std::isfinite(
rz)) {
690 printf(
"peclet::flow CutcellMG::solvePCG: preconditioner produced non-finite z; "
691 "returning zero correction\n");
692 Kokkos::deep_copy(x, 0.0);
693 Kokkos::deep_copy(
l0.x, x);
701 if (!std::isfinite(
pAp) ||
pAp <= 1e-300)
709 printf(
"[mg] it %3d |r|inf=%.6e r/r0=%.4e\n",
it + 1,
rn,
rn /
r0);
716 if (!std::isfinite(
rznew))
722 Kokkos::deep_copy(
l0.x, x);
724 Kokkos::deep_copy(x,
l0.x);
755#ifdef PECLET_FLOW_MPI
756 if (distributed_ && h2) {
774 Kokkos::deep_copy(
l0.rhs,
rr);
775 Kokkos::deep_copy(
l0.x, 0.0);
777 Kokkos::deep_copy(
zz,
l0.x);
780 Kokkos::deep_copy(r, b);
783 Kokkos::deep_copy(
rh, r);
786 if (
r0n > 0.0 && std::isfinite(
r0n)) {
790 Kokkos::deep_copy(p, 0.0);
791 Kokkos::deep_copy(v, 0.0);
794 if (!std::isfinite(
rhoNew) || std::fabs(
rhoNew) < 1e-300)
804 if (!std::isfinite(
rhv) || std::fabs(
rhv) < 1e-300)
810 if (!std::isfinite(
rn))
821 if (!std::isfinite(
tt) ||
tt < 1e-300) {
826 if (!std::isfinite(
omega) || std::fabs(
omega) < 1e-300) {
835 if (!std::isfinite(
rn))
850 Kokkos::deep_copy(
l0.x, x);
852 Kokkos::deep_copy(x,
l0.x);
862 const auto t0 = std::chrono::steady_clock::now();
864 lvTime_.resize(lv_.size(), 0.0);
865 lvTime_[
L] += std::chrono::duration<double>(std::chrono::steady_clock::now() - t0).count();
866 if (
L == 0 && ++lvCycles_ % 50 == 0) {
868 for (std::size_t
i = 0;
i < lvTime_.size(); ++
i)
869 tot += (
i + 1 < lvTime_.size() ? lvTime_[
i] - lvTime_[
i + 1] : lvTime_[
i]);
870 printf(
"[mg] level times over %d V-cycles (total %.3f s):\n", lvCycles_, tot);
871 for (std::size_t
i = 0;
i < lvTime_.size(); ++
i) {
872 const double self = (
i + 1 < lvTime_.size() ? lvTime_[
i] - lvTime_[
i + 1] : lvTime_[
i]);
873 printf(
"[mg] L%zu %5dx%5dx%5d self %7.3f s (%5.1f%%)\n",
i, lv_[
i].inner.x,
874 lv_[
i].inner.y, lv_[
i].inner.z,
self, 100.0 *
self / (tot + 1e-30));
884 if (
L + 1 == (
int)lv_.size()) {
889 smooth(lv, bottom_,
false);
910#ifdef PECLET_FLOW_MPI
911 else if (distributed_) {
913 const C3 lo{g + 1, g + 1, g + 1};
915 lv.dev->exchangeBegin(lv.
x);
918 C3{0, 0, 0},
C3{0, 0, 0});
919 lv.dev->exchangeEnd(lv.
x);
923 C3{g, g, g},
C3{lv.ext.x - g, lv.ext.y - g, lv.ext.z - g},
lo,
hi);
933 Kokkos::deep_copy(cs.x, 0.0);
939 if (meanRemovalAll_ ||
L == 0)
948#ifdef PECLET_FLOW_MPI
959 const C3 lo{g + 1, g + 1, g + 1};
961 const C3 rlo{g - 1, g - 1, g - 1};
963 lv.dev->exchange(lv.
rhs);
966 lv.dev->exchangeBegin(lv.
x);
969 hi,
C3{0, 0, 0},
C3{0, 0, 0});
970 lv.dev->exchangeEnd(lv.
x);
981 for (
int s = 0; s < 2; ++s) {
983#ifdef PECLET_FLOW_MPI
991 const C3 lo{g + 1, g + 1, g + 1};
993 lv.dev->exchangeBegin(lv.
x);
997 lv.dev->exchangeEnd(lv.
x);
1001 C3{g, g, g},
C3{lv.ext.x - g, lv.ext.y - g, lv.ext.z - g},
lo,
hi);
1035 if (agglomMode_ == 0)
1037 if (agglomMode_ == 1)
1055 static const int thresh = [] {
1056 const char* e = std::getenv(
"PECLET_FLOW_AGGLOM_EXTENT");
1057 const int v = e ? std::atoi(e) : 4;
1058 return v > 0 ? v : 4;
1061 long gx = gnxF_,
gy = gnyF_,
gz = gnzF_;
1062 for (
int L = 0;
L + 1 < (int)lv_.size(); ++
L) {
1063 gx /= lv_[
L].ratio.x;
1064 gy /= lv_[
L].ratio.y;
1065 gz /= lv_[
L].ratio.z;
1075 int gbx = gnxF_,
gby = gnyF_,
1077 for (
int L = 0;
L + 1 < (int)lv_.size(); ++
L) {
1078 gbx /= lv_[
L].ratio.x;
1079 gby /= lv_[
L].ratio.y;
1080 gbz /= lv_[
L].ratio.z;
1085 auto h = Kokkos::create_mirror_view(v);
1086 Kokkos::deep_copy(
h, v);
1094 std::vector<std::uint8_t>
lsolid;
1095 amgGlobalOfLocal_.clear();
1096 const int band[6][3] = {{-1, 0, 0}, {1, 0, 0}, {0, -1, 0}, {0, 1, 0}, {0, 0, -1}, {0, 0, 1}};
1098 for (
int k = 0;
k < nz; ++
k)
1099 for (
int j = 0;
j < ny; ++
j)
1100 for (
int i = 0;
i < nx; ++
i) {
1101 const long p = (
long)(
i + g) + (
long)(
j + g) * ex + (
long)(
k + g) * ex * ey;
1104 amgGlobalOfLocal_.push_back(
gid);
1110 lsolid.push_back(
dc == 0.0 ? 1 : 0);
1113 for (
int d = 0; d < 6; ++d) {
1121 const int rx =
gx + band[d][0],
ry =
gy + band[d][1],
rz =
gz + band[d][2];
1122 const int axis = d / 2;
1133 lval.push_back(bc[d]);
1141#ifdef PECLET_FLOW_MPI
1151 amgSolid_.assign((std::size_t)amgGlobalN_, 0);
1152 for (std::size_t r = 0; r <
ggid.size(); ++r)
1157 std::vector<int>
parent((std::size_t)amgGlobalN_);
1158 for (
int i = 0;
i < amgGlobalN_; ++
i)
1160 auto find = [&](
int a) {
1161 while (
parent[(std::size_t)a] != a)
1165 for (std::size_t e = 0; e <
grow.size(); ++e) {
1170 amgComp_.assign((std::size_t)amgGlobalN_, -1);
1171 std::vector<int>
remap((std::size_t)amgGlobalN_, -1);
1173 for (
int i = 0;
i < amgGlobalN_; ++
i) {
1174 if (amgSolid_[(std::size_t)
i])
1176 const int r =
find(
i);
1177 if (
remap[(std::size_t)r] < 0)
1178 remap[(std::size_t)r] = amgNComp_++;
1179 amgComp_[(std::size_t)
i] =
remap[(std::size_t)r];
1183 std::vector<long>
csize((std::size_t)amgNComp_, 0);
1184 for (
int i = 0;
i < amgGlobalN_; ++
i)
1185 if (amgComp_[(std::size_t)
i] >= 0)
1186 ++
csize[(std::size_t)amgComp_[(std::size_t)
i]];
1187 printf(
"[agmg-build] fluid components=%d sizes:", amgNComp_);
1188 for (
int c = 0; c < std::min(amgNComp_, 12); ++c)
1190 printf(amgNComp_ > 12 ?
" ...\n" :
"\n");
1195 for (std::size_t r = 0; r <
ggid.size(); ++r) {
1200 const double ad = std::fabs(
gdiag[r]);
1206 printf(
"[agmg-build] n=%d solid=%ld fluid=%ld fluid|diag| min=%.3e max=%.3e "
1207 "tiny<1e-30=%ld <1e-12=%ld\n",
1214 std::vector<double>
rowsum((std::size_t)amgGlobalN_, 0.0);
1215 for (std::size_t r = 0; r <
ggid.size(); ++r)
1217 for (std::size_t e = 0; e <
grow.size(); ++e)
1218 if (!amgSolid_[(std::size_t)
grow[e]])
1221 for (std::size_t r = 0; r <
ggid.size(); ++r)
1223 const double d = std::fabs(
rowsum[(std::size_t)
ggid[r]]);
1231 peclet::core::solver::HostCsrOp
A;
1233 A.diag.assign((std::size_t)amgGlobalN_, 0.0);
1234 for (std::size_t r = 0; r <
ggid.size(); ++r)
1236 std::vector<std::vector<std::pair<int, double>>>
rows((std::size_t)amgGlobalN_);
1237 for (std::size_t e = 0; e <
grow.size(); ++e)
1239 A.start.assign((std::size_t)amgGlobalN_ + 1, 0);
1240 for (
int r = 0; r < amgGlobalN_; ++r)
1241 A.start[(std::size_t)r + 1] =
A.start[(std::size_t)r] + (
long)
rows[(std::size_t)r].size();
1242 A.nbr.reserve(
grow.size());
1243 A.coef.reserve(
grow.size());
1244 for (
int r = 0; r < amgGlobalN_; ++r)
1245 for (
auto& [c, v] :
rows[(std::size_t)r]) {
1247 A.coef.push_back(v);
1256 for (
int r = 0; r < amgGlobalN_; ++r)
1257 if (!amgSolid_[(std::size_t)r] && !
rows[(std::size_t)r].
empty()) {
1259 for (
auto& [c, v] :
rows[(std::size_t)r])
1261 A.diag[(std::size_t)r] = -s;
1264 amg_ = std::make_shared<peclet::core::solver::GraphAMG>();
1273 if (!amg_ && !distributed_)
1275#ifdef PECLET_FLOW_MPI
1276 if (distributed_ && amgGlobalN_ == 0)
1281 auto hrhs = Kokkos::create_mirror_view(lv.
rhs);
1282 Kokkos::deep_copy(
hrhs, lv.
rhs);
1283 std::vector<double>
lb;
1284 lb.reserve(amgGlobalOfLocal_.size());
1285 for (
int k = 0;
k < nz; ++
k)
1286 for (
int j = 0;
j < ny; ++
j)
1287 for (
int i = 0;
i < nx; ++
i)
1288 lb.push_back((
double)
hrhs((
long)(
i + g) + (
long)(
j + g) * ex + (
long)(
k + g) * ex * ey));
1290 std::vector<double> z((std::size_t)std::max(amgGlobalN_, 1), 0.0);
1291#ifdef PECLET_FLOW_MPI
1293 std::vector<int>
ggid;
1294 std::vector<double>
gb;
1297 std::vector<double> b((std::size_t)amgGlobalN_, 0.0);
1298 for (std::size_t r = 0; r <
ggid.size(); ++r)
1299 b[(std::size_t)
ggid[r]] =
gb[r];
1304 std::vector<double> b(
lb.begin(),
lb.end());
1308 auto hx = Kokkos::create_mirror_view(lv.
x);
1309 Kokkos::deep_copy(
hx, 0.0);
1311 for (
int k = 0;
k < nz; ++
k)
1312 for (
int j = 0;
j < ny; ++
j)
1313 for (
int i = 0;
i < nx; ++
i)
1314 hx((
long)(
i + g) + (
long)(
j + g) * ex + (
long)(
k + g) * ex * ey) =
1315 z[(std::size_t)amgGlobalOfLocal_[c++]];
1316 Kokkos::deep_copy(lv.
x,
hx);
1317 if (agmgDebug() && !distributed_) {
1326 auto hb = Kokkos::create_mirror_view(lv.
rhs);
1327 Kokkos::deep_copy(
hb, lv.
rhs);
1329 for (std::size_t
i = 0;
i <
hb.size(); ++
i)
1330 bn = std::max(
bn, std::fabs((
double)
hb(
i)));
1331 printf(
"[agmg] vcycle-op residual of CSR solution: max|b-Ax|=%.3e max|b|=%.3e rel=%.3e\n",
1342 void pcgAmg(std::vector<double>& b, std::vector<double>& x) {
1343 const std::size_t n = (std::size_t)amgGlobalN_;
1344 const int dbg = agmgDebug();
1348 double sf = 0,
sa = 0;
1349 for (std::size_t
i = 0;
i < n; ++
i) {
1351 if (
i < amgSolid_.size() && amgSolid_[
i])
1363 if (
dbg && amgNComp_ > 1) {
1364 std::vector<double> cs((std::size_t)amgNComp_, 0.0);
1365 for (std::size_t
i = 0;
i < n; ++
i)
1366 if (amgComp_[
i] >= 0)
1367 cs[(std::size_t)amgComp_[
i]] += b[
i];
1372 auto meanZero = [&](std::vector<double>& v) {
1384 std::vector<double> m((std::size_t)amgNComp_, 0.0);
1385 std::vector<long>
cnt((std::size_t)amgNComp_, 0);
1386 for (std::size_t
i = 0;
i < n; ++
i)
1387 if (amgComp_[
i] >= 0) {
1388 m[(std::size_t)amgComp_[
i]] += v[
i];
1389 ++
cnt[(std::size_t)amgComp_[
i]];
1391 for (std::size_t c = 0; c < m.size(); ++c)
1392 m[c] =
cnt[c] ? m[c] / (
double)
cnt[c] : 0.0;
1393 for (std::size_t
i = 0;
i < n; ++
i)
1394 if (amgComp_[
i] >= 0)
1395 v[
i] -= m[(std::size_t)amgComp_[
i]];
1399 std::vector<double> r = b, z(n), p(n),
Ap(n);
1403 auto dot = [&](
const std::vector<double>& a,
const std::vector<double>& c) {
1405 for (std::size_t
i = 0;
i < n; ++
i)
1409 double rz =
dot(r, z),
r0 = std::sqrt(
dot(r, r));
1417 const double a =
rz /
dot(p,
Ap);
1418 for (std::size_t
i = 0;
i < n; ++
i) {
1422 if (std::sqrt(
dot(r, r)) <= 1e-8 *
r0)
1426 const double rzn =
dot(r, z);
1427 const double beta =
rzn /
rz;
1429 for (std::size_t
i = 0;
i < n; ++
i)
1430 p[
i] = z[
i] + beta * p[
i];
1435 for (std::size_t
i = 0;
i < n; ++
i)
1436 if (
i < amgSolid_.size() && amgSolid_[
i])
1440 const double rn = std::sqrt(
dot(r, r));
1443 printf(
"[agmg] call=%ld iters=%d relres=%.2e |b|sol=%.3e |b|fl=%.3e "
1444 "mean(b) fl=%.3e all=%.3e compat=%.2e |x|sol=%.3e |x|fl=%.3e%s\n",
1451#ifdef PECLET_FLOW_MPI
1453 void gatherv(
const std::vector<T>&
local, std::vector<T>& all) {
1460 std::vector<int> bc(size),
bd(size, 0);
1463 for (
int r = 0; r < size; ++r) {
1467 all.resize((std::size_t)tot /
sizeof(
T));
1478#ifdef PECLET_FLOW_MPI
1480 const C3 lo{
G + 1,
G + 1,
G + 1};
1481 const C3 hi{
l0.ext.
x -
G - 1,
l0.ext.y -
G - 1,
l0.ext.z -
G - 1};
1482 l0.dev->exchangeBegin(v);
1484 FPC(
l0.AB),
FPC(
l0.AT),
l0.ext,
lo,
hi,
C3{0, 0, 0},
C3{0, 0, 0});
1485 l0.dev->exchangeEnd(v);
1489 C3{l0.ext.x - G, l0.ext.y - G, l0.ext.z - G},
lo,
hi);
1501#ifdef PECLET_FLOW_MPI
1503 lv.dev->exchange(f);
1521 int dims[3] = {e.x, e.y, e.z};
1522 long st[3] = {1, e.x, (
long)e.x * e.y};
1523 const int a = axis, b = (axis + 1) % 3, c = (axis + 2) % 3;
1524 const long sa = st[a],
sb = st[b], sc = st[c];
1525 const int N =
N3[a];
1527 Kokkos::parallel_for(
1528 "peclet::flow::mg_pfill",
1531 const long base = (
long)p0 *
sb + (
long)
p1 * sc;
1532 for (
int gl = 0;
gl <
G; ++
gl) {
1538#ifdef PECLET_FLOW_MPI
1545 Kokkos::parallel_for(
1546 "peclet::flow::gp_stage_g2",
1547 Kokkos::MDRangePolicy<
CCExec, Kokkos::Rank<3>>(
space, {0, 0, 0}, {
nn.x,
nn.y,
nn.z}),
1549 dst((
long)(x + 2) + (
long)(y + 2) *
ext2.x + (
long)(z + 2) * (
long)
ext2.x *
ext2.y) =
1550 src((
long)(x +
G) + (
long)(y +
G) *
e1.x + (
long)(z +
G) * (
long)
e1.x *
e1.y);
1557 const C3
e1 =
l0.ext;
1560 Kokkos::parallel_for(
1561 "peclet::flow::gp_unstage_g2",
1562 Kokkos::MDRangePolicy<
CCExec, Kokkos::Rank<3>>(
space, {0, 0, 0}, {
e1.x,
e1.y,
e1.z}),
1564 dst((
long)x + (
long)y *
e1.x + (
long)z * (
long)
e1.x *
e1.y) =
1565 src((
long)(x + 1) + (
long)(y + 1) *
ext2.x + (
long)(z + 1) * (
long)
ext2.x *
ext2.y);
1572 std::size_t n = y.extent(0);
1573 Kokkos::parallel_for(
1574 "mgaxpy", Kokkos::RangePolicy<CCExec>(
space, 0, n),
1580 std::size_t n = y.extent(0);
1581 Kokkos::parallel_for(
1582 "mgaypx", Kokkos::RangePolicy<CCExec>(
space, 0, n),
1588 std::size_t n = y.extent(0);
1589 Kokkos::parallel_for(
1590 "mgscale", Kokkos::RangePolicy<CCExec>(
space, 0, n),
1596 std::size_t n = out.extent(0);
1597 Kokkos::parallel_for(
1598 "mglin", Kokkos::RangePolicy<CCExec>(
space, 0, n),
1606 std::size_t n = f.extent(0);
1607 Kokkos::parallel_for(
1609 if (!(
ac(
i) > 1e-30f))
1624 const std::size_t n =
l0.n;
1625 CCField v(
"ev_v", n), w(
"ev_w", n), z(
"ev_z", n),
srhs(
"ev_srhs", n);
1631 Kokkos::deep_copy(
l0.rhs, w);
1632 Kokkos::deep_copy(
l0.x, 0.0);
1634 Kokkos::deep_copy(out,
l0.x);
1639 double nr = std::sqrt(
dot(
l0, x, x));
1644 Kokkos::deep_copy(x,
srhs);
1651 for (
int k = 0;
k < iters; ++
k) {
1654 Kokkos::deep_copy(v, z);
1659 for (
int k = 0;
k < iters; ++
k) {
1663 Kokkos::deep_copy(v, z);
1678 double a,
double bnd) {
1683 const std::size_t n =
l0.n;
1691 CCField r(
"cb_r", n), z(
"cb_z", n), d(
"cb_d", n), w(
"cb_w", n);
1694 Kokkos::deep_copy(
l0.rhs,
rr);
1695 Kokkos::deep_copy(
l0.x, 0.0);
1697 Kokkos::deep_copy(
zz,
l0.x);
1700 double rho = 1.0 /
sigma1;
1702 Kokkos::deep_copy(r, b);
1710 lin(d, 1.0 / theta, z, 0.0, z);
1738 "mgdot",
C3{g, g, g},
C3{e.x - g, e.y - g, e.z - g},
1740 const long i = (
long)x + (
long)y * e.x + (
long)z * (
long)e.x * e.y;
1745 return allreduce(s, MPI_SUM_);
1755 "mgmax",
C3{g, g, g},
C3{e.x - g, e.y - g, e.z - g},
1757 const long i = (
long)x + (
long)y * e.x + (
long)z * (
long)e.x * e.y;
1758 if (
ac(
i) > 1e-30f) {
1759 const double v = Kokkos::fabs(
aa(
i));
1764 Kokkos::Max<double>(m));
1765 return allreduce(m, MPI_MAX_);
1777 Kokkos::parallel_reduce(
1779 Kokkos::MDRangePolicy<
CCExec, Kokkos::Rank<3>>(
space, {g, g, g},
1780 {e.x - g, e.y - g, e.z - g}),
1782 const long i = (
long)x + (
long)y * e.x + (
long)z * (
long)e.x * e.y;
1783 if (
ac(
i) > 1e-30f) {
1790 allreduceSum2(sum,
dcnt);
1795 Kokkos::parallel_for(
1797 Kokkos::MDRangePolicy<
CCExec, Kokkos::Rank<3>>(
space, {g, g, g},
1798 {e.x - g, e.y - g, e.z - g}),
1800 const long i = (
long)x + (
long)y * e.x + (
long)z * (
long)e.x * e.y;
1821 allreduceTime_ = 0.0;
1822 allreduceCount_ = 0;
1826 enum AllOp { kSum, kMax };
1829 double allreduce(
double v, AllOp op) {
1830#ifdef PECLET_FLOW_MPI
1832 const auto t0 = std::chrono::steady_clock::now();
1835 allreduceTime_ += std::chrono::duration<double>(std::chrono::steady_clock::now() - t0).count();
1845 void allreduceSum2(
double& a,
double& b) {
1846#ifdef PECLET_FLOW_MPI
1848 const auto t0 = std::chrono::steady_clock::now();
1849 double v[2] = {
a, b},
g[2] = {0.0, 0.0};
1851 allreduceTime_ += std::chrono::duration<double>(std::chrono::steady_clock::now() - t0).count();
1858 static constexpr AllOp MPI_SUM_ = kSum, MPI_MAX_ = kMax;
1860 std::vector<Level> lv_;
1861 int pre_ = 2, post_ = 2, bottom_ = 4;
1862 int bc_[6] = {0, 0, 0, 0, 0, 0};
1863 bool hasBC_ =
false, removeMean_ =
true, hasOutflow_ =
false;
1867 bool meanRemovalAll_ =
false;
1868 bool distributed_ =
false;
1870 std::vector<double> lvTime_;
1874 bool resFill_ = [] {
1875 const char*
e = std::getenv(
"PECLET_FLOW_MG_RESFILL");
1876 return !
e || std::atoi(e) != 0;
1878 double allreduceTime_ = 0.0;
1879 long allreduceCount_ = 0;
1888 int agglomMode_ = 0;
1889 int gnxF_ = 0, gnyF_ = 0, gnzF_ = 0;
1890 mutable std::shared_ptr<peclet::core::solver::GraphAMG>
1892 mutable peclet::core::solver::HostCsrOp
1894 mutable std::vector<int> amgOwnerCount_;
1895 mutable std::vector<int>
1897 mutable int amgGlobalN_ = 0;
1899 mutable std::vector<std::uint8_t> amgSolid_;
1900 mutable std::vector<int> amgComp_;
1901 mutable int amgNComp_ = 0;
1902 mutable long agmgCalls_ = 0;
1903 static int agmgDebug() {
1904 static const int v = [] {
1905 const char*
e = std::getenv(
"PECLET_FLOW_AGMG_DEBUG");
1906 return e ? std::atoi(e) : 0;
1910#ifdef PECLET_FLOW_MPI
1920 agglomMode_ =
on ? 1 : 0;
void axpy(CCField y, double a, CCField x)
void scale(CCField y, double a)
int solveChebyshev(CCField b, CCField x, int maxit, double rtol, int pre, int post, int bottom, double a, double bnd)
void aypx(CCField y, double a, CCField x)
void fillAxis(Level &lv, CCField f, int axis)
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})
void matvecOverlap(Level &l0, CCField y, CCField v)
void resetAllreduceCounters()
void setGraphAmgBottom(bool on)
static C3 parityOg(const Level &lv)
void vcycle(int L, bool sym)
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 setBoundaryConditions(const int bc[6])
long allreduceCount() const
double maxabs(Level &lv, CCField a)
void vcycleImpl(int L, bool sym)
void maskSolid(Level &lv, CCField f)
void lin(CCField out, double a, CCField x, double b, CCField y)
double dot(Level &lv, CCField a, CCField b)
void fill(Level &lv, CCField f)
void removeMean(Level &lv, CCField f)
void applyBoundaryOpenness(Level &lv)
double allreduceSeconds() const
int agglomerationMode() const
void graphAmgSolveBottom(Level &lv)
void setAgglomerationMode(int mode)
void smooth(Level &lv, int sweeps, bool reverse)
void applyOutflowGhost(C3 ext, CCField x, int g=G)
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 GpOverlay &ov, int nOv, C3 nn)
void init(int nx, int ny, int nz, int nLevels)
bool caSmooth(const Level &lv) const
void setMeanRemovalScope(bool all)
void estimateEigenvalues(CCConst seed, double &lmin, double &lmax, int iters, int pre, int post, int bottom)
bool agglomerateBottom() const
flow — directional ghost-cell IBM projection overlay (experimental second staggered IBM).
flow — portable (Kokkos) native per-face domain boundary conditions for the MAC grid.
flow — portable (Kokkos) cut-cell pressure operator + Chorin projection.
void bcSetOpenness(BField oa, B3 ext, int g, int a, int s, double val)
void gpApplyDelta(CCField y, CCConst x, const GpOverlay &ov, int nOv, C3 nn, C3 extY, int gbY, C3 extX, int gbX, bool useGhost=false)
Overlay matvec correction: y(r) = rho_r * (y(r) + closure-face phi terms), where y currently holds th...
Kokkos::View< const MReal *, CCMem > FPC
void prolongAdd(CCField fine, CCConst coarse, C3 fext, C3 cext, int gf, int gc, C3 finner, C3 ratio)
void bcZeroPressureGhost(BField phi, B3 ext, int g, int a, int s)
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 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 starApplyDelta(CCField y, CCConst x, const StarOverlay &ov, int nOv, C3 nn, C3 extY, int gY, C3 extX, int gX)
y += S_star x over the inner cells of the (extY, gY) block, x read from the (extX,...
void cutcellSmoothColorBox(CCField phi, CCConst b, OpV AC, OpV AW, OpV AE, OpV AS, OpV AN, OpV AB, OpV AT, C3 e, C3 og, int color, C3 rlo, C3 rhi, C3 slo, C3 shi)
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 ccReduce3(const char *name, C3 lo, C3 hi, F f, R &&reducer)
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)
void ibmFillEntry(const OV &o, int list_idx, int c_idx, float sdf_c, const float sdf_n[6], int bc_type, const float *thEx)
void buildCutcellOp(OpV AC, OpV AW, OpV AE, OpV AS, OpV AN, OpV AB, OpV AT, CCConst ox, CCConst oy, CCConst oz, C3 e, int g, double gfx, double gfy, double gfz)
Kokkos::View< double *, CCMem > CCField
void restrictAvg(CCField coarse, CCConst fine, C3 cext, C3 fext, int gc, int gf, C3 cinner, C3 ratio)
Kokkos::View< MReal *, CCMem > FPV
void cutcellSmoothColor(CCField phi, CCConst b, OpV AC, OpV AW, OpV AE, OpV AS, OpV AN, OpV AB, OpV AT, C3 e, C3 og, int g, int color)
Kokkos::DefaultExecutionSpace CCExec
void applyCutcellOpBox(CCField y, CCConst x, OpV AC, OpV AW, OpV AE, OpV AS, OpV AN, OpV AB, OpV AT, C3 e, C3 rlo, C3 rhi, C3 slo, C3 shi)
Kokkos::View< const double *, CCMem > CCConst
flow — Design B of the fluid-only collocated constraint (route 2b): Kron (star-mesh) elimination of s...
std::unique_ptr< GridHalo< double > > dev
std::unique_ptr< GridHaloTopology< kDim > > halo
One entry per eliminated solid-centered cell: packed INNER flat index + the apertures of its (up to 6...
static constexpr double AC
static constexpr double F