flow 1.3.0
Kokkos cut-cell IBM incompressible Navier-Stokes solver + pnm pore extraction
Loading...
Searching...
No Matches
test_bottom_direct.cpp
Go to the documentation of this file.
1
40#include <cmath>
41#include <cstdio>
42#include <cstring>
43#include <Kokkos_Core.hpp>
44#include <random>
45#include <type_traits>
46#include <vector>
47
48#include "mac_cutcell_mg.hpp"
49
50using namespace peclet::flow;
51
52namespace {
53
54constexpr bool kHost = std::is_same_v<CCMem, Kokkos::HostSpace>;
55
56std::vector<double> toHost(CCField f) {
57 auto h = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), f);
58 return std::vector<double>(h.data(), h.data() + h.extent(0));
59}
60std::vector<double> toHostOp(FPV f) {
61 auto h = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), f);
62 std::vector<double> v(h.extent(0));
63 for (std::size_t i = 0; i < v.size(); ++i)
64 v[i] = (double)h(i);
65 return v;
66}
67CCField toDevice(const std::vector<double>& v, const char* name) {
68 CCField f(name, v.size());
69 auto h = Kokkos::create_mirror_view(f);
70 for (std::size_t i = 0; i < v.size(); ++i)
71 h(i) = v[i];
72 Kokkos::deep_copy(f, h);
73 return f;
74}
75template <class V>
76bool sameBits(const V& a, const V& b) {
77 auto ha = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), a);
78 auto hb = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), b);
79 return ha.span() == hb.span() &&
80 std::memcmp(ha.data(), hb.data(), ha.span() * sizeof(typename V::value_type)) == 0;
81}
82
83// The bottom level on the host: its face form, labels (union-find, an independent check of the
84// device ones), the re-summed diagonal and the scaling of §13.4.1, and the planes.
85struct HostBottom {
86 C3 e{0, 0, 0}, n{0, 0, 0};
87 int bc[6];
88 std::vector<double> AC, AX, AY, AZ;
89 std::vector<int> comp; // ext-indexed, -1 = solid
90 int nc = 0;
91 std::vector<double> d, s; // ext-indexed
92 long idx(int x, int y, int z) const { // 0-based inner coordinates
93 return (long)(x + 1) + (long)(y + 1) * e.x + (long)(z + 1) * e.x * e.y;
94 }
95 // face f6 of inner cell c: false if it crosses a non-periodic domain face
96 bool face(const int c[3], int f6, int nb[3], double& af) const {
97 const int a = f6 >> 1, side = f6 & 1, nn[3] = {n.x, n.y, n.z};
98 nb[0] = c[0];
99 nb[1] = c[1];
100 nb[2] = c[2];
101 int v = c[a] + (side ? 1 : -1);
102 if (v < 0 || v >= nn[a]) {
103 if (bc[f6] != 0)
104 return false;
105 v = (v + nn[a]) % nn[a];
106 }
107 nb[a] = v;
108 const long i = idx(c[0], c[1], c[2]);
109 const long st = (a == 0) ? 1 : (a == 1 ? e.x : (long)e.x * e.y);
110 const std::vector<double>& F = (a == 0) ? AX : (a == 1 ? AY : AZ);
111 af = F[side ? i + st : i];
112 return true;
113 }
114 template <class Fn>
115 void forCells(Fn fn) const {
116 for (int z = 0; z < n.z; ++z)
117 for (int y = 0; y < n.y; ++y)
118 for (int x = 0; x < n.x; ++x) {
119 const int c[3] = {x, y, z};
120 fn(c, idx(x, y, z));
121 }
122 }
123 void build() {
124 std::vector<long> parent(AC.size());
125 for (std::size_t i = 0; i < parent.size(); ++i)
126 parent[i] = (long)i;
127 auto find = [&](long a) {
128 while (parent[a] != a)
129 a = parent[a] = parent[parent[a]];
130 return a;
131 };
132 forCells([&](const int c[3], long i) {
133 if (!(AC[i] > 1e-30))
134 return;
135 for (int f6 = 0; f6 < 6; ++f6) {
136 int nb[3];
137 double af;
138 if (face(c, f6, nb, af) && af < 0.0) {
139 const long j = idx(nb[0], nb[1], nb[2]);
140 if (AC[j] > 1e-30)
141 parent[find(i)] = find(j);
142 }
143 }
144 });
145 comp.assign(AC.size(), -1);
146 std::vector<int> id(AC.size(), -1);
147 nc = 0;
148 forCells([&](const int*, long i) {
149 if (!(AC[i] > 1e-30))
150 return;
151 const long r = find(i);
152 if (id[r] < 0)
153 id[r] = nc++;
154 comp[i] = id[r];
155 });
156 d.assign(AC.size(), 0.0);
157 s.assign(AC.size(), 0.0);
158 forCells([&](const int c[3], long i) {
159 double sum = 0.0;
160 for (int f6 = 0; f6 < 6; ++f6) {
161 int nb[3];
162 double af;
163 if (face(c, f6, nb, af))
164 sum += af;
165 }
166 d[i] = -sum;
167 s[i] = (comp[i] >= 0 && d[i] > 0.0) ? 1.0 / std::sqrt(d[i]) : 0.0;
168 });
169 }
170 // (A x)_i with the re-summed diagonal (the matrix M factors), fluid rows
171 double applyResummed(const std::vector<double>& x, const int c[3], long i) const {
172 double v = d[i] * x[i];
173 for (int f6 = 0; f6 < 6; ++f6) {
174 int nb[3];
175 double af;
176 if (face(c, f6, nb, af))
177 v += af * x[idx(nb[0], nb[1], nb[2])];
178 }
179 return v;
180 }
181 // (A x)_i with the STORED operator, read the way the device kernels read it (wrapped indices)
182 double applyStored(const std::vector<double>& x, const int c[3], long i) const {
183 const int nn[3] = {n.x, n.y, n.z};
184 auto at = [&](int dx, int dy, int dz) {
185 const int q[3] = {(c[0] + dx + nn[0]) % nn[0], (c[1] + dy + nn[1]) % nn[1],
186 (c[2] + dz + nn[2]) % nn[2]};
187 return x[idx(q[0], q[1], q[2])];
188 };
189 const long sy = e.x, sz = (long)e.x * e.y;
190 return AC[i] * x[i] + AX[i + 1] * at(1, 0, 0) + AX[i] * at(-1, 0, 0) +
191 AY[i + sy] * at(0, 1, 0) + AY[i] * at(0, -1, 0) + AZ[i + sz] * at(0, 0, 1) +
192 AZ[i] * at(0, 0, -1);
193 }
194 // per-component means of x (long double) -> max |mean| ; also max |x| on fluid, max |x| solid
195 void means(const std::vector<double>& x, double& meanMax, double& xmax, double& xsolid) const {
196 std::vector<long double> sum(nc, 0.0L);
197 std::vector<long> cnt(nc, 0);
198 xmax = xsolid = meanMax = 0.0;
199 forCells([&](const int*, long i) {
200 if (comp[i] >= 0) {
201 sum[comp[i]] += (long double)x[i];
202 ++cnt[comp[i]];
203 xmax = std::max(xmax, std::fabs(x[i]));
204 } else {
205 xsolid = std::max(xsolid, std::fabs(x[i]));
206 }
207 });
208 for (int c = 0; c < nc; ++c)
209 meanMax = std::max(meanMax, (double)std::fabs(sum[c] / (long double)cnt[c]));
210 }
211};
212
213// A compatible rhs: random on the fluid cells, each component's mean removed (long double), 0 on
214// solids and ghosts.
215std::vector<double> compatibleRhs(const HostBottom& H, unsigned seed) {
216 std::vector<double> v(H.AC.size(), 0.0);
217 std::mt19937 rng(seed);
218 std::uniform_real_distribution<double> U(-1.0, 1.0);
219 std::vector<long double> sum(H.nc, 0.0L);
220 std::vector<long> cnt(H.nc, 0);
221 H.forCells([&](const int*, long i) {
222 const double r = U(rng);
223 if (H.comp[i] >= 0) {
224 v[i] = r;
225 sum[H.comp[i]] += r;
226 ++cnt[H.comp[i]];
227 }
228 });
229 H.forCells([&](const int*, long i) {
230 if (H.comp[i] >= 0)
231 v[i] = (double)((long double)v[i] - sum[H.comp[i]] / (long double)cnt[H.comp[i]]);
232 });
233 return v;
234}
235
236double residualResummed(const HostBottom& H, const std::vector<double>& x,
237 const std::vector<double>& r) {
238 double rn = 0.0, r0 = 0.0;
239 H.forCells([&](const int c[3], long i) {
240 if (H.comp[i] < 0)
241 return;
242 r0 = std::max(r0, std::fabs(r[i]));
243 rn = std::max(rn, std::fabs(H.applyResummed(x, c, i) - r[i]));
244 });
245 return rn / r0;
246}
247
248// x_ref = A'^{-1} r on the host in FP64: the scaled augmented matrix A~' of §13.4.1-2 assembled
249// densely from the host copies (planes from the solver; labels, k_c, m_c host-computed), a dense
250// Cholesky, x = S y, then each component's mean removed (long double).
251std::vector<double> denseReference(const HostBottom& H, const BottomPlanes& pl,
252 const std::vector<double>& r) {
253 const int nn = pl.P * pl.b;
254 auto plane = [&](const int c[3]) { return c[pl.s]; };
255 auto fidx = [&](const int c[3]) { return c[pl.s] * pl.b + c[pl.a0] + pl.n[pl.a0] * c[pl.a1]; };
256 std::vector<int> kc(H.nc, -1), mc(H.nc, 0);
257 H.forCells([&](const int c[3], long i) {
258 if (H.comp[i] >= 0)
259 kc[H.comp[i]] = std::max(kc[H.comp[i]], plane(c));
260 });
261 H.forCells([&](const int c[3], long i) {
262 if (H.comp[i] >= 0 && plane(c) == kc[H.comp[i]])
263 ++mc[H.comp[i]];
264 });
265 std::vector<double> A((std::size_t)nn * nn, 0.0), rhs(nn, 0.0);
266 std::vector<long> ext(nn);
267 H.forCells([&](const int c[3], long i) {
268 const int fi = fidx(c);
269 ext[fi] = i;
270 rhs[fi] = H.s[i] * r[i];
271 A[(std::size_t)fi * nn + fi] += 1.0;
272 for (int f6 = 0; f6 < 6; ++f6) {
273 int nb[3];
274 double af;
275 if (H.face(c, f6, nb, af)) {
276 const long j = H.idx(nb[0], nb[1], nb[2]);
277 A[(std::size_t)fi * nn + fidx(nb)] += H.s[i] * af * H.s[j];
278 }
279 }
280 });
281 for (int fi = 0; fi < nn; ++fi)
282 for (int fj = 0; fj < nn; ++fj) {
283 const long i = ext[fi], j = ext[fj];
284 const int ci = H.comp[i];
285 if (ci >= 0 && H.comp[j] == ci && fi / pl.b == kc[ci] && fj / pl.b == kc[ci])
286 A[(std::size_t)fi * nn + fj] += 1.0 / mc[ci];
287 }
288 for (int j = 0; j < nn; ++j) { // in-place Cholesky (lower)
289 double p = A[(std::size_t)j * nn + j];
290 for (int m = 0; m < j; ++m)
291 p -= A[(std::size_t)j * nn + m] * A[(std::size_t)j * nn + m];
292 const double ljj = std::sqrt(p);
293 A[(std::size_t)j * nn + j] = ljj;
294 for (int i = j + 1; i < nn; ++i) {
295 double v = A[(std::size_t)i * nn + j];
296 for (int m = 0; m < j; ++m)
297 v -= A[(std::size_t)i * nn + m] * A[(std::size_t)j * nn + m];
298 A[(std::size_t)i * nn + j] = v / ljj;
299 }
300 }
301 for (int i = 0; i < nn; ++i) { // L y = rhs
302 double v = rhs[i];
303 for (int m = 0; m < i; ++m)
304 v -= A[(std::size_t)i * nn + m] * rhs[m];
305 rhs[i] = v / A[(std::size_t)i * nn + i];
306 }
307 for (int i = nn - 1; i >= 0; --i) { // L^T y = .
308 double v = rhs[i];
309 for (int m = i + 1; m < nn; ++m)
310 v -= A[(std::size_t)m * nn + i] * rhs[m];
311 rhs[i] = v / A[(std::size_t)i * nn + i];
312 }
313 std::vector<double> x(H.AC.size(), 0.0);
314 for (int fi = 0; fi < nn; ++fi)
315 x[ext[fi]] = (H.comp[ext[fi]] >= 0) ? H.s[ext[fi]] * rhs[fi] : 0.0;
316 std::vector<long double> sum(H.nc, 0.0L);
317 std::vector<long> cnt(H.nc, 0);
318 H.forCells([&](const int*, long i) {
319 if (H.comp[i] >= 0) {
320 sum[H.comp[i]] += x[i];
321 ++cnt[H.comp[i]];
322 }
323 });
324 H.forCells([&](const int*, long i) {
325 if (H.comp[i] >= 0)
326 x[i] = (double)((long double)x[i] - sum[H.comp[i]] / (long double)cnt[H.comp[i]]);
327 });
328 return x;
329}
330
331struct Case {
332 const char* name;
333 int bc[6];
334 int nx, ny, nz;
335 bool solids, pocket;
336};
337
338int runCase(const Case& cs) {
339 const int nx = cs.nx, ny = cs.ny, nz = cs.nz;
340 CutcellMG mg;
342 mg.init(nx, ny, nz, 2);
344 const C3 e{nx + 2, ny + 2, nz + 2};
345 const std::size_t n = (std::size_t)e.x * e.y * e.z;
346 std::vector<double> hx(n), hy(n), hz(n);
347 const double lr = std::log(50.0), k = 2.0 * M_PI;
348 for (int z = 0; z < e.z; ++z)
349 for (int y = 0; y < e.y; ++y)
350 for (int x = 0; x < e.x; ++x) {
351 const std::size_t i = x + (std::size_t)y * e.x + (std::size_t)z * e.x * e.y;
352 const double s = 0.5 + 0.5 * std::sin(k * x / nx) * std::cos(k * (y + 0.5 * z) / ny);
353 const double t = 0.5 + 0.5 * std::cos(k * (x + z) / nz);
354 hx[i] = std::exp(lr * s) / 50.0; // in [0.02, 1]: ratio 50
355 hy[i] = std::exp(lr * t) / 50.0;
356 hz[i] = std::exp(lr * (1.0 - s)) / 50.0;
357 const int ix = x - 1, iy = y - 1, iz = z - 1; // the low face of fine cell (ix, iy, iz)
358 if (cs.solids) { // the B1b sheet: coarse x = 3 solid, coarse faces x = 0 and 8 closed
359 if (ix == 0 || ix == 16 || ix == 6 || ix == 8)
360 hx[i] = 0.0;
361 if (ix == 6 || ix == 7)
362 hy[i] = hz[i] = 0.0;
363 }
364 if (cs.pocket) { // fine box [20,24) x [10,12) x [6,8) = coarse cells (10..11, 5, 3)
365 const bool inX = ix >= 20 && ix < 24, inY = iy >= 10 && iy < 12, inZ = iz >= 6 && iz < 8;
366 if ((ix == 20 || ix == 24) && inY && inZ)
367 hx[i] = 0.0;
368 if ((iy == 10 || iy == 12) && inX && inZ)
369 hy[i] = 0.0;
370 if ((iz == 6 || iz == 8) && inX && inY)
371 hz[i] = 0.0;
372 }
373 }
374 CCField ox = toDevice(hx, "ox"), oy = toDevice(hy, "oy"), oz = toDevice(hz, "oz");
375 mg.setOpenness(CCConst(ox), CCConst(oy), CCConst(oz), 1.0, 1.0, 1.0);
377 CutcellMG::Level& bt = mg.level(mg.nLevels() - 1);
378 HostBottom H;
379 H.e = bt.ext;
380 H.n = bt.inner;
381 for (int f = 0; f < 6; ++f)
382 H.bc[f] = cs.bc[f];
383 H.AC = toHostOp(bt.AC);
384 H.AX = toHostOp(bt.AFX);
385 H.AY = toHostOp(bt.AFY);
386 H.AZ = toHostOp(bt.AFZ);
387 H.build();
388 const BottomPlanes pl = mg.directPlanes();
389 const char* why = mg.directBottomIneligible();
390 printf(
391 "[%s] bottom %dx%dx%d: slow axis %d, P = %d, b = %d, border %d; %d fluid component(s) "
392 "(host %d); %s\n",
393 cs.name, bt.inner.x, bt.inner.y, bt.inner.z, pl.s, pl.P, pl.b, pl.border,
394 mg.bottomComponents(), H.nc, why ? why : "eligible");
395 int fails = 0;
396 if (!pl.valid() || mg.bottomComponents() != H.nc) {
397 printf("[%s] FAIL: no plane ordering, or the device labels disagree\n", cs.name);
398 return 1;
399 }
400 const std::vector<double> r = compatibleRhs(H, 7);
401 CCField rd = toDevice(r, "r");
402
403 { // U1: FP64 exactness
404 auto D = mg.directFactorForTest<double>(0);
405 CCField z("z", bt.n);
406 mg.directApplyForTest(D, z, rd, 0);
407 const auto x = toHost(z);
408 const double res = residualResummed(H, x, r);
409 double mm, xm, xs;
410 H.means(x, mm, xm, xs);
411 const bool ok = res <= 1e-12 && mm <= 1e-15 * xm && xs == 0.0;
412 printf(
413 "[%s] U1 FP64 M: residual %.3e, max|component mean| %.2e max|x|, max|x| solid %.1e -> "
414 "%s\n",
415 cs.name, res, mm / xm, xs, ok ? "ok" : "FAIL");
416 fails += !ok;
417 }
418 std::vector<double> zf;
419 { // U2: FP32 accuracy
420 auto D = mg.directFactorForTest<float>(0);
421 CCField z("z", bt.n);
422 mg.directApplyForTest(D, z, rd, 0);
423 zf = toHost(z);
424 const double res = residualResummed(H, zf, r);
425 const bool ok = res <= 1e-3;
426 printf("[%s] U2 FP32 M: residual %.3e -> %s\n", cs.name, res, ok ? "ok" : "FAIL");
427 fails += !ok;
428 }
429 { // U3: team-size independence (factor storage; M before the mean removal)
430 std::vector<int> Ts =
431 kHost ? std::vector<int>{1, 2, 4, 8} : std::vector<int>{32, 64, 128, 256, 0};
433 CCField zref("zref", bt.n);
434 int Tref = -1;
435 bool ok = true;
436 std::vector<int> used;
437 for (int T : Ts) {
438 int Tf = 0;
439 auto D = mg.directFactorForTest<float>(T, kBottomPivotTol, &Tf);
440 CCField z("z", bt.n);
441 const int Ta = mg.directApplyForTest(D, z, rd, T, /*noMean=*/true);
442 if ((T != 0 && (Tf != T || Ta != T))) {
443 printf("[%s] U3 team size %d not available (factor %d, solve %d): skipped\n", cs.name, T,
444 Tf, Ta);
445 continue;
446 }
447 used.push_back(Ta);
448 if (Tref < 0) {
449 ref = D;
450 Kokkos::deep_copy(zref, z);
451 Tref = Ta;
452 continue;
453 }
454 const bool same = sameBits(D.Q, ref.Q) && sameBits(D.e, ref.e) && sameBits(D.s, ref.s) &&
455 (!pl.border || sameBits(D.Y, ref.Y)) && sameBits(z, zref);
456 if (!same)
457 printf("[%s] U3 team size %d differs from %d\n", cs.name, Ta, Tref);
458 ok = ok && same;
459 }
460 ok = ok && used.size() >= 2;
461 printf("[%s] U3 team sizes", cs.name);
462 for (int T : used)
463 printf(" %d", T);
464 printf(": factor + M %s -> %s\n", ok ? "bitwise identical" : "DIFFER (or < 2 sizes)",
465 ok ? "ok" : "FAIL");
466 fails += !ok;
467 }
468 if (kHost) { // U8: the host schedule against the team algorithm, its bitwise oracle
469 bool ok = true;
470 std::vector<int> used;
471 auto agree = [&](const auto& A, const auto& B) {
472 return sameBits(A.Q, B.Q) && sameBits(A.e, B.e) && sameBits(A.s, B.s) &&
473 (!pl.border || sameBits(A.Y, B.Y)) && sameBits(A.stat, B.stat);
474 };
475 for (int T : {1, 2, 4}) {
476 for (double tau : {kBottomPivotTol, 1e30}) { // 1e30: every first-attempt pivot fails
477 int Th = 0, Tt = 0, Th64 = 0, Tt64 = 0;
478 const auto Dh = mg.directFactorForTest<float>(T, tau, &Th);
479 const auto Dt = mg.directFactorForTest<float>(T, tau, &Tt, /*teamAlgorithm=*/true);
480 const auto Dh64 = mg.directFactorForTest<double>(T, tau, &Th64);
481 const auto Dt64 = mg.directFactorForTest<double>(T, tau, &Tt64, /*teamAlgorithm=*/true);
482 if (Th != T || Tt != T || Th64 != T || Tt64 != T)
483 continue;
484 if (tau == kBottomPivotTol)
485 used.push_back(T);
486 const bool same = agree(Dh, Dt) && agree(Dh64, Dt64);
487 if (!same)
488 printf("[%s] U8 team size %d, first pivot floor %.0e: host schedule differs\n", cs.name,
489 T, tau);
490 ok = ok && same;
491 }
492 }
493 ok = ok && used.size() >= 2;
494 printf(
495 "[%s] U8 host schedule vs team algorithm (FP32 + FP64, with and without a restart), "
496 "team sizes",
497 cs.name);
498 for (int T : used)
499 printf(" %d", T);
500 printf(": %s -> %s\n", ok ? "bitwise identical" : "DIFFER (or < 2 sizes)", ok ? "ok" : "FAIL");
501 fails += !ok;
502 }
503 if (kHost) { // U3, host: the whole FCG solve is bitwise across T (single-lane reductions)
504 std::vector<double> hb(bt.n, 0.0);
505 std::mt19937 rng(5);
506 std::uniform_real_distribution<double> U(-1.0, 1.0);
507 H.forCells([&](const int*, long i) { hb[i] = U(rng); });
508 CCField b = toDevice(hb, "b");
509 CCField xref("xref", bt.n);
510 int itRef = -1, Tref = -1;
511 bool ok = true;
512 std::vector<int> used;
513 for (int T : {1, 2, 4, 8}) {
514 Kokkos::deep_copy(bt.rhs, b);
515 int Tu = 0;
516 const int it = mg.directSolveTeamForTest(T, &Tu);
517 if (Tu != T) {
518 printf("[%s] U3 FCG team size %d not available (ran %d): skipped\n", cs.name, T, Tu);
519 continue;
520 }
521 used.push_back(T);
522 if (Tref < 0) {
523 Kokkos::deep_copy(xref, bt.x);
524 itRef = it;
525 Tref = T;
526 continue;
527 }
528 const bool same = it == itRef && sameBits(bt.x, xref);
529 if (!same)
530 printf("[%s] U3 FCG team size %d differs from %d (iterations %d vs %d)\n", cs.name, T, Tref,
531 it, itRef);
532 ok = ok && same;
533 }
534 ok = ok && used.size() >= 2 && !mg.lastSolveFailed();
535 printf("[%s] U3 FCG team sizes", cs.name);
536 for (int T : used)
537 printf(" %d", T);
538 printf(": the whole solve (%d iterations) %s -> %s\n", itRef,
539 ok ? "bitwise identical" : "DIFFERS (or < 2 sizes)", ok ? "ok" : "FAIL");
540 fails += !ok;
541 }
542 auto fcg = [&](const char* tag, int itMax, int restartsWant) {
543 std::vector<double> hb(bt.n, 0.0); // a random rhs WITH component means (the kernel removes)
544 std::mt19937 rng(11);
545 std::uniform_real_distribution<double> U(-1.0, 1.0);
546 H.forCells([&](const int*, long i) { hb[i] = U(rng); });
547 {
548 CCField b = toDevice(hb, "b");
549 Kokkos::deep_copy(bt.rhs, b);
550 }
551 const int it = mg.directSolveForTest();
552 const int restarts = mg.directRestarts();
553 const bool flag = mg.lastSolveFailed();
554 const auto x = toHost(bt.x);
555 std::vector<long double> bsum(H.nc, 0.0L);
556 std::vector<long> bcnt(H.nc, 0);
557 H.forCells([&](const int*, long i) {
558 if (H.comp[i] >= 0) {
559 bsum[H.comp[i]] += hb[i];
560 ++bcnt[H.comp[i]];
561 }
562 });
563 double r0 = 0.0, rn = 0.0;
564 H.forCells([&](const int c[3], long i) {
565 if (H.comp[i] < 0)
566 return;
567 const double bp =
568 (double)((long double)hb[i] - bsum[H.comp[i]] / (long double)bcnt[H.comp[i]]);
569 r0 = std::max(r0, std::fabs(bp));
570 rn = std::max(rn, std::fabs(bp - H.applyStored(x, c, i)));
571 });
572 double mm, xm, xs;
573 H.means(x, mm, xm, xs);
574 const bool ok = it >= 1 && it <= itMax && !flag && restarts == restartsWant &&
575 rn <= 10.0 * kBottomTau * r0 && xs == 0.0 && mm <= 4e-16 * xm;
576 printf(
577 "[%s] %s FCG: %d iteration(s), restarts %d, flag %d, true residual %.3e r0, max|x| "
578 "solid %.1e, max|component mean| %.2e max|x| -> %s\n",
579 tag, cs.name, it, restarts, flag ? 1 : 0, rn / r0, xs, mm / xm, ok ? "ok" : "FAIL");
580 return ok ? 0 : 1;
581 };
582 fails += fcg("U4", 3, 0); // U4
583 mg.directPivotTolForTest(1e30); // U5: the first attempt fails at every pivot
585 fails += fcg("U5", kBottomCap - 1, 1);
588 { // U6: a non-finite face coefficient
589 auto hA = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), bt.AFX);
590 const long i0 = 1 + (long)1 * bt.ext.x + (long)1 * bt.ext.x * bt.ext.y;
591 long i = i0;
592 while (!(hA(i) < 0) && i < (long)bt.n - 1) // the first open x face
593 ++i;
594 const auto keep = hA(i);
595 hA(i) = std::numeric_limits<MReal>::quiet_NaN();
596 Kokkos::deep_copy(bt.AFX, hA);
598 Kokkos::deep_copy(bt.rhs, rd);
600 const bool flag = mg.lastSolveFailed();
601 const auto x = toHost(bt.x);
602 double xmax = 0.0;
603 for (double v : x)
604 xmax = std::max(xmax, std::fabs(v));
605 const bool ok = flag && xmax == 0.0;
606 printf("[%s] U6 NaN coefficient: flag %d, max|x| %.1e -> %s\n", cs.name, flag ? 1 : 0, xmax,
607 ok ? "ok" : "FAIL");
608 fails += !ok;
609 hA(i) = keep;
610 Kokkos::deep_copy(bt.AFX, hA);
612 }
613 if (bt.inner.x == 16 && bt.inner.y == 12 && bt.inner.z == 8) { // U7
614 const std::vector<double> xr = denseReference(H, pl, r);
615 double dm = 0.0, am = 0.0;
616 H.forCells([&](const int*, long i) {
617 am = std::max(am, std::fabs(xr[i]));
618 dm = std::max(dm, std::fabs(zf[i] - xr[i]));
619 });
620 const bool ok = dm <= 1e-3 * am;
621 printf("[%s] U7 FP32 M vs dense FP64 A'^-1: max diff %.3e max|x_ref| -> %s\n", cs.name, dm / am,
622 ok ? "ok" : "FAIL");
623 fails += !ok;
624 }
625 return fails;
626}
627
628} // namespace
629
630int main(int argc, char** argv) {
631 Kokkos::initialize(argc, argv);
632 int fails = 0;
633 {
634 const Case cases[] = {
635 {"periodic", {0, 0, 0, 0, 0, 0}, 32, 24, 16, false, false},
636 {"periodic-P2", {0, 0, 0, 0, 0, 0}, 4, 4, 4, false, false},
637 {"walls-y", {0, 0, 1, 1, 0, 0}, 32, 24, 16, false, false},
638 {"solids", {0, 0, 0, 0, 0, 0}, 32, 24, 16, true, false},
639 {"solids-walls-y", {0, 0, 1, 1, 0, 0}, 32, 24, 16, true, false},
640 {"pocket", {0, 0, 0, 0, 0, 0}, 32, 24, 16, false, true},
641 };
642 for (const Case& c : cases)
643 fails += runCase(c);
644 }
645 Kokkos::finalize();
646 printf(fails ? "bottom_direct: FAIL (%d)\n" : "bottom_direct: PASS\n", fails);
647 return fails ? 1 : 0;
648}
const BottomPlanes & directPlanes() const
int directSolveTeamForTest(int team, int *Tused=nullptr)
int directApplyForTest(const BottomDirect< FR > &D, CCField z, CCField r, int team, bool noMean=false)
const char * directBottomIneligible() const
void directPivotTolForTest(double t)
void setOpenness(CCConst ox, CCConst oy, CCConst oz, double idx2, double idy2, double idz2)
void setBoundaryConditions(const int bc[6])
void setAgglomerationMode(int mode)
void init(int nx, int ny, int nz, int nLevels)
BottomDirect< FR > directFactorForTest(int team, double tauPiv0=kBottomPivotTol, int *Tused=nullptr, bool teamAlgorithm=false)
int main()
Definition dbg_scene.cpp:13
flow — portable (Kokkos) geometric multigrid for the cut-cell (variable-openness) pressure Poisson.
x
Definition cmp.py:24
y
Definition cmp.py:24
dict res
Definition disc_bc.py:230
constexpr int kBottomCap
constexpr double kBottomTau
Kokkos::View< double *, CCMem > CCField
Kokkos::View< MReal *, CCMem > FPV
constexpr double kBottomPivotTol
Kokkos::View< const double *, CCMem > CCConst
xref(Bi)
Definition robin.py:13
int D
Definition tracers.py:128
static int runCase(const Config &c, const peclet::core::decomp::BlockDecomposer< 3 > &dec, int rank, int size)
static constexpr double F
static constexpr double U
static bool same(double a, double b)