30#include <Kokkos_Core.hpp>
40 std::fprintf(stderr, "CHECK failed: %s\n at %s:%d\n", #cond, __FILE__, __LINE__); \
50using HV = Kokkos::View<double*, Kokkos::HostSpace>;
51HV host(
const CCField& f) {
52 return Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), f);
60 const Case
cases[] = {
61 {{64, 64, 64}, {1, 1, 1}}, {{48, 40, 12}, {1, 1, 1}}, {{128, 128, 4}, {1, 1, 1}},
62 {{30, 64, 7}, {1, 1, 1}}, {{77, 77, 77}, {1, 1, 1}}, {{64, 64, 32}, {1, 1, 2}},
63 {{20, 24, 48}, {1, 1.5, 3}}, {{96, 32, 64}, {2, 1, 1}},
65 for (
const Case& c :
cases) {
66 const double w[3] = {1.0 / (
c.hp[0] *
c.hp[0]), 1.0 / (
c.hp[1] *
c.hp[1]),
67 1.0 / (
c.hp[2] *
c.hp[2])};
69 vmg.setMetric(w,
c.hp);
70 vmg.init(
c.n[0],
c.n[1],
c.n[2], 64);
72 smg.setMetric(w,
c.hp);
73 smg.init(
c.n[0],
c.n[1],
c.n[2], [](CCField) {});
74 bool same = vmg.levels() == smg.levels();
75 for (
int L = 0;
same &&
L < smg.levels(); ++
L) {
76 const auto&
a = vmg.level(L);
77 const auto&
b = smg.level(L);
78 same =
a.inner.x ==
b.inner.x &&
a.inner.y ==
b.inner.y &&
a.inner.z ==
b.inner.z &&
79 a.ratio.x ==
b.ratio.x &&
a.ratio.y ==
b.ratio.y &&
a.ratio.z ==
b.ratio.z &&
80 a.cfac.x ==
b.cfac.x &&
a.cfac.y ==
b.cfac.y &&
a.cfac.z ==
b.cfac.z;
82 std::printf(
"levels %dx%dx%d h' (%g,%g,%g): %d levels, bottom %dx%dx%d %s\n",
c.n[0],
c.n[1],
83 c.n[2],
c.hp[0],
c.hp[1],
c.hp[2], smg.levels(),
84 smg.level(smg.levels() - 1).inner.x, smg.level(smg.levels() - 1).inner.y,
85 smg.level(smg.levels() - 1).inner.z,
same ?
"== VelocityMG" :
"DIFFERS");
91std::vector<double>
sphereSdf(
int n,
double cx,
double cy,
double cz,
double R) {
92 std::vector<double>
s((std::size_t)n * n * n);
93 for (
int z = 0;
z <
n; ++
z)
94 for (
int y = 0;
y <
n; ++
y)
95 for (
int x = 0;
x <
n; ++
x) {
96 const double dx =
x + 0.5 - cx,
dy =
y + 0.5 - cy,
dz =
z + 0.5 - cz;
97 s[(std::size_t)x + (std::size_t)
y *
n + (std::size_t)z * n * n] =
98 std::sqrt(dx * dx + dy * dy + dz * dz) -
R;
104void g1Setup(
IbmSolver& s,
int n,
double R,
double off) {
108 for (
int f = 0;
f < 6; ++
f)
109 s.setDomainBc(f, 1, 0.0, 0.0, 0.0);
110 s.setSolid(
sphereSdf(n, 0.5 * n + off, 0.5 * n - 0.6 * off, 0.5 * n + 0.3 * off, R),
false);
111 s.addScalar(
"c", 0.7, 1, 50,
true);
112 for (
int f = 0;
f < 6; ++
f)
113 s.setScalarBc(
"c", f, 2, 0.25);
114 s.setScalarWall(
"c", 1, 1.0, 0.0, -1);
120 g1Setup(s, n, 8.0, 0.37);
121 s.solveScalarSteady(
"c");
122 auto&
st = *
s.scalarField(
"c").cut;
123 const ScalarMG& mg = *
st.mg;
124 CHECK(
st.mgLevels == mg.levels() && mg.levels() == 5);
125 long pinned = 0,
rows = 0, badId = 0, badMM = 0, badFace = 0;
126 for (
int L = 1;
L < mg.levels(); ++
L) {
127 const auto&
c = mg.level(L);
128 const auto&
f = mg.level(L - 1);
129 auto AC = host(
c.AC),
AX = host(
c.AFX), AY = host(
c.AFY), AZ = host(
c.AFZ),
U = host(
c.unk);
130 auto PX = host(
c.px);
131 const long sy =
c.ext.x, sz = (long)
c.ext.x *
c.ext.y;
132 const double wx = 1.0 / ((double)
c.cfac.x *
c.cfac.x);
134 HV
FP =
L >= 2 ? host(
f.px) : HV();
135 for (
int z =
c.g;
z <
c.ext.z -
c.g; ++
z)
136 for (
int y =
c.g;
y <
c.ext.y -
c.g; ++
y)
137 for (
int x =
c.g;
x <
c.ext.x -
c.g; ++
x) {
138 const long i =
x +
y * sy +
z * sz;
142 if (!(
AC(i) == 1.0 &&
AX(i) == 0.0 && AY(i) == 0.0 && AZ(i) == 0.0))
146 const double off = -(
AX(i) +
AX(i + 1) + AY(i) + AY(i + sy) + AZ(i) + AZ(i + sz));
147 const bool nonpos =
AX(i) <= 0 &&
AX(i + 1) <= 0 && AY(i) <= 0 && AY(i + sy) <= 0 &&
148 AZ(i) <= 0 && AZ(i + sz) <= 0;
149 if (!(
AC(i) > 0.0 && nonpos &&
AC(i) >= off * (1.0 - 1e-14)))
151 if (!(
AX(i) == -(wx * PX(i))))
154 const long fsy =
f.ext.x, fsz = (long)
f.ext.x *
f.ext.y;
155 const int fx = 2 * (
x -
c.g) +
f.g, fy = 2 * (y -
c.g) +
f.g, fz = 2 * (
z -
c.g) +
f.g;
157 for (
int a = 0;
a < 2; ++
a)
158 for (
int b = 0;
b < 2; ++
b)
159 sxs +=
FP(fx + (fy + a) * fsy + (fz + b) * fsz);
160 if (!(PX(i) == sxs / 4.0))
166 "coarse: %ld rows (%ld pinned): identity-row defects %ld, M-matrix defects %ld, "
167 "face-rule defects %ld\n",
168 rows, pinned, badId, badMM, badFace);
169 CHECK(pinned > 0 && badId == 0 && badMM == 0 && badFace == 0);
172void testLevelRule() {
175 g1Setup(s, n, 8.0, 0.37);
177 const double Fsw = (ScalarMG::kFullTableKappa - 1.0) / 12.0;
178 s.setDt(0.95 * Fsw / 0.7);
180 const int small =
s.scalarField(
"c").cut->mgLevels;
181 s.setDt(1.05 * Fsw / 0.7);
183 const int at =
s.scalarField(
"c").cut->mgLevels;
184 s.solveScalarSteady(
"c");
185 const int steady =
s.scalarField(
"c").cut->mgLevels;
186 std::printf(
"level rule: dt D/h^2 = %.3f -> %d level(s), %.3f -> %d, steady -> %d\n", 0.95 * Fsw,
187 small, 1.05 * Fsw, at, steady);
188 CHECK(small == 1 && at == 5 && steady == 5);
192 auto&
st = *
s.scalarField(
"c").cut;
193 const std::size_t
n =
st.SAC.extent(0);
195 const std::vector<double>
r =
st.mg->contraction(30, e, t, z);
198 for (
int k = (
int)
r.size() - tail;
k < (
int)
r.size(); ++
k)
199 gm *= r[(std::size_t)
k];
200 gm = std::pow(gm, 1.0 / tail);
202 "%s: %d levels, ||e_k||/||e_k-1||: first %.3f, k=10 %.3f, k=30 %.3f; "
203 "geometric mean of the last %d: %.3f\n",
204 tag,
st.mgLevels, r[0], r[9],
r.back(), tail, gm);
210void shellsScene(
IbmSolver& s,
double c,
double Ri,
double Ro) {
211 const std::vector<int> ni = {1, -1, -1, 3, -1, -1, 1, -1, -1, 34, 1, 2};
212 std::vector<double> nr(4 * 16, 0.0);
213 for (
int k = 0;
k < 4; ++
k) {
214 nr[16 *
k + 14] = 1.0;
215 nr[16 *
k + 15] = 1.0;
218 nr[16 + 0] = nr[16 + 1] = nr[16 + 2] = 1e5;
220 const std::vector<int> ii = {0, -1, 3, -1};
221 std::vector<double> ir(2 * 18, 0.0);
222 for (
int k = 0;
k < 2; ++
k) {
223 ir[18 *
k + 0] = ir[18 *
k + 1] = ir[18 *
k + 2] =
c;
224 ir[18 *
k + 6] = 1.0;
225 ir[18 *
k + 7] = 1.0;
227 s.setScene(ni, nr, ii, ir,
false);
228 s.setSolidFromScene(
false);
235 for (
int f = 0;
f < 6; ++
f)
236 s.setDomainBc(f, 1, 0.0, 0.0, 0.0);
240void guardRow(
IbmSolver& s,
const char* tag,
int cls) {
241 s.solveScalarSteady(
"c");
243 std::snprintf(t,
sizeof t,
"%-44s %2d it", tag,
s.scalarField(
"c").cut->iterations);
252void testContraction() {
254 for (
int n : {32, 64, 128}) {
256 g1Setup(s, n, 0.25 * n, 0.37);
257 std::snprintf(tag,
sizeof tag,
"C2 G1 R/h=%d (%d^3)", n / 4, n);
260 const int g2box[3] = {48, 80, 128};
261 for (
int k = 0;
k < 3; ++
k) {
262 const int Rih = 6 <<
k,
n = g2box[
k];
265 shellsScene(s, 0.5 * n + 0.29, Rih, 2.5 * Rih);
266 s.addScalar(
"c", 0.8, 1, 50,
true);
267 for (
int f = 0;
f < 6; ++
f)
268 s.setScalarBc(
"c", f, 1, 0.0);
269 s.setScalarWall(
"c", 0, 0.3, 0.0, 0);
270 s.setScalarWall(
"c", 1, 0.0, 0.0, 1);
271 std::snprintf(tag,
sizeof tag,
"C2 G2 Ri/h=%d (%d^3)", Rih, n);
274 for (
int n : {32, 64, 128}) {
276 g1Setup(s, n, 0.25 * n, 0.37);
277 s.setScalarWall(
"c", 2, 0.0, 0.7 / (0.25 * n), -1);
278 std::snprintf(tag,
sizeof tag,
"C2 G3a Da=1 R/h=%d (%d^3)", n / 4, n);
283 g1Setup(s, 64, 16.0, 0.37);
284 s.setScalarWall(
"c", 0, 0.3, 0.0, -1);
285 guardRow(s,
"C2 Neumann sphere + Dirichlet box (64^3)", 2);
290 s.setSolid(std::vector<double>((std::size_t)64 * 64 * 64, 100.0),
false);
291 s.addScalar(
"c", 0.7, 1, 50,
true);
292 for (
int f = 0;
f < 6; ++
f)
293 s.setScalarBc(
"c", f, 2, 0.25);
294 s.setScalarSource(
"c", 0.1);
295 guardRow(s,
"C2 no solid, Dirichlet box (64^3)", 2);
297 for (
int n : {32, 48, 80, 77}) {
302 const double R = 0.5 *
n * std::cbrt(0.3 * 6.0 / M_PI);
303 s.setSolid(
sphereSdf(n, 0.5 * n + 0.21, 0.5 * n - 0.13, 0.5 * n + 0.07, R),
false);
304 s.addScalar(
"c", 0.7, 1, 50,
true);
305 s.setScalarWall(
"c", 0, 0.3, 0.0, -1);
306 s.setScalarSource(
"c", 0.1);
308 std::snprintf(tag,
sizeof tag,
"%s G5b geometry n=%d (ND %.1f, singular)",
309 n == 77 ?
"C1" :
"C2",
n, 2 *
R);
310 guardRow(s, tag, n == 77 ? 1 : 2);
311 CHECK(
s.scalarField(
"c").cut->singular);
313 for (
int n : {64, 128}) {
318 s.setSolid(
sphereSdf(n, 0.5 * n + 0.37, 0.5 * n - 0.22, 0.5 * n + 0.11, 0.25 * n),
false);
319 s.addScalar(
"c", 0.7, 1, 50,
true);
320 s.setScalarWall(
"c", 1, 1.0, 0.0, -1);
321 s.setScalarSource(
"c", -0.3);
322 std::snprintf(tag,
sizeof tag,
"C3 periodic + Dirichlet sphere R/h=%d (%d^3)", n / 4, n);
331long inner(
const ScalarMG::Level& lv,
int x,
int y,
int z) {
332 return (
long)(
x +
lv.g) + (
long)(
y +
lv.g) *
lv.ext.x + (
long)(
z +
lv.g) *
lv.ext.x *
lv.ext.y;
341 s.setSolid(std::vector<double>((std::size_t)
s.nx() *
s.ny() *
s.nz(), 100.0),
true);
342 s.addScalar(
"c", 0.7, 1, 50,
true);
343 s.setScalarSource(
"c", 0.1);
349void testAdvUniform() {
350 const int nn[2][3] = {{32, 32, 32}, {32, 32, 4}};
351 for (
const auto& n : nn)
355 const std::size_t
nc = (std::size_t)n[0] * n[1] * n[2];
356 const char* nm[3] = {
"u",
"v",
"w"};
357 for (
int a = 0;
a < 3; ++
a)
358 s.setField(nm[a], std::vector<double>(nc, a == ax ? 0.75 : 0.0));
359 s.solveScalarSteady(
"c");
360 const auto& mg = *
s.scalarField(
"c").cut->mg;
361 long bad = 0, cells = 0;
363 for (
int L = 1;
L < mg.levels(); ++
L) {
364 const auto&
lv = mg.level(L);
365 aniso = aniso || (
lv.cfac.x !=
lv.cfac.z);
366 const int cf[3] = {
lv.cfac.x,
lv.cfac.y,
lv.cfac.z};
367 for (
int a = 0;
a < 3; ++
a) {
368 const HV qp = host(
lv.qp[a]), qm = host(
lv.qm[a]);
369 const double want =
a ==
ax ? 0.75 / (double)cf[a] : 0.0;
370 for (
int z = 0;
z <
lv.inner.z; ++
z)
371 for (
int y = 0;
y <
lv.inner.y; ++
y)
372 for (
int x = 0;
x <
lv.inner.x; ++
x) {
373 const long i =
inner(lv, x, y, z);
374 bad += (qp(i) ==
want && qm(i) == 0.0) ? 0 : 1;
380 "(u1) uniform flow along %c, %dx%dx%d (%d levels%s): %ld of %ld Qp/Qm entries "
381 "differ from phi/cf_a, 0\n",
382 "xyz"[ax], n[0], n[1], n[2], mg.levels(), aniso ?
", (2,2,1) levels" :
"", bad, cells);
383 CHECK(bad == 0 && cells > 0);
393void testAdvDivergenceFree() {
397 std::vector<double> psi[3];
398 unsigned long long st = 0x2545F4914F6CDD1Dull;
403 return (
double)(
st >> 11) * (1.0 / 9007199254740992.0) - 0.5;
405 for (
auto& p : psi) {
406 p.resize((std::size_t)n * n * n);
410 auto P = [&](
int c,
int x,
int y,
int z) {
411 return psi[
c][(std::size_t)((x + n) %
n) + (std::size_t)((
y +
n) % n) *
n +
412 (std::size_t)((z + n) %
n) * n * n];
414 std::vector<double>
u((std::size_t)n * n * n),
v(
u.size()),
w(
u.size());
415 for (
int z = 0;
z <
n; ++
z)
416 for (
int y = 0;
y <
n; ++
y)
417 for (
int x = 0;
x <
n; ++
x) {
418 const std::size_t
k = (std::size_t)x + (std::size_t)
y *
n + (std::size_t)z * n * n;
419 u[
k] = (P(2, x, y + 1, z) - P(2, x, y, z)) - (P(1, x, y, z + 1) - P(1, x, y, z));
420 v[
k] = (P(0, x, y, z + 1) - P(0, x, y, z)) - (P(2, x + 1, y, z) - P(2, x, y, z));
421 w[
k] = (P(1, x + 1, y, z) - P(1, x, y, z)) - (P(0, x, y + 1, z) - P(0, x, y, z));
426 s.solveScalarSteady(
"c");
427 const auto&
sc =
s.scalarField(
"c");
428 const auto& mg = *
sc.cut->mg;
429 CHECK(
sc.cut->advecting &&
sc.cut->steady);
431 for (
int L = 0;
L < mg.levels(); ++
L) {
432 const auto&
lv = mg.level(L);
433 const long sx = 1, sy =
lv.ext.x, sz = (long)
lv.ext.x *
lv.ext.y;
436 for (
int a = 0;
a < 3; ++
a) {
437 const HV
ph = host(
sc.cut->phi[a]);
438 q[
a] = HV(
"qp",
ph.extent(0));
439 q[3 +
a] = HV(
"qm",
ph.extent(0));
440 for (std::size_t k = 0;
k <
ph.extent(0); ++
k) {
441 q[
a](
k) = std::fmax(
ph(k), 0.0);
442 q[3 +
a](
k) = std::fmax(-
ph(k), 0.0);
446 for (
int a = 0;
a < 3; ++
a) {
447 q[
a] = host(
lv.qp[a]);
448 q[3 +
a] = host(
lv.qm[a]);
451 const HV
AC = host(
lv.AC), AW = host(
lv.AW), AE = host(
lv.AE), AS = host(
lv.AS),
452 AN = host(
lv.AN), AB = host(
lv.AB), AT = host(
lv.AT);
453 double maxQ = 0.0, maxAC = 0.0,
row = 0.0,
col = 0.0;
454 for (
int z = 0;
z <
lv.inner.z; ++
z)
455 for (
int y = 0;
y <
lv.inner.y; ++
y)
456 for (
int x = 0;
x <
lv.inner.x; ++
x) {
457 const long i =
inner(lv, x, y, z);
458 for (
int k = 0;
k < 6; ++
k)
459 maxQ = std::fmax(maxQ, q[k](i));
460 maxAC = std::fmax(maxAC, std::fabs(
AC(i)));
462 ((
q[3](
i) + q[0](i + sx)) + (q[4](i) +
q[1](
i + sy))) + (q[5](i) +
q[2](
i + sz));
464 ((
q[0](
i) + q[3](i + sx)) + (q[1](i) +
q[4](
i + sy))) + (q[2](i) +
q[5](
i + sz));
465 row = std::fmax(row, std::fabs(out - in));
468 auto I = [&](
int dx,
int dy,
int dz) {
469 return inner(lv, (x + dx +
lv.inner.x) %
lv.inner.x, (y + dy +
lv.inner.y) %
lv.inner.y,
470 (z + dz +
lv.inner.z) %
lv.inner.z);
472 const double cs =
AC(i) + AW(
I(1, 0, 0)) + AE(
I(-1, 0, 0)) + AS(
I(0, 1, 0)) +
473 AN(
I(0, -1, 0)) + AB(
I(0, 0, 1)) + AT(
I(0, 0, -1));
474 col = std::fmax(col, std::fabs(cs));
476 std::printf(
"(u2) level %d: max|out - in| = %.2e max Q, max|column sum| = %.2e max AC\n", L,
477 row / maxQ, col / maxAC);
478 ok =
ok &&
row <= 1
e-13 * maxQ &&
col <= 1
e-13 * maxAC && maxQ > 0.0;
492 s.setSolid(
sphereSdf(n, 0.5 * n + 0.37, 0.5 * n - 0.22, 0.5 * n + 0.11, 0.25 * n),
true);
493 s.setBodyForce(1e-3, 4e-4, 2e-4);
494 for (
int k = 0;
k < 20; ++
k)
496 s.addScalar(
"c", 0.7, 1, 50,
true);
497 s.setScalarWall(
"c", 1, 1.0, 0.0, -1);
498 s.setScalarSource(
"c", -0.3);
504void rescalePeclet(
IbmSolver& s,
double pe) {
505 s.solveScalarSteady(
"c");
506 const double k = pe /
s.scalarField(
"c").cut->maxCellPeclet;
507 for (
const char* nm : {
"u",
"v",
"w"}) {
508 std::vector<double>
f =
s.getField(nm);
517double vcycleSeconds(
IbmSolver& s,
int reps) {
518 auto&
st = *
s.scalarField(
"c").cut;
519 const std::size_t
n =
st.SAC.extent(0);
521 const std::vector<double> unk =
s.scalarGeometryField(4);
522 std::vector<double> rv(unk.size());
523 for (std::size_t k = 0;
k < rv.size(); ++
k)
524 rv[k] = unk[k] > 0.5 ? std::sin(0.37 * (
double)k) : 0.0;
526 Kokkos::deep_copy(r,
s.scalarField(
"c").c);
529 const auto t0 = std::chrono::steady_clock::now();
530 for (
int k = 0;
k < reps; ++
k)
533 return std::chrono::duration<double>(std::chrono::steady_clock::now() - t0).count() / reps;
545long mMatrixDefects(
const ScalarMG& mg,
const char* tag) {
547 for (
int L = 0;
L < mg.levels(); ++
L) {
548 const auto&
lv = mg.level(L);
549 const HV
AC = host(
lv.AC), AW = host(
lv.AW), AE = host(
lv.AE), AS = host(
lv.AS),
550 AN = host(
lv.AN), AB = host(
lv.AB), AT = host(
lv.AT);
552 for (
int z = 0;
z <
lv.inner.z; ++
z)
553 for (
int y = 0;
y <
lv.inner.y; ++
y)
554 for (
int x = 0;
x <
lv.inner.x; ++
x)
555 maxAC = std::fmax(maxAC, std::fabs(
AC(
inner(lv, x, y, z))));
556 double minCol = 1e300;
557 long rows = 0, positive = 0, negative = 0;
558 for (
int z = 0;
z <
lv.inner.z; ++
z)
559 for (
int y = 0;
y <
lv.inner.y; ++
y)
560 for (
int x = 0;
x <
lv.inner.x; ++
x) {
561 const long i =
inner(lv, x, y, z);
562 auto I = [&](
int dx,
int dy,
int dz) {
563 return inner(lv, (x + dx +
lv.inner.x) %
lv.inner.x, (y + dy +
lv.inner.y) %
lv.inner.y,
564 (z + dz +
lv.inner.z) %
lv.inner.z);
566 for (
const HV* b : {&AW, &AE, &AS, &AN, &AB, &AT})
567 positive += (*b)(
i) > 0.0 ? 1 : 0;
568 const double cs =
AC(i) + AW(
I(1, 0, 0)) + AE(
I(-1, 0, 0)) + AS(
I(0, 1, 0)) +
569 AN(
I(0, -1, 0)) + AB(
I(0, 0, 1)) + AT(
I(0, 0, -1));
570 minCol = std::fmin(minCol, cs / maxAC);
571 negative +=
cs < -1
e-13 * maxAC ? 1 : 0;
575 "(u3) %s level %d: %ld columns, min column sum = %.3e max AC, %ld below -1e-13, %ld "
578 tag, L, rows, minCol, negative, positive);
579 bad += positive + negative;
589void testAdvective() {
595 for (
const char* nm : {
"u",
"v",
"w"})
596 s.setField(nm, std::vector<double>((std::size_t)n * n * n, 0.0));
597 s.solveScalarSteady(
"c");
598 CHECK(!
s.scalarField(
"c").cut->advecting);
599 tRest = vcycleSeconds(s, 20);
601 for (
const double pe : {0.1, 1.0, 10.0}) {
604 rescalePeclet(s, pe);
605 const auto&
st = *
s.scalarField(
"c").cut;
607 std::snprintf(tag,
sizeof tag,
"C4 C3 + advection, Pe_h=%.3g, R/h=%d (%d^3)", pe, n / 4, n);
608 s.solveScalarSteady(
"c");
609 std::printf(
" census max_cell_peclet %.6g; %ld implicit faces of %ld carrying flux\n",
610 st.maxCellPeclet,
st.numImplicitFaces,
st.numFluxFaces);
611 CHECK(std::fabs(
st.maxCellPeclet / pe - 1.0) <= 1e-12);
612 CHECK(
st.advecting &&
st.numImplicitFaces ==
st.numFluxFaces &&
st.numFluxFaces > 0);
614 std::snprintf(t,
sizeof t,
"%-44s %2d it", tag,
st.iterations);
619 auto&
sc =
s.scalarField(
"c");
620 CHECK(mMatrixDefects(*
st.mg,
"Pe_h 10") == 0);
622 const std::size_t nn =
st.SAC.extent(0);
623 CCField x(
"x", nn), y1(
"y1", nn), y2(
"y2", nn);
625 auto hx = Kokkos::create_mirror_view(x);
626 for (std::size_t k = 0;
k < nn; ++
k)
627 hx(k) = std::cos(1.3 * (
double)k) * (1.0 + 0.1 * std::sin(0.01 * (
double)k));
628 Kokkos::deep_copy(x, hx);
630 st.mg->applySurrogate(y1, x);
633 const HV h1 = host(y1), h2 = host(y2);
634 const auto& l0 =
st.mg->level(0);
636 for (
int z = 0;
z < l0.inner.z; ++
z)
637 for (
int yy = 0; yy < l0.inner.y; ++yy)
638 for (
int xx = 0; xx < l0.inner.x; ++xx) {
639 const long i =
inner(l0, xx, yy, z);
640 diff += std::memcmp(&h1(i), &h2(i),
sizeof(
double)) == 0 ? 0 : 1;
643 "(u4) level-0 advective applySurrogate vs the band matvec with SAC: %ld of %d "
644 "rows not bitwise\n",
645 diff, l0.inner.x * l0.inner.y * l0.inner.z);
647 const double tAdv = vcycleSeconds(s, 20);
649 "G-perf: V-cycle %d^3 (%d levels): advective %.2f ms, symmetric (at rest) "
650 "%.2f ms, ratio %.2f (expected ~1.5, red flag > 2)\n",
651 n,
st.mgLevels, 1e3 * tAdv, 1e3 * tRest, tAdv / tRest);
659void testAdvOpenFaces() {
660 const int nx = 64,
ny = 32, nz = 32;
665 s.setDomainBc(0, 2, 0.05, 0.0, 0.0);
666 s.setDomainBc(1, 3, 0.0, 0.0, 0.0);
667 for (
int f = 2;
f < 6; ++
f)
668 s.setDomainBc(f, 1, 0.0, 0.0, 0.0);
669 std::vector<double>
sdf((std::size_t)nx * ny * nz);
670 for (
int z = 0;
z < nz; ++
z)
671 for (
int y = 0;
y <
ny; ++
y)
672 for (
int x = 0;
x < nx; ++
x) {
673 const double dx =
x + 0.5 - 27.3,
dy =
y + 0.5 - 15.6,
dz =
z + 0.5 - 16.9;
674 sdf[(std::size_t)x + (std::size_t)
y * nx + (std::size_t)z * nx * ny] =
675 std::sqrt(dx * dx + dy * dy + dz * dz) - 7.4;
677 s.setSolid(sdf,
true);
678 for (
int k = 0;
k < 10; ++
k)
680 s.addScalar(
"c", 0.4, 0, 50,
true);
681 s.setScalarBc(
"c", 0, 2, 1.0);
682 for (
int f = 1;
f < 6; ++
f)
683 s.setScalarBc(
"c", f, 1, 0.0);
684 s.solveScalarSteady(
"c");
685 const auto&
st = *
s.scalarField(
"c").cut;
686 const auto& mg = *
st.mg;
688 long bad = 0, checked = 0;
689 for (
int L = 1;
L < mg.levels(); ++
L) {
690 const auto&
lv = mg.level(L);
691 const int ext[3] = {
lv.ext.x,
lv.ext.y,
lv.ext.z};
692 const long sa[3] = {1, (long)
lv.ext.x, (
long)
lv.ext.x *
lv.ext.y};
693 for (
int a = 0;
a < 3; ++
a) {
694 const HV qp = host(
lv.qp[a]), qm = host(
lv.qm[a]);
695 const int b = (
a + 1) % 3, c = (a + 2) % 3;
696 for (
const int pl : {
lv.g, ext[
a] -
lv.g})
697 for (
int jb =
lv.g; jb < ext[
b] -
lv.g; ++jb)
699 const long i = (long)pl * sa[a] + (
long)jb * sa[
b] + (long)
jc * sa[c];
700 bad += (qp(i) == 0.0 && qm(i) == 0.0) ? 0 : 1;
706 "(u5) channel with open x faces, walls y z: %d levels, %ld domain-face entries, %ld "
707 "nonzero; steady %d iterations, census max_cell_peclet %.3g\n",
708 mg.levels(), checked, bad,
st.iterations,
st.maxCellPeclet);
709 CHECK(bad == 0 && checked > 0);
713int main(
int argc,
char** argv) {
714 Kokkos::initialize(argc, argv);
716 const bool advOnly = argc > 1 && std::strcmp(argv[argc - 1],
"advective") == 0;
724 testAdvDivergenceFree();
flow — host-facing Kokkos IBM Navier-Stokes solver (drop-in flow-style API).
contraction(A, levels, singular, nu, its=40)
void applyCutcellOp(CCField y, CCConst x, OpV AC, OpV AW, OpV AE, OpV AS, OpV AN, OpV AB, OpV AT, C3 e, int g)
Kokkos::View< double *, CCMem > CCField
Solver< Staggered > IbmSolver
Kokkos::View< const double *, CCMem > CCConst
steady(s, probe, maxit=3000, tol=1e-9)
static std::vector< double > sphereSdf()
static constexpr double AC
static constexpr double U
static bool same(double a, double b)
static constexpr double AX