flow
Kokkos cut-cell IBM incompressible Navier-Stokes solver + pnm pore extraction
Loading...
Searching...
No Matches
mac_mg.hpp
Go to the documentation of this file.
1
9#ifndef PECLET_FLOW_MAC_MG_HPP
10#define PECLET_FLOW_MAC_MG_HPP
11
12#include <Kokkos_Core.hpp>
13#include <vector>
14
15#include "mac_stencils.hpp" // SField/SConst, I3, L3
16#include "mac_transfer.hpp" // restrict_, prolong, T3
17
18namespace peclet::flow {
19
20// Periodic ghost fill (g ghosts, all 3 axes) of a level field with inner size N.
21inline void mgPeriodicFill(SField f, I3 e, int N, int g) {
22 SExec space;
23 int dims[3] = {e.x, e.y, e.z};
24 long st[3] = {1, e.x, (long)e.x * e.y};
25 for (int axis = 0; axis < 3; ++axis) {
26 const int b = (axis + 1) % 3, c = (axis + 2) % 3;
27 const long sa = st[axis], sb = st[b], sc = st[c];
28 SField ff = f;
29 using MD = Kokkos::MDRangePolicy<SExec, Kokkos::Rank<2>>;
30 Kokkos::parallel_for(
31 "peclet::flow::mg_pfill", MD(space, {0, 0}, {dims[b], dims[c]}),
32 KOKKOS_LAMBDA(int p0, int p1) {
33 const long base = (long)p0 * sb + (long)p1 * sc;
34 for (int gl = 0; gl < g; ++gl) {
35 ff(base + (long)gl * sa) = ff(base + (long)(gl + N) * sa);
36 ff(base + (long)(g + N + gl) * sa) = ff(base + (long)(g + gl) * sa);
37 }
38 });
39 }
40}
41
42class MgPoisson {
43 public:
44 // Build the hierarchy: N, N/2, ... while even and >= minN. g = 1 ghost per level.
45 MgPoisson(int N, int minN = 4) {
46 int n = N;
47 double h2 = 1.0;
48 while (true) {
49 Level L;
50 L.N = n;
51 L.h2 = h2;
52 L.e = I3{n + 2 * G, n + 2 * G, n + 2 * G};
53 const std::size_t ne = (std::size_t)L.e.x * L.e.y * L.e.z;
54 L.phi = SField("mg_phi", ne);
55 L.f = SField("mg_f", ne);
56 L.r = SField("mg_r", ne);
57 lv_.push_back(L);
58 if (n % 2 != 0 || n / 2 < minN)
59 break;
60 n /= 2;
61 h2 *= 4.0;
62 }
63 }
64
65 int numLevels() const { return (int)lv_.size(); }
66
67 // Solve Lap(phi)=d on the finest grid (in/out phi, rhs d). A = -Lap, so f0 = -d.
68 void solve(SField phi, SConst d, int nVcycles, int nu1 = 2, int nu2 = 2) {
69 // f0 = -d
70 SExec space;
71 SField f0 = lv_[0].f;
72 Kokkos::parallel_for(
73 "peclet::flow::mg_negd", Kokkos::RangePolicy<SExec>(space, 0, f0.extent(0)),
74 KOKKOS_LAMBDA(std::size_t i) { f0(i) = -d(i); });
75
76 Kokkos::deep_copy(lv_[0].phi, phi);
77 for (int v = 0; v < nVcycles; ++v) {
78 vcycle(0, nu1, nu2);
79 removeMean(lv_[0].phi, lv_[0]);
80 }
81 Kokkos::deep_copy(phi, lv_[0].phi);
82 }
83
84 // Residual norm of the finest level's current solve (max|f - A phi|), for diagnostics.
87 return maxAbsInner(lv_[0].r, lv_[0]);
88 }
89
90 // Public so the Kokkos launch-compiler stubs (and the extended-lambda enclosing methods below)
91 // can name these; the V-cycle internals are not meant for external use.
92 static constexpr int G = 1;
93 struct Level {
94 int N;
96 double h2;
98 };
99 std::vector<Level> lv_;
100
101 // RB-GS sweep: phi = (sum_nbr + h2*f) / 6, both colours; A = -Lap = (6 phi - sum)/h2.
102 void smoothColor(Level& L, int color) {
103 SExec space;
104 const I3 e = L.e;
105 const double h2 = L.h2;
106 SField phi = L.phi, f = L.f;
107 using MD = Kokkos::MDRangePolicy<SExec, Kokkos::Rank<3>>;
108 Kokkos::parallel_for(
109 "peclet::flow::mg_smooth", MD(space, {G, G, G}, {e.x - G, e.y - G, e.z - G}),
110 KOKKOS_LAMBDA(int x, int y, int z) {
111 if (((x + y + z) & 1) != color)
112 return;
113 const long i = L3(x, y, z, e), sx = 1, sy = e.x, sz = (long)e.x * e.y;
114 const double s =
115 phi(i + sx) + phi(i - sx) + phi(i + sy) + phi(i - sy) + phi(i + sz) + phi(i - sz);
116 phi(i) = (s + h2 * f(i)) / 6.0;
117 });
118 }
119 void smooth(Level& L, int sweeps) {
120 for (int it = 0; it < sweeps; ++it) {
121 mgPeriodicFill(L.phi, L.e, L.N, G);
122 smoothColor(L, 0);
123 mgPeriodicFill(L.phi, L.e, L.N, G);
124 smoothColor(L, 1);
125 }
126 }
127 void computeResidual(int l) {
128 Level& L = lv_[l];
129 mgPeriodicFill(L.phi, L.e, L.N, G);
130 SExec space;
131 const I3 e = L.e;
132 const double h2 = L.h2;
133 SField phi = L.phi, f = L.f, r = L.r;
134 using MD = Kokkos::MDRangePolicy<SExec, Kokkos::Rank<3>>;
135 Kokkos::parallel_for(
136 "peclet::flow::mg_resid", MD(space, {G, G, G}, {e.x - G, e.y - G, e.z - G}),
137 KOKKOS_LAMBDA(int x, int y, int z) {
138 const long i = L3(x, y, z, e), sx = 1, sy = e.x, sz = (long)e.x * e.y;
139 const double s =
140 phi(i + sx) + phi(i - sx) + phi(i + sy) + phi(i - sy) + phi(i + sz) + phi(i - sz);
141 r(i) = f(i) - (6.0 * phi(i) - s) / h2; // f - A phi
142 });
143 }
144 void vcycle(int l, int nu1, int nu2) {
145 Level& L = lv_[l];
146 if (l == (int)lv_.size() - 1) {
147 smooth(L, 40);
148 return;
149 } // coarsest: smooth hard
150 smooth(L, nu1);
152 // restrict r_l -> f_{l+1}; zero coarse correction
153 Level& C = lv_[l + 1];
154 Kokkos::deep_copy(C.phi, 0.0);
155 restrict_(C.f, SConst(L.r), T3{C.e.x, C.e.y, C.e.z}, T3{L.e.x, L.e.y, L.e.z}, G,
156 T3{C.N, C.N, C.N}, T3{2, 2, 2});
157 vcycle(l + 1, nu1, nu2);
158 // prolong correction back (needs coarse ghosts)
159 mgPeriodicFill(C.phi, C.e, C.N, G);
160 prolong(L.phi, SConst(C.phi), T3{L.e.x, L.e.y, L.e.z}, T3{C.e.x, C.e.y, C.e.z}, G,
161 T3{L.N, L.N, L.N}, T3{2, 2, 2});
162 smooth(L, nu2);
163 }
164 void removeMean(SField f, Level& L) {
165 SExec space;
166 const I3 e = L.e;
167 const int N = L.N;
168 double sum = 0;
169 Kokkos::parallel_reduce(
170 "peclet::flow::mg_mean", Kokkos::RangePolicy<SExec>(space, 0, (long)N * N * N),
171 KOKKOS_LAMBDA(long c, double& acc) {
172 const int ix = (int)(c % N), iy = (int)((c / N) % N), iz = (int)(c / ((long)N * N));
173 acc += f(L3(ix + G, iy + G, iz + G, e));
174 },
175 sum);
176 const double mean = sum / ((double)N * N * N);
177 Kokkos::parallel_for(
178 "peclet::flow::mg_submean", Kokkos::RangePolicy<SExec>(space, 0, f.extent(0)),
179 KOKKOS_LAMBDA(std::size_t i) { f(i) -= mean; });
180 }
181 double maxAbsInner(SField f, Level& L) {
182 SExec space;
183 const I3 e = L.e;
184 const int N = L.N;
185 double m = 0;
186 Kokkos::parallel_reduce(
187 "peclet::flow::mg_maxabs", Kokkos::RangePolicy<SExec>(space, 0, (long)N * N * N),
188 KOKKOS_LAMBDA(long c, double& acc) {
189 const int ix = (int)(c % N), iy = (int)((c / N) % N), iz = (int)(c / ((long)N * N));
190 double v = Kokkos::fabs(f(L3(ix + G, iy + G, iz + G, e)));
191 if (v > acc)
192 acc = v;
193 },
194 Kokkos::Max<double>(m));
195 return m;
196 }
197};
198
199} // namespace peclet::flow
200
201#endif // PECLET_FLOW_MAC_MG_HPP
double finestResidualMax()
Definition mac_mg.hpp:85
double maxAbsInner(SField f, Level &L)
Definition mac_mg.hpp:181
MgPoisson(int N, int minN=4)
Definition mac_mg.hpp:45
void smoothColor(Level &L, int color)
Definition mac_mg.hpp:102
void vcycle(int l, int nu1, int nu2)
Definition mac_mg.hpp:144
std::vector< Level > lv_
Definition mac_mg.hpp:99
void removeMean(SField f, Level &L)
Definition mac_mg.hpp:164
int numLevels() const
Definition mac_mg.hpp:65
void computeResidual(int l)
Definition mac_mg.hpp:127
void smooth(Level &L, int sweeps)
Definition mac_mg.hpp:119
static constexpr int G
Definition mac_mg.hpp:92
void solve(SField phi, SConst d, int nVcycles, int nu1=2, int nu2=2)
Definition mac_mg.hpp:68
flow — portable (Kokkos) MAC stencil operators: Red-Black Gauss-Seidel smoothers + divergence.
flow — portable (Kokkos) multigrid transfer operators + projection velocity correction.
long L3(int x, int y, int z, I3 e)
void restrict_(TField coarse, TConst fine, T3 cext, T3 fext, int g, T3 cinner, T3 ratio)
void prolong(TField fine, TConst coarse, T3 fext, T3 cext, int g, T3 finner, T3 ratio)
Kokkos::View< const double *, SMem > SConst
void mgPeriodicFill(SField f, I3 e, int N, int g)
Definition mac_mg.hpp:21
Kokkos::DefaultExecutionSpace SExec
Kokkos::View< double *, SMem > SField
static constexpr int N