43#include <Kokkos_Core.hpp>
54constexpr bool kHost = std::is_same_v<CCMem, Kokkos::HostSpace>;
57 auto h = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), f);
58 return std::vector<double>(
h.data(),
h.data() +
h.extent(0));
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)
69 auto h = Kokkos::create_mirror_view(f);
70 for (std::size_t i = 0;
i <
v.size(); ++
i)
72 Kokkos::deep_copy(f, h);
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;
86 C3 e{0, 0, 0},
n{0, 0, 0};
88 std::vector<double> AC, AX, AY, AZ;
89 std::vector<int> comp;
91 std::vector<double>
d,
s;
92 long idx(
int x,
int y,
int z)
const {
93 return (
long)(
x + 1) + (
long)(
y + 1) *
e.x + (
long)(
z + 1) *
e.x *
e.y;
96 bool face(
const int c[3],
int f6,
int nb[3],
double&
af)
const {
101 int v =
c[
a] + (
side ? 1 : -1);
108 const long i =
idx(c[0], c[1], c[2]);
110 const std::vector<double>&
F = (
a == 0) ? AX : (
a == 1 ? AY : AZ);
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};
124 std::vector<long>
parent(AC.size());
125 for (std::size_t i = 0;
i <
parent.size(); ++
i)
127 auto find = [&](
long a) {
132 forCells([&](
const int c[3],
long i) {
133 if (!(AC[i] > 1e-30))
135 for (
int f6 = 0;
f6 < 6; ++
f6) {
138 if (face(c,
f6, nb,
af) &&
af < 0.0) {
139 const long j =
idx(nb[0], nb[1], nb[2]);
145 comp.assign(AC.size(), -1);
146 std::vector<int> id(AC.size(), -1);
149 if (!(AC[i] > 1e-30))
151 const long r =
find(i);
156 d.assign(AC.size(), 0.0);
157 s.assign(AC.size(), 0.0);
158 forCells([&](
const int c[3],
long i) {
160 for (
int f6 = 0;
f6 < 6; ++
f6) {
163 if (face(c,
f6, nb,
af))
167 s[
i] = (comp[
i] >= 0 &&
d[
i] > 0.0) ? 1.0 / std::sqrt(d[i]) : 0.0;
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) {
176 if (face(c,
f6, nb,
af))
177 v +=
af *
x[
idx(nb[0], nb[1], nb[2])];
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],
187 return x[
idx(q[0], q[1], q[2])];
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);
196 std::vector<long double> sum(nc, 0.0L);
197 std::vector<long> cnt(nc, 0);
201 sum[comp[
i]] += (
long double)x[i];
203 xmax = std::max(
xmax, std::fabs(x[i]));
208 for (
int c = 0;
c <
nc; ++
c)
209 meanMax = std::max(
meanMax, (
double)std::fabs(sum[c] / (
long double)cnt[c]));
216 std::vector<double>
v(
H.AC.size(), 0.0);
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) {
229 H.forCells([&](
const int*,
long i) {
237 const std::vector<double>& r) {
238 double rn = 0.0, r0 = 0.0;
239 H.forCells([&](
const int c[3],
long i) {
242 r0 = std::max(r0, std::fabs(r[i]));
243 rn = std::max(
rn, std::fabs(
H.applyResummed(x, c, i) - r[i]));
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) {
259 kc[
H.comp[i]] = std::max(kc[
H.comp[i]], plane(c));
261 H.forCells([&](
const int c[3],
long i) {
262 if (
H.comp[i] >= 0 && plane(c) == kc[
H.comp[i]])
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) {
271 A[(std::size_t)
fi *
nn +
fi] += 1.0;
272 for (
int f6 = 0;
f6 < 6; ++
f6) {
275 if (
H.face(c,
f6, nb,
af)) {
276 const long j =
H.idx(nb[0], nb[1], nb[2]);
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])
288 for (
int j = 0;
j <
nn; ++
j) {
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;
301 for (
int i = 0;
i <
nn; ++
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];
307 for (
int i =
nn - 1;
i >= 0; --
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];
313 std::vector<double>
x(
H.AC.size(), 0.0);
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];
324 H.forCells([&](
const int*,
long i) {
326 x[i] = (
double)((
long double)x[i] - sum[
H.comp[i]] / (
long double)cnt[
H.comp[i]]);
339 const int nx =
cs.nx,
ny =
cs.ny, nz =
cs.nz;
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;
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;
359 if (
ix == 0 ||
ix == 16 ||
ix == 6 ||
ix == 8)
361 if (
ix == 6 ||
ix == 7)
381 for (
int f = 0;
f < 6; ++
f)
391 "[%s] bottom %dx%dx%d: slow axis %d, P = %d, b = %d, border %d; %d fluid component(s) "
397 printf(
"[%s] FAIL: no plane ordering, or the device labels disagree\n",
cs.name);
410 H.means(x,
mm, xm, xs);
411 const bool ok =
res <= 1
e-12 &&
mm <= 1
e-15 * xm && xs == 0.0;
413 "[%s] U1 FP64 M: residual %.3e, max|component mean| %.2e max|x|, max|x| solid %.1e -> "
415 cs.name, res,
mm / xm, xs, ok ?
"ok" :
"FAIL");
418 std::vector<double>
zf;
425 const bool ok =
res <= 1
e-3;
426 printf(
"[%s] U2 FP32 M: residual %.3e -> %s\n",
cs.name, res, ok ?
"ok" :
"FAIL");
430 std::vector<int>
Ts =
431 kHost ? std::vector<int>{1, 2, 4, 8} : std::vector<int>{32, 64, 128, 256, 0};
436 std::vector<int>
used;
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,
450 Kokkos::deep_copy(
zref, z);
457 printf(
"[%s] U3 team size %d differs from %d\n",
cs.name,
Ta,
Tref);
461 printf(
"[%s] U3 team sizes",
cs.name);
464 printf(
": factor + M %s -> %s\n", ok ?
"bitwise identical" :
"DIFFER (or < 2 sizes)",
470 std::vector<int>
used;
471 auto agree = [&](
const auto&
A,
const auto&
B) {
475 for (
int T : {1, 2, 4}) {
482 if (
Th != T || Tt != T ||
Th64 != T ||
Tt64 != T)
488 printf(
"[%s] U8 team size %d, first pivot floor %.0e: host schedule differs\n",
cs.name,
495 "[%s] U8 host schedule vs team algorithm (FP32 + FP64, with and without a restart), "
500 printf(
": %s -> %s\n", ok ?
"bitwise identical" :
"DIFFER (or < 2 sizes)",
ok ?
"ok" :
"FAIL");
504 std::vector<double>
hb(
bt.n, 0.0);
506 std::uniform_real_distribution<double>
U(-1.0, 1.0);
507 H.forCells([&](
const int*,
long i) {
hb[
i] =
U(rng); });
512 std::vector<int>
used;
513 for (
int T : {1, 2, 4, 8}) {
514 Kokkos::deep_copy(
bt.rhs, b);
518 printf(
"[%s] U3 FCG team size %d not available (ran %d): skipped\n",
cs.name, T,
Tu);
523 Kokkos::deep_copy(xref,
bt.x);
530 printf(
"[%s] U3 FCG team size %d differs from %d (iterations %d vs %d)\n",
cs.name, T,
Tref,
535 printf(
"[%s] U3 FCG team sizes",
cs.name);
538 printf(
": the whole solve (%d iterations) %s -> %s\n",
itRef,
539 ok ?
"bitwise identical" :
"DIFFERS (or < 2 sizes)",
ok ?
"ok" :
"FAIL");
543 std::vector<double>
hb(
bt.n, 0.0);
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); });
549 Kokkos::deep_copy(
bt.rhs, b);
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];
563 double r0 = 0.0,
rn = 0.0;
564 H.forCells([&](
const int c[3],
long i) {
569 r0 = std::max(r0, std::fabs(
bp));
570 rn = std::max(
rn, std::fabs(
bp -
H.applyStored(x, c, i)));
573 H.means(x,
mm, xm, xs);
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",
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;
592 while (!(
hA(i) < 0) &&
i < (
long)
bt.n - 1)
595 hA(i) = std::numeric_limits<MReal>::quiet_NaN();
596 Kokkos::deep_copy(
bt.AFX,
hA);
598 Kokkos::deep_copy(
bt.rhs,
rd);
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,
610 Kokkos::deep_copy(
bt.AFX,
hA);
613 if (
bt.inner.x == 16 &&
bt.inner.y == 12 &&
bt.inner.z == 8) {
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]));
620 const bool ok =
dm <= 1
e-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,
631 Kokkos::initialize(
argc, argv);
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},
642 for (
const Case& c : cases)
646 printf(
fails ?
"bottom_direct: FAIL (%d)\n" :
"bottom_direct: PASS\n",
fails);
647 return fails ? 1 : 0;
const BottomPlanes & directPlanes() const
void directMarkStaleForTest()
int directSolveTeamForTest(int team, int *Tused=nullptr)
int directApplyForTest(const BottomDirect< FR > &D, CCField z, CCField r, int team, bool noMean=false)
int bottomComponents() const
const char * directBottomIneligible() const
void directPivotTolForTest(double t)
void setOpenness(CCConst ox, CCConst oy, CCConst oz, double idx2, double idy2, double idz2)
void bottomForceForTest()
void setBoundaryConditions(const int bc[6])
void setAgglomerationMode(int mode)
int directRestarts() const
void init(int nx, int ny, int nz, int nLevels)
BottomDirect< FR > directFactorForTest(int team, double tauPiv0=kBottomPivotTol, int *Tused=nullptr, bool teamAlgorithm=false)
flow — portable (Kokkos) geometric multigrid for the cut-cell (variable-openness) pressure Poisson.
constexpr double kBottomTau
Kokkos::View< double *, CCMem > CCField
Kokkos::View< MReal *, CCMem > FPV
constexpr double kBottomPivotTol
Kokkos::View< const double *, CCMem > CCConst
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)