flow 0.4.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
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) {
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
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 }
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)
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)
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