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