flow 1.3.0
Kokkos cut-cell IBM incompressible Navier-Stokes solver + pnm pore extraction
Loading...
Searching...
No Matches
test_predict_weighted_mpi.cpp
Go to the documentation of this file.
1// flow — the pure pressure-hierarchy forecast (CutcellMG::predict, Python flow.predict_hierarchy)
2// against the hierarchy the distributed Solver actually builds, before and after
3// rebalanceByWeights.
4//
5// For each grid and weight field, on np ranks:
6//
7// A the Solver on its default (aligned ORB) partition: its built pressure-MG table equals
8// predict(G, np, levels, telescope ON) row for row — global dims, ranks holding the level, the
9// level's block 0, the ratio to the next level, and the kind of stage out of it;
10// B after rebalanceByWeights(w): the built table equals predict(..., &w, &align) row for row,
11// and `align` equals the alignment 2^a the rebalance returned (core's chooseAlignedWeighted at
12// its 1.05 budget, a pure function of (np, G, w)).
13//
14// The comparison is exact (integers and flags). Coverage is gated, so the test cannot pass on
15// trivial hierarchies: at np > 1 at least one weighted case must telescope and at least one must
16// be a Repartition stage (the kind only a weighted dec0 arms), and some case must align above 1.
17// Build with -DPECLET_FLOW_MPI.
18#include <mpi.h>
19
20#include <cmath>
21#include <cstdio>
22#include <Kokkos_Core.hpp>
23#include <string>
24#include <vector>
25
26#include "flow_ibm.hpp"
27#include "peclet/core/common/types.hpp"
28
31using Row = CutcellMG::PlanRow;
32
33static constexpr int LEVELS = 8, MIN_EXTENT = 4;
34
35static bool sameRow(const Row& a, const Row& b) {
36 auto eq = [](peclet::flow::C3 p, peclet::flow::C3 q) {
37 return p.x == q.x && p.y == q.y && p.z == q.z;
38 };
39 return eq(a.global, b.global) && eq(a.block, b.block) && eq(a.ratio, b.ratio) &&
40 a.ranks == b.ranks && a.tele == b.tele && a.repartition == b.repartition;
41}
42
43static void printRows(const char* tag, const std::vector<Row>& rows) {
44 for (std::size_t L = 0; L < rows.size(); ++L) {
45 const Row& r = rows[L];
46 std::printf(
47 " %s L%zu global %3dx%3dx%3d ranks %2d block0 %3dx%3dx%3d ratio(%d,%d,%d)%s\n", tag,
48 L, r.global.x, r.global.y, r.global.z, r.ranks, r.block.x, r.block.y, r.block.z, r.ratio.x,
49 r.ratio.y, r.ratio.z,
50 r.tele ? (r.repartition ? " -> REPARTITION" : " -> TELESCOPE") : "");
51 }
52}
53
54// Rank 0 compares (it holds every level); the verdict is broadcast so every rank exits alike.
55static bool compare(const char* what, const std::vector<Row>& built,
56 const std::vector<Row>& predicted, int rank) {
57 int ok = 1;
58 if (rank == 0) {
59 ok = built.size() == predicted.size();
60 for (std::size_t L = 0; ok && L < built.size(); ++L)
61 ok = sameRow(built[L], predicted[L]);
62 std::printf(" %s: %zu levels built, %zu predicted -> %s\n", what, built.size(),
63 predicted.size(), ok ? "match" : "MISMATCH");
64 if (!ok) {
65 printRows("built ", built);
66 printRows("predicted", predicted);
67 }
68 }
69 MPI_Bcast(&ok, 1, MPI_INT, 0, MPI_COMM_WORLD);
70 return ok != 0;
71}
72
73struct Grid3 {
74 int x, y, z;
75};
76
77// The weight fields: a heavy lower half in z (the balanced-force / redistribute tests' kind), a
78// smooth off-centre bump (odd split planes), uniform weights (the alignment can go deepest), and a
79// step in x balanced exactly on the ODD plane x = 13 (weights G.x-13 below it, 13 above). On 32^3
80// either even neighbour costs a 1.077 imbalance: a = 0, an odd level-0 split, so even at np = 2
81// the stage out of level 0 is a Repartition (measured; on 48x32x40 an even plane fits the budget).
82static std::vector<peclet::core::Real> weights(int kind, Grid3 g) {
83 std::vector<peclet::core::Real> w((std::size_t)g.x * g.y * g.z, 1.0);
84 for (int z = 0; z < g.z; ++z)
85 for (int y = 0; y < g.y; ++y)
86 for (int x = 0; x < g.x; ++x) {
87 double v = 1.0;
88 if (kind == 0)
89 v = z < g.z / 2 ? 4.0 : 1.0;
90 else if (kind == 1) {
91 const double dx = (x + 0.5) / g.x - 0.3, dy = (y + 0.5) / g.y - 0.6,
92 dz = (z + 0.5) / g.z - 0.35;
93 v = 1.0 + 6.0 * std::exp(-(dx * dx + dy * dy + dz * dz) / 0.02);
94 } else if (kind == 3)
95 v = x < 13 ? g.x - 13 : 13;
96 w[(std::size_t)x + (std::size_t)y * g.x + (std::size_t)z * g.x * g.y] = v;
97 }
98 return w;
99}
100
101int main(int argc, char** argv) {
102 MPI_Init(&argc, &argv);
103 Kokkos::initialize(argc, argv);
104 int fail = 0, size = 1, rank = 0;
105 {
108 int teleCases = 0, repartCases = 0, alignedCases = 0;
109 const Grid3 grids[] = {{32, 32, 32}, {48, 32, 40}};
110 const char* names[] = {"half-z x4", "bump", "uniform", "odd step x=13"};
111 for (const Grid3 g : grids) {
112 for (int kind = 0; kind < 4; ++kind) {
113 if (rank == 0)
114 std::printf("grid %dx%dx%d, weights '%s', np=%d\n", g.x, g.y, g.z, names[kind], size);
115 const auto dec =
116 CutcellMG::decomposition((std::size_t)size, g.x, g.y, g.z); // what initMpi builds
117 const auto blk = dec.block((std::size_t)rank);
118 const int lnx = (int)blk.size[0], lny = (int)blk.size[1], lnz = (int)blk.size[2];
119 IbmSolver s(lnx, lny, lnz);
120 s.initMpi(g.x, g.y, g.z, MPI_COMM_WORLD);
121 s.setRho(1.0);
122 s.setMu(0.1);
123 s.setDt(0.02);
125 s.setPressurePcg(true, 200, 1e-6);
126 s.setSolid(std::vector<double>((std::size_t)lnx * lny * lnz, 1e30), true);
127
128 // A: the default partition.
129 const auto predA = CutcellMG::predict(g.x, g.y, g.z, size, LEVELS, true, MIN_EXTENT);
130 if (!compare("A default partition", s.pressureMgPlan(), predA, rank))
131 fail = 1;
132
133 // B: after the weighted rebalance.
134 const auto w = weights(kind, g);
135 int alignPred = 0;
136 const auto predB = CutcellMG::predict(g.x, g.y, g.z, size, LEVELS, true, MIN_EXTENT, 0,
137 1.05, &w, &alignPred);
138 const int alignGot = s.rebalanceByWeights(w);
139 const auto built = s.pressureMgPlan();
140 if (!compare("B after rebalanceByWeights", built, predB, rank))
141 fail = 1;
142 if (rank == 0)
143 std::printf(" alignment: rebalanceByWeights %d, predict %d -> %s\n", alignGot, alignPred,
144 alignGot == alignPred ? "match" : "MISMATCH");
145 if (alignGot != alignPred)
146 fail = 1;
147 if (rank == 0) {
148 bool tele = false, rep = false;
149 for (const Row& r : built) {
150 tele = tele || r.tele;
151 rep = rep || r.repartition;
152 }
153 teleCases += tele;
154 repartCases += rep;
155 alignedCases += alignGot > 1;
156 printRows("", built);
157 }
158 }
159 }
160 if (rank == 0) {
161 std::printf("coverage: %d weighted case(s) telescope, %d repartition, %d align > 1\n",
163 if (alignedCases == 0 || (size > 1 && (teleCases == 0 || repartCases == 0))) {
164 std::printf("FAIL: the cases no longer exercise what this test gates\n");
165 fail = 1;
166 }
167 }
169 if (rank == 0)
170 std::printf("%s\n", fail ? "FAIL" : "PASS");
171 }
172 Kokkos::finalize();
173 MPI_Finalize();
174 return fail;
175}
void setSolid(const std::vector< double > &sdfInner, bool cutcellPressure)
Host entry point: upload the inner SDF once and delegate.
void setPressureLevels(int levels)
void setPressurePcg(bool on, int maxit, double rtol)
void setMu(double m)
Dynamic viscosity, in the caller's units.
void setRho(double r)
Fluid density, in the caller's units.
int main()
Definition dbg_scene.cpp:13
flow — host-facing Kokkos IBM Navier-Stokes solver (drop-in flow-style API).
static constexpr int MIN_EXTENT
static bool sameRow(const Row &a, const Row &b)
static std::vector< peclet::core::Real > weights(int kind, Grid3 g)
static void printRows(const char *tag, const std::vector< Row > &rows)
static bool compare(const char *what, const std::vector< Row > &built, const std::vector< Row > &predicted, int rank)
CutcellMG::PlanRow Row
static constexpr int LEVELS