flow 1.3.0
Kokkos cut-cell IBM incompressible Navier-Stokes solver + pnm pore extraction
Loading...
Searching...
No Matches
test_anderson_mpi.cpp
Go to the documentation of this file.
1// flow — the distributed Anderson accelerator of a steady march (doc/steady_acceleration.md
2// rev 1, gate G4c).
3//
4// The §11 case in cell units (one sphere of solid fraction 0.125 at the centre of a periodic
5// 16^3 box, rho = mu = 1, a body force along x, Stokes) on the staggered Solver, 40 unconditional
6// accelerated steps (AndersonAccelerator::step(true), window 5), two ways: distributed over np
7// ranks (each rank its ORB block, initMpi, its local SDF block) and single-rank on rank 0 (the
8// serial path, the full grid). Gates:
9// * max|u_np - u_1| <= 1e-8 max|u_1| after the 40 steps, every velocity component (np = 1, 2, 4);
10// * gamma, the window size and the status are BITWISE equal on all ranks after every step
11// (rank 0's broadcast decision packet, design §6.1: each rank mixes its own ghosts, which stay
12// equal to the neighbour's mixed inner values only under identical coefficients).
13// Inner solves are tight (PCG rtol 1e-12, velocity residual tolerance 1e-12) and nu dt / h^2 = 0.5,
14// so the momentum solve is red-black Gauss-Seidel on every block size (no V-cycle switch between
15// the 16^3 single-rank block and the 8-cell distributed blocks) and converges to its tolerance:
16// what remains between np is the reduction order of the pressure PCG and of the accelerator's own
17// Gram sums, which is what this gate bounds.
18#include <mpi.h>
19
20#include <algorithm>
21#include <cmath>
22#include <cstdio>
23#include <cstring>
24#include <Kokkos_Core.hpp>
25#include <vector>
26
28#include "flow_ibm.hpp"
29#include "peclet/core/decomp/block_decomposer.hpp"
30
34
35static constexpr int N = 16, STEPS = 40, WINDOW = 5;
36
37static std::vector<double> sphereSdf() {
38 const double R = std::cbrt(3.0 * 0.125 / (4.0 * M_PI)) * N, c = 0.5 * N;
39 std::vector<double> sdf((std::size_t)N * N * N);
40 for (int z = 0; z < N; ++z)
41 for (int y = 0; y < N; ++y)
42 for (int x = 0; x < N; ++x) {
43 const double dx = x + 0.5 - c, dy = y + 0.5 - c, dz = z + 0.5 - c;
44 sdf[(std::size_t)x + (std::size_t)y * N + (std::size_t)z * N * N] =
45 std::sqrt(dx * dx + dy * dy + dz * dz) - R;
46 }
47 return sdf;
48}
49
50static void configure(IbmSolver& s) {
51 s.setRho(1.0);
52 s.setMu(1.0);
53 s.setDt(0.5);
54 s.setBodyForce(1e-3, 0.0, 0.0);
55 s.setAdvection(false);
58 s.setPressurePcg(true, 400, 1e-12);
60}
61
62int main(int argc, char** argv) {
63 MPI_Init(&argc, &argv);
64 Kokkos::initialize(argc, argv);
65 int fail = 0, size = 1, rank = 0;
66 {
69 const std::vector<double> gsdf = sphereSdf();
70
71 // --- distributed ---
72 auto dec =
73 peclet::flow::CutcellMG::decomposition(static_cast<std::size_t>(size), N, N, N, 0, 1.05);
74 auto blk = dec.block(rank);
75 const int org[3] = {(int)blk.origin[0], (int)blk.origin[1], (int)blk.origin[2]};
76 const int ln[3] = {(int)blk.size[0], (int)blk.size[1], (int)blk.size[2]};
77 std::vector<double> lsdf((std::size_t)ln[0] * ln[1] * ln[2]);
78 for (int z = 0; z < ln[2]; ++z)
79 for (int y = 0; y < ln[1]; ++y)
80 for (int x = 0; x < ln[0]; ++x)
81 lsdf[(std::size_t)x + (std::size_t)y * ln[0] + (std::size_t)z * ln[0] * ln[1]] =
82 gsdf[(std::size_t)(x + org[0]) + (std::size_t)(y + org[1]) * N +
83 (std::size_t)(z + org[2]) * N * N];
84 IbmSolver sd(ln[0], ln[1], ln[2]);
85 sd.initMpi(N, N, N, MPI_COMM_WORLD);
87 sd.setSolid(lsdf, true);
88 long gammaMismatch = 0;
89 double lastRes = 0.0;
90 {
92 for (int k = 0; k < STEPS; ++k) {
93 acc.step(true);
94 std::vector<double> g = acc.core().gamma();
95 int head[2] = {(int)g.size(), (int)acc.core().status()};
96 int head0[2] = {head[0], head[1]};
98 std::vector<double> g0 = g;
99 g0.resize((std::size_t)head0[0]);
100 MPI_Bcast(g0.data(), head0[0], MPI_DOUBLE, 0, MPI_COMM_WORLD);
101 if (head[0] != head0[0] || head[1] != head0[1] ||
102 std::memcmp(g.data(), g0.data(), sizeof(double) * g.size()) != 0)
104 }
105 lastRes = acc.residual();
106 if (rank == 0)
107 std::printf(" distributed: status %s, residual %.6e, %d columns, %d restarts\n",
108 acc.statusName(), acc.residual(), acc.numColumns(), acc.numRestarts());
109 }
110 long gm = 0;
112
113 // gather the distributed velocity onto rank 0 (inner blocks, x-fastest)
114 std::vector<std::vector<double>> ug(3);
115 for (int c = 0; c < 3; ++c) {
116 const std::vector<double> loc = sd.getVelocity(c);
117 std::vector<int> meta(6 * size);
118 int mine[6] = {org[0], org[1], org[2], ln[0], ln[1], ln[2]};
119 MPI_Gather(mine, 6, MPI_INT, meta.data(), 6, MPI_INT, 0, MPI_COMM_WORLD);
120 std::vector<int> counts(size), displs(size);
121 int nloc = (int)loc.size();
122 MPI_Gather(&nloc, 1, MPI_INT, counts.data(), 1, MPI_INT, 0, MPI_COMM_WORLD);
123 std::vector<double> all;
124 if (rank == 0) {
125 int off = 0;
126 for (int r = 0; r < size; ++r) {
127 displs[r] = off;
128 off += counts[r];
129 }
130 all.resize((std::size_t)off);
131 }
132 MPI_Gatherv(loc.data(), nloc, MPI_DOUBLE, all.data(), counts.data(), displs.data(),
134 if (rank == 0) {
135 ug[c].assign((std::size_t)N * N * N, 0.0);
136 for (int r = 0; r < size; ++r) {
137 const int* m = &meta[6 * r];
138 for (int z = 0; z < m[5]; ++z)
139 for (int y = 0; y < m[4]; ++y)
140 for (int x = 0; x < m[3]; ++x)
141 ug[c][(std::size_t)(x + m[0]) + (std::size_t)(y + m[1]) * N +
142 (std::size_t)(z + m[2]) * N * N] =
143 all[(std::size_t)displs[r] + (std::size_t)x + (std::size_t)y * m[3] +
144 (std::size_t)z * m[3] * m[4]];
145 }
146 }
147 }
148
149 // --- single-rank reference on rank 0 (the serial path) ---
150 if (rank == 0) {
151 IbmSolver ref(N, N, N);
152 configure(ref);
153 ref.setSolid(gsdf, true);
155 for (int k = 0; k < STEPS; ++k)
156 acc.step(true);
157 std::printf(" single-rank: status %s, residual %.6e (distributed %.6e)\n", acc.statusName(),
158 acc.residual(), lastRes);
159 double worst = 0.0;
160 for (int c = 0; c < 3; ++c) {
161 const std::vector<double> u1 = ref.getVelocity(c);
162 double du = 0.0, umax = 0.0;
163 for (std::size_t i = 0; i < u1.size(); ++i) {
164 du = std::max(du, std::fabs(ug[c][i] - u1[i]));
165 umax = std::max(umax, std::fabs(u1[i]));
166 }
167 const double rel = umax > 0.0 ? du / umax : du;
168 worst = std::max(worst, rel);
169 std::printf(" component %d: max|u_np - u_1| / max|u_1| = %.3e (max|u_1| = %.6e)\n", c, rel,
170 umax);
171 }
172 std::printf(" gamma / window / status mismatches across ranks: %ld\n", gm);
173 if (worst > 1e-8 || gm != 0)
174 fail = 1;
175 }
176 }
177 int totalFail = 0;
179 if (rank == 0) {
180 if (totalFail == 0)
181 std::printf("OK (np=%d): distributed Anderson march == single-rank, gamma identical\n", size);
182 else
183 std::fprintf(stderr, "FAILED (np=%d)\n", size);
184 }
185 Kokkos::finalize();
186 MPI_Finalize();
187 return totalFail == 0 ? 0 : 1;
188}
flow — the Anderson accelerator of a steady march: core's AndersonCore wrapped around one Solver<Grid...
void setSolid(const std::vector< double > &sdfInner, bool cutcellPressure)
Host entry point: upload the inner SDF once and delegate.
void setAdvection(bool on)
void setVelocityIterations(int it)
void setPressureLevels(int levels)
void setBodyForce(double fx, double fy, double fz)
Body force per unit volume, in the caller's units (e.g. a mean pressure gradient, or rho*g).
std::vector< double > getVelocity(int c)
void setPressurePcg(bool on, int maxit, double rtol)
void setMu(double m)
Dynamic viscosity, in the caller's units.
void setVelocityResidualTolerance(double rtol)
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 WINDOW
static constexpr int STEPS
static constexpr int N
static std::vector< double > sphereSdf()
static void configure(IbmSolver &s)