60 const double hx =
LBOX / nx, hy =
LBOX / ny, hz =
LBOX / nz, dref = hx;
61 const double hp[3] = {hx / dref, hy / dref, hz / dref};
62 const double wx = 1.0 / (hp[0] * hp[0]), wy = 1.0 / (hp[1] * hp[1]), wz = 1.0 / (hp[2] * hp[2]);
63 const std::size_t n = (std::size_t)ext.
x * ext.
y * ext.
z;
64 CCField sdf(
"sdf", n), ox(
"ox", n), oy(
"oy", n), oz(
"oz", n);
66 auto m = Kokkos::create_mirror_view(sdf);
67 for (
int z = 0; z < ext.
z; ++z)
68 for (
int y = 0; y < ext.
y; ++y)
69 for (
int x = 0; x < ext.
x; ++x)
70 m((
long)x + (
long)y * ext.
x + (
long)z * (
long)ext.
x * ext.
y) =
71 sdfAt(x -
G + og.x, y -
G + og.y, z -
G + og.z, nx, ny, nz, dref);
72 Kokkos::deep_copy(sdf, m);
74 buildOpenness(ox, oy, oz, CCConst(sdf), ext, hp[0], hp[1], hp[2], 1);
75 mg.
setOpenness(CCConst(ox), CCConst(oy), CCConst(oz), wx, wy, wz);
80 std::vector<double> hb(n, 0.0);
82 auto hAC = Kokkos::create_mirror_view(mg.
level(0).
AC);
83 Kokkos::deep_copy(hAC, mg.
level(0).
AC);
84 const double k = 2.0 * M_PI /
LBOX;
85 double loc[2] = {0.0, 0.0};
86 for (
int z =
G; z < ext.
z -
G; ++z)
87 for (
int y =
G; y < ext.
y -
G; ++y)
88 for (
int x =
G; x < ext.
x -
G; ++x) {
90 (std::size_t)x + (std::size_t)y * ext.
x + (std::size_t)z * (std::size_t)ext.
x * ext.
y;
93 const double px = (x -
G + og.x + 0.5) * (
LBOX / nx),
94 py = (y -
G + og.y + 0.5) * (
LBOX / ny),
95 pz = (z -
G + og.z + 0.5) * (
LBOX / nz);
96 hb[i] = std::cos(k * px) * std::cos(k * py) * std::cos(k * pz);
100 double glob[2] = {loc[0], loc[1]};
102 MPI_Allreduce(loc, glob, 2, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);
103 const double mean = glob[1] > 0.0 ? glob[0] / glob[1] : 0.0;
104 for (
int z =
G; z < ext.
z -
G; ++z)
105 for (
int y =
G; y < ext.
y -
G; ++y)
106 for (
int x =
G; x < ext.
x -
G; ++x) {
107 const std::size_t i =
108 (std::size_t)x + (std::size_t)y * ext.
x + (std::size_t)z * (std::size_t)ext.
x * ext.
y;
113 CCField b(
"b", n), x(
"x", n), r(
"r", n), p(
"p", n), z(
"z", n), Ap(
"Ap", n);
115 auto m = Kokkos::create_mirror_view(b);
116 for (std::size_t i = 0; i < n; ++i)
118 Kokkos::deep_copy(b, m);
120 *iters = mg.
solvePCG(b, x, r, p, z, Ap, 500, 1e-10, 2, 2, 12);
121 auto hx2 = Kokkos::create_mirror_view(x);
122 Kokkos::deep_copy(hx2, x);
126int main(
int argc,
char** argv) {
127 MPI_Init(&argc, &argv);
128 Kokkos::initialize(argc, argv);
129 int fail = 0, size = 1, rank = 0;
131 MPI_Comm_rank(MPI_COMM_WORLD, &rank);
132 MPI_Comm_size(MPI_COMM_WORLD, &size);
133 const int nx =
N, ny = 2 *
N, nz =
N / 2;
134 const double hp[3] = {1.0, 0.5, 2.0};
139 ref.init(nx, ny, nz,
NLEV);
140 const C3 re{nx + 2 *
G, ny + 2 *
G, nz + 2 *
G}, ro{0, 0, 0};
142 auto hr =
setupAndSolve(ref, re, ro, nx, ny, nz,
false, &refIt);
143 const std::vector<C3> refTab = ref.levelRatios();
151 mg.initMpi(nx, ny, nz,
NLEV, MPI_COMM_WORLD);
152 CutcellMG::Level& l0 = mg.
level(0);
154 auto hx =
setupAndSolve(mg, l0.ext, l0.og, nx, ny, nz,
true, &it);
159 int nl = (int)tab.size(), nlMin = 0, nlMax = 0;
160 MPI_Allreduce(&nl, &nlMin, 1, MPI_INT, MPI_MIN, MPI_COMM_WORLD);
161 MPI_Allreduce(&nl, &nlMax, 1, MPI_INT, MPI_MAX, MPI_COMM_WORLD);
162 if (nlMin != nlMax) {
164 std::fprintf(stderr,
"[rank %d] level COUNT differs across ranks: %d..%d\n", rank, nlMin,
167 std::vector<int> mine(3 * (std::size_t)nl), lo(3 * (std::size_t)nl),
168 hi(3 * (std::size_t)nl);
169 for (
int L = 0; L < nl; ++L) {
170 mine[3 * (std::size_t)L] = tab[(std::size_t)L].x;
171 mine[3 * (std::size_t)L + 1] = tab[(std::size_t)L].y;
172 mine[3 * (std::size_t)L + 2] = tab[(std::size_t)L].z;
174 MPI_Allreduce(mine.data(), lo.data(), 3 * nl, MPI_INT, MPI_MIN, MPI_COMM_WORLD);
175 MPI_Allreduce(mine.data(), hi.data(), 3 * nl, MPI_INT, MPI_MAX, MPI_COMM_WORLD);
176 for (
int i = 0; i < 3 * nl; ++i)
177 if (lo[(std::size_t)i] != hi[(std::size_t)i]) {
179 std::fprintf(stderr,
"[rank %d] level table entry %d differs across ranks\n", rank, i);
184 std::printf(
" level table (np=%d):", size);
185 for (
const C3& rr : tab)
186 std::printf(
" (%d,%d,%d)", rr.x, rr.y, rr.z);
187 std::printf(
"\n single-rank reference: ");
188 for (
const C3& rr : refTab)
189 std::printf(
" (%d,%d,%d)", rr.x, rr.y, rr.z);
193 if (!(tab[0].x == 1 && tab[0].y == 2 && tab[0].z == 1)) {
195 std::fprintf(stderr,
"[rank %d] level 0 ratio (%d,%d,%d), expected (1,2,1)\n", rank,
196 tab[0].x, tab[0].y, tab[0].z);
203 for (
int L = 0; L < mg.
nLevels(); ++L)
204 if (mg.
level(L).tele)
207 MPI_Allreduce(&tele, &teleAll, 1, MPI_INT, MPI_MAX, MPI_COMM_WORLD);
211 "[rank %d] %d level(s) telescoped with the economic trigger off — an "
212 "aspect-DEFERRED axis was treated as BLOCKED (trap 6)\n",
214 }
else if (rank == 0)
215 std::printf(
" trap 6: no telescope on the deferred hierarchy (min-extent trigger off)\n");
220 for (std::size_t i = 0; i < hr.extent(0); ++i)
221 if (!(std::fabs(hr(i)) <= xref))
222 xref = std::fabs(hr(i));
225 for (
int z =
G; z < ext.z -
G; ++z)
226 for (
int y =
G; y < ext.y -
G; ++y)
227 for (
int x =
G; x < ext.x -
G; ++x) {
228 const int gx = x -
G + og.x, gy = y -
G + og.y, gz = z -
G + og.z;
229 const double a = h((
long)x + (
long)y * ext.x + (
long)z * (
long)ext.x * ext.y);
231 hr((
long)(gx +
G) + (
long)(gy +
G) * re.x + (
long)(gz +
G) * (
long)re.x * re.y);
232 const double d = std::fabs(a - rr);
248 const double tol = (size == 1) ? 0.0 : 1e-6 * xref;
249 const double d1 =
blockDiff(hx, l0.ext, l0.og);
253 "[rank %d] pass 1 max|distributed - single-rank| = %.3e (rel %.3e, tol %.3e)\n",
254 rank, d1, d1 / xref, tol);
255 }
else if (rank == 0)
257 " pass 1: %d iters (single-rank %d), max|dist - single| = %.3e = %.3e relative "
258 "of max|phi| = %.4f (np=%d%s)\n",
259 it, refIt, d1, d1 / xref, xref, size, size == 1 ?
", BIT-EXACT" :
"");
260 if (std::abs(it - refIt) > 2) {
262 std::fprintf(stderr,
"[rank %d] pass 1 iterations %d vs single-rank %d (allowed +/-2)\n",
265 if (it >= 500 || refIt >= 500) {
267 std::fprintf(stderr,
"[rank %d] pass 1 CAPPED (%d / %d iters)\n", rank, it, refIt);
277 mg2.initMpi(nx, ny, nz,
NLEV, MPI_COMM_WORLD);
278 CutcellMG::Level& m0 = mg2.
level(0);
280 auto hx2 =
setupAndSolve(mg2, m0.ext, m0.og, nx, ny, nz,
true, &it2);
281 int forced = mg2.
level(1).tele ? 1 : 0, forcedAll = 0;
282 MPI_Allreduce(&forced, &forcedAll, 1, MPI_INT, MPI_MAX, MPI_COMM_WORLD);
283 const double d2 =
blockDiff(hx2, m0.ext, m0.og);
284 if (forcedAll != 1) {
286 std::fprintf(stderr,
"[rank %d] pass 2: setTelescopeForceLevel(1) did not merge\n", rank);
288 if (!(d2 <= 1e-6 * xref)) {
290 std::fprintf(stderr,
"[rank %d] pass 2 max|telescoped - single-rank| = %.3e (rel %.3e)\n",
291 rank, d2, d2 / xref);
292 }
else if (rank == 0)
294 " pass 2 (telescope forced at level 1): %d iters, max|dist - single| = %.3e "
298 std::printf(
" pass 2: the aspect rule still owns level 0 across the merge\n");
302 MPI_Allreduce(&fail, &totalFail, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD);
305 std::printf(
"OK (np=%d): the anisotropic CutcellMG hierarchy, distributed\n", size);
307 std::fprintf(stderr,
"FAILED (np=%d): %d rank(s) failed\n", size, totalFail);
311 return totalFail ? 1 : 0;
int solvePCG(CCField b, CCField x, CCField r, CCField p, CCField z, CCField Ap, int maxit, double rtol, int pre, int post, int bottom, const StarOverlay *star=nullptr, int nStar=0, C3 nnStar=C3{0, 0, 0})