flow 1.3.0
Kokkos cut-cell IBM incompressible Navier-Stokes solver + pnm pore extraction
Loading...
Searching...
No Matches
test_stencils.cpp
Go to the documentation of this file.
1// Correctness of the Kokkos MAC stencils (peclet::flow::poisSweep RB-GS + divergence) vs a host
2// replication, plus a smoother sanity check (Poisson residual decreases over sweeps). Extended
3// (inner+ghost=1) block, x-fastest. Within a Red-Black colour the updates are independent, so
4// device (parallel) and host (sequential, colour 0 then 1) match exactly. Runs on whatever backend
5// Kokkos was built for.
6#include <cmath>
7#include <cstdio>
8#include <Kokkos_Core.hpp>
9#include <random>
10#include <vector>
11
12#include "cut_cell_ibm.hpp"
13#include "mac_stencils.hpp"
14#include "mac_velocity_mg.hpp"
15
16using namespace peclet::flow;
17
18static long l3(int x, int y, int z, I3 e) {
19 return (long)x + (long)y * e.x + (long)z * (long)e.x * e.y;
20}
21
22int main(int argc, char** argv) {
23 Kokkos::initialize(argc, argv);
24 int status = 0;
25 {
26 const int g = 1;
27 I3 inner{18, 14, 10};
28 I3 e{inner.x + 2 * g, inner.y + 2 * g, inner.z + 2 * g};
29 I3 og{0, 0, 0}; // single block: global origin 0
30 const std::size_t n = (std::size_t)e.x * e.y * e.z;
31
32 std::mt19937 rng(8);
33 std::uniform_real_distribution<double> uf(-1.0, 1.0);
34 std::vector<double> hphi(n), hd(n), hu(n), hv(n), hw(n);
35 for (std::size_t i = 0; i < n; ++i) {
36 hphi[i] = uf(rng);
37 hd[i] = uf(rng);
38 hu[i] = uf(rng);
39 hv[i] = uf(rng);
40 hw[i] = uf(rng);
41 }
42
43 auto up = [&](const char* nm, std::vector<double>& h) {
44 SField v(nm, n);
45 auto m = Kokkos::create_mirror_view(v);
46 for (std::size_t i = 0; i < n; ++i)
47 m(i) = h[i];
48 Kokkos::deep_copy(v, m);
49 return v;
50 };
51 SField phi = up("phi", hphi), d = up("d", hd), u = up("u", hu), v = up("v", hv),
52 w = up("w", hw);
53 SField dout("dout", n);
54
55 // device: one RB-GS sweep + divergence
56 poisSweep(phi, d, e, og, g);
57 divergence(u, v, w, dout, e, g);
58 auto hp = Kokkos::create_mirror_view(phi);
59 Kokkos::deep_copy(hp, phi);
60 auto hdo = Kokkos::create_mirror_view(dout);
61 Kokkos::deep_copy(hdo, dout);
62
63 // host reference: identical RB-GS sweep (colour 0 then 1) + divergence
64 std::vector<double> rphi = hphi;
65 for (int color = 0; color < 2; ++color)
66 for (int z = g; z < e.z - g; ++z)
67 for (int y = g; y < e.y - g; ++y)
68 for (int x = g; x < e.x - g; ++x) {
69 if ((((x + og.x) + (y + og.y) + (z + og.z)) & 1) != color)
70 continue;
71 long i = l3(x, y, z, e), sx = 1, sy = e.x, sz = (long)e.x * e.y;
72 double s = rphi[i + sx] + rphi[i - sx] + rphi[i + sy] + rphi[i - sy] + rphi[i + sz] +
73 rphi[i - sz];
74 rphi[i] = (s - hd[i]) / 6.0;
75 }
76 auto close = [](double a, double b) {
77 return std::fabs(a - b) <= 1e-10 * (1.0 + std::fabs(b));
78 };
79 int bad = 0;
80 for (std::size_t i = 0; i < n; ++i)
81 if (!close(hp(i), rphi[i]))
82 ++bad;
83 for (int z = g; z < e.z - g; ++z)
84 for (int y = g; y < e.y - g; ++y)
85 for (int x = g; x < e.x - g; ++x) {
86 long i = l3(x, y, z, e), sx = 1, sy = e.x, sz = (long)e.x * e.y;
87 double rd = (hu[i + sx] - hu[i]) + (hv[i + sy] - hv[i]) + (hw[i + sz] - hw[i]);
88 if (!close(hdo(i), rd))
89 ++bad;
90 }
91 if (bad) {
92 std::fprintf(stderr, "FAIL: %d cells differ (sweep/divergence)\n", bad);
93 status = 1;
94 }
95
96 // smoother sanity: many sweeps on Lap(phi)=d (periodic-ish, ghosts left as data) -> residual
97 // drops.
98 SField phi2 = up("phi2", hphi);
99 auto resid = [&]() {
100 auto m = Kokkos::create_mirror_view(phi2);
101 Kokkos::deep_copy(m, phi2);
102 double r = 0.0;
103 for (int z = g; z < e.z - g; ++z)
104 for (int y = g; y < e.y - g; ++y)
105 for (int x = g; x < e.x - g; ++x) {
106 long i = l3(x, y, z, e), sx = 1, sy = e.x, sz = (long)e.x * e.y;
107 double s = m(i + sx) + m(i - sx) + m(i + sy) + m(i - sy) + m(i + sz) + m(i - sz);
108 double res = (s - 6.0 * m(i)) - hd[i]; // Lap(phi)-d
109 r += res * res;
110 }
111 return std::sqrt(r);
112 };
113 double r0 = resid();
114 for (int it = 0; it < 100; ++it)
115 poisSweep(phi2, d, e, og, g);
116 double r1 = resid();
117 if (!(r1 < 0.2 * r0)) {
118 std::fprintf(stderr, "FAIL: smoother did not reduce residual (%.3e -> %.3e)\n", r0, r1);
119 status = 1;
120 }
121
122 if (!status)
123 std::printf(
124 "[mac_stencils] PASS: RB-GS sweep + divergence match host; residual %.2e -> %.2e (exec: "
125 "%s)\n",
126 r0, r1, SExec::name());
127 }
128 // ------------------------------------------------------------------------------------------
129 // Phase 2 commit C1 (doc/anisotropic_metric.md §2, §9 row C1): the per-axis viscous fold.
130 // A_C = idiag + 2*((bx + by) + bz), AW = AE = -bx, AS = AN = -by, AB = AT = -bz
131 // in EXACTLY that association order, for BOTH assemblers of the constant-coefficient momentum
132 // operator: `ibmBuildDiffusion` (the fine stencil) and `buildConstAniso` (the velocity-MG
133 // levels). Two statements are gated:
134 // (a) an anisotropic metric w = (1, 4, 1/4) produces exactly those per-axis bands;
135 // (b) at w = (1,1,1) the diagonal is BITWISE the pre-Phase-2 expression `idiag + 6.0*beta`
136 // — (beta+beta) is exact, +beta rounds once, and the *2 is exact, so 2*fl(3 beta) ==
137 // fl(6 beta). That is the whole isotropic-identity argument, checked at kernel level.
138 {
140 using peclet::flow::C3;
141 using peclet::flow::FPV;
144
145 const int ex = 6, ey = 5, ez = 4;
146 const std::size_t n = (std::size_t)ex * ey * ez;
147 const C3 e3{ex, ey, ez};
148 const double mu = 0.1, idiag = 1.0 / 50.0;
149
150 auto bands = [&](FPV AC, FPV AW, FPV AE, FPV AS, FPV AN, FPV AB, FPV AT, MReal out[7]) {
151 FPV all[7] = {AC, AW, AE, AS, AN, AB, AT};
152 for (int k = 0; k < 7; ++k) {
153 auto m = Kokkos::create_mirror_view(all[k]);
154 Kokkos::deep_copy(m, all[k]);
155 out[k] = m(n / 2);
156 }
157 };
158 FPV AC("AC", n), AW("AW", n), AE("AE", n), AS("AS", n), AN("AN", n), AB("AB", n), AT("AT", n);
159
160 // (a) anisotropic metric
161 {
162 const double w[3] = {1.0, 4.0, 0.25};
163 const double bx = mu * w[0], by = mu * w[1], bz = mu * w[2];
164 const MReal wantC = (MReal)(idiag + 2.0 * ((bx + by) + bz));
165 const MReal wantX = (MReal)(-bx), wantY = (MReal)(-by), wantZ = (MReal)(-bz);
166 const MReal want[7] = {wantC, wantX, wantX, wantY, wantY, wantZ, wantZ};
167 const char* nm[7] = {"AC", "AW", "AE", "AS", "AN", "AB", "AT"};
168 const char* who[2] = {"ibmBuildDiffusion", "buildConstAniso"};
169 for (int k = 0; k < 2; ++k) {
170 if (k == 0)
171 ibmBuildDiffusion(AC, AW, AE, AS, AN, AB, AT, ex, ey, ez, bx, by, bz, idiag);
172 else
173 buildConstAniso(AC, AW, AE, AS, AN, AB, AT, e3, bx, by, bz, idiag);
174 MReal got[7];
175 bands(AC, AW, AE, AS, AN, AB, AT, got);
176 for (int b = 0; b < 7; ++b)
177 if (!(got[b] == want[b])) {
178 std::fprintf(stderr, "FAIL: %s aniso %s = %.17g, want %.17g\n", who[k], nm[b],
179 (double)got[b], (double)want[b]);
180 status = 1;
181 }
182 if (!status)
183 std::printf(
184 "[aniso_fold] %-17s w=(1,4,0.25): AC %.17g AW/AE %.17g AS/AN %.17g AB/AT %.17g\n",
185 who[k], (double)got[0], (double)got[1], (double)got[3], (double)got[5]);
186 }
187 }
188
189 // (b) isotropic metric -> bitwise the pre-Phase-2 expression
190 {
191 const double beta = mu;
192 const MReal legacyC = (MReal)(idiag + 6.0 * beta), legacyO = (MReal)(-beta);
193 const MReal want[7] = {legacyC, legacyO, legacyO, legacyO, legacyO, legacyO, legacyO};
194 const char* nm[7] = {"AC", "AW", "AE", "AS", "AN", "AB", "AT"};
195 const char* who[2] = {"ibmBuildDiffusion", "buildConstAniso"};
196 for (int k = 0; k < 2; ++k) {
197 if (k == 0)
198 ibmBuildDiffusion(AC, AW, AE, AS, AN, AB, AT, ex, ey, ez, beta, beta, beta, idiag);
199 else
200 buildConstAniso(AC, AW, AE, AS, AN, AB, AT, e3, beta, beta, beta, idiag);
201 MReal got[7];
202 bands(AC, AW, AE, AS, AN, AB, AT, got);
203 for (int b = 0; b < 7; ++b)
204 if (!(got[b] == want[b])) {
205 std::fprintf(stderr, "FAIL: %s isotropic %s = %.17g, want (legacy) %.17g\n", who[k],
206 nm[b], (double)got[b], (double)want[b]);
207 status = 1;
208 }
209 if (!status)
210 std::printf("[aniso_fold] %-17s w=(1,1,1): AC %.17g == idiag+6*beta %.17g (bitwise)\n",
211 who[k], (double)got[0], (double)legacyC);
212 }
213 }
214 if (!status)
215 std::printf("[aniso_fold] PASS: per-axis viscous fold; isotropic == legacy bitwise\n");
216 }
217
218 Kokkos::finalize();
219 return status;
220}
flow — portable (Kokkos) Robust-Scaled cut-cell IBM primitives + per-cut-cell overlay build.
int main()
Definition dbg_scene.cpp:13
flow — portable (Kokkos) MAC stencil operators: Red-Black Gauss-Seidel smoothers + divergence.
flow — portable (Kokkos) velocity (momentum) multigrid for the IBM diffusion solve: the STAIRCASE coa...
void buildConstAniso(FPV AC, FPV AW, FPV AE, FPV AS, FPV AN, FPV AB, FPV AT, C3 e, double bx, double by, double bz, double idiag)
void divergence(SConst u, SConst v, SConst w, SField d, I3 e, int g)
Kokkos::View< MReal *, CCMem > FPV
void ibmBuildDiffusion(MV AC, MV AW, MV AE, MV AS, MV AN, MV AB, MV AT, int ex, int ey, int ez, double bx, double by, double bz, double idiag)
Kokkos::View< double *, SMem > SField
void poisSweep(SField phi, SConst d, I3 e, I3 og, int g)
static constexpr double AC
static long l3(int x, int y, int z, I3 e)