14#ifndef PECLET_FLOW_MAC_CUTCELL_MG_HPP
15#define PECLET_FLOW_MAC_CUTCELL_MG_HPP
18#include <Kokkos_Core.hpp>
24#include "peclet/core/solver/graph_amg.hpp"
32#include "peclet/core/decomp/block_decomposer.hpp"
33#include "peclet/core/decomp/grid_redistribute.hpp"
34#include "peclet/core/halo/grid_halo.hpp"
35#include "peclet/core/halo/grid_halo_topology.hpp"
41using peclet::core::halo::GridHalo;
42using peclet::core::halo::GridHaloTopology;
46using FPV = Kokkos::View<MReal*, CCMem>;
47using FPC = Kokkos::View<const MReal*, CCMem>;
54 using MD = Kokkos::MDRangePolicy<CCExec, Kokkos::Rank<3>>;
56 "peclet::flow::coarsen_open", MD(space, {0, 0, 0}, {cinner.
x, cinner.
y, cinner.
z}),
57 KOKKOS_LAMBDA(
int icx,
int icy,
int icz) {
58 const int rx = ratio.
x, ry = ratio.
y, rz = ratio.
z;
59 const int fx0 = rx * icx + g, fy0 = ry * icy + g, fz0 = rz * icz + g;
60 const long fsy = fext.
x, fsz = (long)fext.
x * fext.
y;
61 auto F = [&](
CCConst T,
int x,
int y,
int z) {
62 return T((
long)x + (
long)y * fsy + (
long)z * fsz);
64 double sx = 0, sy = 0, sz = 0;
65 for (
int a = 0; a < ry; ++a)
66 for (
int b = 0; b < rz; ++b)
67 sx +=
F(oxf, fx0, fy0 + a, fz0 + b);
68 for (
int a = 0; a < rx; ++a)
69 for (
int b = 0; b < rz; ++b)
70 sy +=
F(oyf, fx0 + a, fy0, fz0 + b);
71 for (
int a = 0; a < rx; ++a)
72 for (
int b = 0; b < ry; ++b)
73 sz +=
F(ozf, fx0 + a, fy0 + b, fz0);
75 (long)(icx + g) + (long)(icy + g) * cext.
x + (long)(icz + g) * (long)cext.
x * cext.
y;
76 oxc(ci) = sx / (double)(ry * rz);
77 oyc(ci) = sy / (double)(rx * rz);
78 ozc(ci) = sz / (double)(rx * ry);
86 using MD = Kokkos::MDRangePolicy<CCExec, Kokkos::Rank<3>>;
88 "peclet::flow::cc_residual", MD(space, {g, g, g}, {e.x - g, e.y - g, e.z - g}),
89 KOKKOS_LAMBDA(
int lx,
int ly,
int lz) {
90 const long sx = 1, sy = e.x, sz = (long)e.x * e.y;
91 const long i = (long)lx + (
long)ly * sy + (long)lz * sz;
92 const double Ax = (double)
AC(i) * x(i) + (double)AE(i) * x(i + sx) +
93 (double)AW(i) * x(i - sx) + (double)AN(i) * x(i + sy) +
94 (double)AS(i) * x(i - sy) + (double)AT(i) * x(i + sz) +
95 (double)AB(i) * x(i - sz);
105 using MD = Kokkos::MDRangePolicy<CCExec, Kokkos::Rank<3>>;
106 Kokkos::parallel_for(
107 "peclet::flow::restrict", MD(space, {0, 0, 0}, {cinner.
x, cinner.
y, cinner.
z}),
108 KOKKOS_LAMBDA(
int icx,
int icy,
int icz) {
109 const long fsy = fext.
x, fsz = (long)fext.
x * fext.
y;
111 for (
int dz = 0; dz < ratio.
z; ++dz)
112 for (
int dy = 0; dy < ratio.
y; ++dy)
113 for (
int dx = 0; dx < ratio.
x; ++dx) {
114 const int fx = ratio.
x * icx + dx + g, fy = ratio.
y * icy + dy + g,
115 fz = ratio.
z * icz + dz + g;
116 s += fine((
long)fx + (
long)fy * fsy + (
long)fz * fsz);
119 (long)(icx + g) + (long)(icy + g) * cext.
x + (long)(icz + g) * (long)cext.
x * cext.
y;
120 coarse(ci) = s / (double)(ratio.
x * ratio.
y * ratio.
z);
125 using MD = Kokkos::MDRangePolicy<CCExec, Kokkos::Rank<3>>;
126 Kokkos::parallel_for(
127 "peclet::flow::prolong", MD(space, {0, 0, 0}, {finner.
x, finner.
y, finner.
z}),
128 KOKKOS_LAMBDA(
int ifx,
int ify,
int ifz) {
131 const double cx = (ratio.
x == 2) ? 0.5 * ifx - 0.25 + g : ifx + g;
132 const double cy = (ratio.
y == 2) ? 0.5 * ify - 0.25 + g : ify + g;
133 const double cz = (ratio.
z == 2) ? 0.5 * ifz - 0.25 + g : ifz + g;
134 const double fxw = Kokkos::floor(cx), fyw = Kokkos::floor(cy), fzw = Kokkos::floor(cz);
135 const double wx = cx - fxw, wy = cy - fyw, wz = cz - fzw;
136 const int x0 = (int)fxw, y0 = (
int)fyw, z0 = (int)fzw;
137 const long sy = cext.
x, sz = (long)cext.
x * cext.
y;
138 auto C = [&](
int xx,
int yy,
int zz) {
139 return coarse((
long)xx + (
long)yy * sy + (
long)zz * sz);
141 const double c00 = C(x0, y0, z0) * (1 - wx) + C(x0 + 1, y0, z0) * wx;
142 const double c10 = C(x0, y0 + 1, z0) * (1 - wx) + C(x0 + 1, y0 + 1, z0) * wx;
143 const double c01 = C(x0, y0, z0 + 1) * (1 - wx) + C(x0 + 1, y0, z0 + 1) * wx;
144 const double c11 = C(x0, y0 + 1, z0 + 1) * (1 - wx) + C(x0 + 1, y0 + 1, z0 + 1) * wx;
145 const double c0 = c00 * (1 - wy) + c10 * wy, c1 = c01 * (1 - wy) + c11 * wy;
147 (long)(ifx + g) + (long)(ify + g) * fext.
x + (long)(ifz + g) * (long)fext.
x * fext.
y;
148 fine(fi) += c0 * (1 - wz) + c1 * wz;
160#ifdef PECLET_FLOW_MPI
161 std::shared_ptr<GridHaloTopology<3>> halo;
162 std::shared_ptr<GridHalo<double>> dev;
165 static constexpr int G = 1;
175 C3 inner{nx, ny, nz}, cf{1, 1, 1};
179 v.
ext =
C3{inner.
x + 2 *
G, inner.y + 2 *
G, inner.z + 2 *
G};
182 auto can = [&](
int d) {
return (d % 2 == 0) && (d / 2 >= 2); };
188 next.x = inner.x / 2;
192 next.y = inner.y / 2;
196 next.z = inner.z / 2;
207 *p =
FPV(
"mg_A", v.
n);
209 if (
next.x == inner.x &&
next.y == inner.y &&
next.z == inner.z)
212 cf =
C3{cf.x * ratio.x, cf.y * ratio.y, cf.z * ratio.z};
215#ifdef PECLET_FLOW_MPI
228 const peclet::core::decomp::BlockDecomposer<3>*
dec0 =
nullptr) {
236 int rank = 0, size = 1;
239 std::array<bool, 3>
per{
true,
true,
true};
241 auto can = [&](
int d) {
return (d % 2 == 0) && (d / 2 >= 2); };
244 v.
halo = std::make_shared<GridHaloTopology<3>>();
245 peclet::core::decomp::BlockDecomposer<3>
decOwn(
static_cast<std::size_t
>(size),
246 peclet::core::IVec<3>{
gs.x,
gs.y,
gs.z});
247 const peclet::core::decomp::BlockDecomposer<3>&
dec = (
L == 0 &&
dec0) ? *
dec0 :
decOwn;
249 v.
dev = std::make_shared<GridHalo<double>>();
251 const auto&
idx = v.
halo->indexer();
252 const auto eg =
idx.sizeInclGhost(),
ino =
idx.sizeInner(),
oig =
idx.originInclGhost();
258 v.
n =
idx.numCellsInclGhost();
259 C3
next =
gs, ratio{1, 1, 1};
282 *p =
FPV(
"mg_A", v.
n);
287 cf = C3{
cf.x * ratio.x,
cf.y * ratio.y,
cf.z * ratio.z};
291 int nLevels()
const {
return (
int)lv_.size(); }
300 for (
int i = 0;
i < 6; ++
i) {
315 B3 e{ext.
x, ext.
y, ext.
z};
316 for (
int a = 0;
a < 3; ++
a)
317 for (
int s = 0;
s < 2; ++
s)
318 if (bc_[2 *
a +
s] == 3)
329 for (
int a = 0;
a < 3; ++
a)
330 for (
int s = 0;
s < 2; ++
s) {
331 const int t = bc_[2 *
a +
s];
332 if (
t == 1 ||
t == 2)
343 Kokkos::deep_copy(f.ox, ox);
344 Kokkos::deep_copy(f.oy, oy);
345 Kokkos::deep_copy(f.oz, oz);
353 for (
int L = 1;
L < (
int)lv_.size(); ++
L) {
357 fin.ext,
G, c.inner,
fin.ratio);
360 const double sx = 1.0 / (
double)(c.cfac.x * c.cfac.x),
361 sy = 1.0 / (
double)(c.cfac.y * c.cfac.y),
362 sz = 1.0 / (
double)(c.cfac.z * c.cfac.z);
376 Kokkos::deep_copy(
l0.x, x);
384 Kokkos::deep_copy(
l0.rhs,
rr);
385 Kokkos::deep_copy(
l0.x, 0.0);
387 Kokkos::deep_copy(
zz,
l0.x);
390 Kokkos::deep_copy(r, b);
397 Kokkos::deep_copy(p, z);
419 Kokkos::deep_copy(
l0.x, x);
421 Kokkos::deep_copy(x,
l0.x);
428 if (
L + 1 == (
int)lv_.size()) {
429 if (useGraphAmgBottom_)
433 smooth(lv, bottom_,
false);
442 Kokkos::deep_copy(
cs.x, 0.0);
453 for (
int s = 0;
s < 2; ++
s) {
467 int gbx = gnxF_,
gby = gnyF_,
469 for (
int L = 0;
L + 1 < (
int)lv_.size(); ++
L) {
470 gbx /= lv_[
L].ratio.x;
471 gby /= lv_[
L].ratio.y;
472 gbz /= lv_[
L].ratio.z;
477 auto h = Kokkos::create_mirror_view(v);
478 Kokkos::deep_copy(
h, v);
486 amgGlobalOfLocal_.clear();
487 const int band[6][3] = {{-1, 0, 0}, {1, 0, 0}, {0, -1, 0}, {0, 1, 0}, {0, 0, -1}, {0, 0, 1}};
488 for (
int k = 0;
k < nz; ++
k)
489 for (
int j = 0;
j < ny; ++
j)
490 for (
int i = 0;
i < nx; ++
i) {
494 amgGlobalOfLocal_.push_back(
gid);
502 for (
int d = 0; d < 6; ++d) {
511 const int axis = d / 2;
522 lval.push_back(bc[d]);
529#ifdef PECLET_FLOW_MPI
542 peclet::core::solver::HostCsrOp
A;
544 A.diag.assign((std::size_t)amgGlobalN_, 0.0);
545 for (std::size_t r = 0; r <
ggid.size(); ++r)
547 std::vector<std::vector<std::pair<int, double>>>
rows((std::size_t)amgGlobalN_);
548 for (std::size_t e = 0; e <
grow.size(); ++e)
550 A.start.assign((std::size_t)amgGlobalN_ + 1, 0);
551 for (
int r = 0; r < amgGlobalN_; ++r)
552 A.start[(std::size_t)r + 1] =
A.start[(std::size_t)r] + (
long)
rows[(std::size_t)r].size();
553 A.nbr.reserve(
grow.size());
554 A.coef.reserve(
grow.size());
555 for (
int r = 0; r < amgGlobalN_; ++r)
556 for (
auto& [c, v] :
rows[(std::size_t)r]) {
561 amg_ = std::make_shared<peclet::core::solver::GraphAMG>();
568 if (!amg_ && !distributed_)
570#ifdef PECLET_FLOW_MPI
571 if (distributed_ && amgGlobalN_ == 0)
575 auto hrhs = Kokkos::create_mirror_view(lv.
rhs);
576 Kokkos::deep_copy(
hrhs, lv.
rhs);
577 std::vector<double>
lb;
578 lb.reserve(amgGlobalOfLocal_.size());
579 for (
int k = 0;
k < nz; ++
k)
580 for (
int j = 0;
j < ny; ++
j)
581 for (
int i = 0;
i < nx; ++
i)
582 lb.push_back((
double)
hrhs((
long)(
i +
G) + (
long)(
j +
G) * ex + (
long)(
k +
G) * ex * ey));
584 std::vector<double> z((std::size_t)std::max(amgGlobalN_, 1), 0.0);
586#ifdef PECLET_FLOW_MPI
591 std::vector<int>
ggid;
592 std::vector<double>
gb;
596 std::vector<double> b((std::size_t)amgGlobalN_, 0.0);
597 for (std::size_t r = 0; r <
ggid.size(); ++r)
598 b[(std::size_t)
ggid[r]] =
gb[r];
605 std::vector<double> b(
lb.begin(),
lb.end());
610 auto hx = Kokkos::create_mirror_view(lv.
x);
611 Kokkos::deep_copy(
hx, 0.0);
613 for (
int k = 0;
k < nz; ++
k)
614 for (
int j = 0;
j < ny; ++
j)
615 for (
int i = 0;
i < nx; ++
i)
616 hx((
long)(
i +
G) + (
long)(
j +
G) * ex + (
long)(
k +
G) * ex * ey) =
617 z[(std::size_t)amgGlobalOfLocal_[c++]];
618 Kokkos::deep_copy(lv.
x,
hx);
626 void pcgAmg(std::vector<double>& b, std::vector<double>& x) {
627 const std::size_t n = (std::size_t)amgGlobalN_;
628 auto meanZero = [&](std::vector<double>& v) {
640 std::vector<double> r = b, z(n), p(n),
Ap(n);
644 auto dot = [&](
const std::vector<double>&
a,
const std::vector<double>& c) {
646 for (std::size_t
i = 0;
i < n; ++
i)
650 double rz =
dot(r, z),
r0 = std::sqrt(
dot(r, r));
654 for (std::size_t
i = 0;
i < n; ++
i) {
658 if (std::sqrt(
dot(r, r)) <= 1e-10 *
r0)
662 const double rzn =
dot(r, z);
663 const double beta =
rzn /
rz;
665 for (std::size_t
i = 0;
i < n; ++
i)
666 p[
i] = z[
i] + beta * p[
i];
670#ifdef PECLET_FLOW_MPI
673 int rank = 0, size = 1;
677 std::vector<int> bc(size),
bd(size, 0);
681 for (
int r = 0; r < size; ++r) {
685 all.resize((std::size_t)
tot /
sizeof(
T));
696#ifdef PECLET_FLOW_MPI
715 int dims[3] = {e.x, e.y, e.z};
716 long st[3] = {1, e.x, (
long)e.x * e.y};
721 Kokkos::parallel_for(
722 "peclet::flow::mg_pfill",
726 for (
int gl = 0;
gl <
G; ++
gl) {
735 std::size_t n = y.extent(0);
736 Kokkos::parallel_for(
737 "mgaxpy", Kokkos::RangePolicy<CCExec>(
space, 0, n),
743 std::size_t n = y.extent(0);
744 Kokkos::parallel_for(
745 "mgaypx", Kokkos::RangePolicy<CCExec>(
space, 0, n),
751 std::size_t n = y.extent(0);
752 Kokkos::parallel_for(
753 "mgscale", Kokkos::RangePolicy<CCExec>(
space, 0, n),
759 std::size_t n = out.extent(0);
760 Kokkos::parallel_for(
761 "mglin", Kokkos::RangePolicy<CCExec>(
space, 0, n),
769 std::size_t n = f.extent(0);
770 Kokkos::parallel_for(
772 if (!(
ac(
i) > 1e-30f))
787 const std::size_t n =
l0.n;
788 CCField v(
"ev_v", n), w(
"ev_w", n), z(
"ev_z", n),
srhs(
"ev_srhs", n);
799 Kokkos::deep_copy(
l0.rhs, w);
800 Kokkos::deep_copy(
l0.x, 0.0);
802 Kokkos::deep_copy(out,
l0.x);
807 double nr = std::sqrt(
dot(
l0, x, x));
812 Kokkos::deep_copy(x,
srhs);
819 for (
int k = 0;
k < iters; ++
k) {
822 Kokkos::deep_copy(v, z);
827 for (
int k = 0;
k < iters; ++
k) {
831 Kokkos::deep_copy(v, z);
846 double a,
double bnd) {
851 const std::size_t n =
l0.n;
859 CCField r(
"cb_r", n), z(
"cb_z", n), d(
"cb_d", n), w(
"cb_w", n);
867 Kokkos::deep_copy(
l0.rhs,
rr);
868 Kokkos::deep_copy(
l0.x, 0.0);
870 Kokkos::deep_copy(
zz,
l0.x);
873 double rho = 1.0 /
sigma1;
875 Kokkos::deep_copy(r, b);
883 lin(d, 1.0 / theta, z, 0.0, z);
909 Kokkos::parallel_reduce(
912 {e.x -
G, e.y -
G, e.z -
G}),
914 const long i = (
long)x + (
long)y * e.x + (
long)z * (
long)e.x * e.y;
919 return allreduce(
s, MPI_SUM_);
927 Kokkos::parallel_reduce(
930 {e.x -
G, e.y -
G, e.z -
G}),
932 const long i = (
long)x + (
long)y * e.x + (
long)z * (
long)e.x * e.y;
933 if (
ac(
i) > 1e-30f) {
934 const double v = Kokkos::fabs(
aa(
i));
939 Kokkos::Max<double>(m));
940 return allreduce(m, MPI_MAX_);
951 Kokkos::parallel_reduce(
954 {e.x -
G, e.y -
G, e.z -
G}),
956 const long i = (
long)x + (
long)y * e.x + (
long)z * (
long)e.x * e.y;
957 if (
ac(
i) > 1e-30f) {
963 sum = allreduce(sum, MPI_SUM_);
968 Kokkos::parallel_for(
971 {e.x -
G, e.y -
G, e.z -
G}),
973 const long i = (
long)x + (
long)y * e.x + (
long)z * (
long)e.x * e.y;
980 enum AllOp { kSum, kMax };
983 double allreduce(
double v, AllOp
op) {
984#ifdef PECLET_FLOW_MPI
994 static constexpr AllOp MPI_SUM_ = kSum, MPI_MAX_ = kMax;
996 std::vector<Level> lv_;
997 int pre_ = 2, post_ = 2, bottom_ = 4;
998 int bc_[6] = {0, 0, 0, 0, 0, 0};
999 bool hasBC_ =
false, removeMean_ =
true, hasOutflow_ =
false;
1000 bool distributed_ =
false;
1009 bool useGraphAmgBottom_ =
false;
1010 int gnxF_ = 0, gnyF_ = 0, gnzF_ = 0;
1011 mutable std::shared_ptr<peclet::core::solver::GraphAMG>
1013 mutable peclet::core::solver::HostCsrOp
1015 mutable std::vector<int> amgOwnerCount_;
1016 mutable std::vector<int>
1018 mutable int amgGlobalN_ = 0;
1019#ifdef PECLET_FLOW_MPI
1027 useGraphAmgBottom_ =
on;
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 applyOutflowGhost(C3 ext, CCField x)
void aypx(CCField y, double a, CCField x)
void fillAxis(Level &lv, CCField f, int axis)
void fillOpenness(Level &lv)
void setGraphAmgBottom(bool on)
void vcycle(int L, bool sym)
void setOpenness(CCConst ox, CCConst oy, CCConst oz, double idx2, double idy2, double idz2)
int solvePCG(CCField b, CCField x, CCField r, CCField p, CCField z, CCField Ap, int maxit, double rtol, int pre, int post, int bottom)
void pcgAmg(std::vector< double > &b, std::vector< double > &x)
void setBoundaryConditions(const int bc[6])
double maxabs(Level &lv, CCField a)
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)
void graphAmgSolveBottom(Level &lv)
void smooth(Level &lv, int sweeps, bool reverse)
void init(int nx, int ny, int nz, int nLevels)
void estimateEigenvalues(CCConst seed, double &lmin, double &lmax, int iters, int pre, int post, int bottom)
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)
Kokkos::View< const MReal *, CCMem > FPC
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 g, C3 cinner, C3 ratio)
void prolongAdd(CCField fine, CCConst coarse, C3 fext, C3 cext, int g, C3 finner, 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 applyCutcellOp(CCField y, CCConst x, OpV AC, OpV AW, OpV AE, OpV AS, OpV AN, OpV AB, OpV AT, C3 e, int g)
void restrictAvg(CCField coarse, CCConst fine, C3 cext, C3 fext, int g, C3 cinner, C3 ratio)
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
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
Kokkos::View< const double *, CCMem > CCConst
std::unique_ptr< GridHalo< double > > dev
std::unique_ptr< GridHaloTopology< kDim > > halo
static constexpr double AC
static constexpr double F