flow 1.3.0
Kokkos cut-cell IBM incompressible Navier-Stokes solver + pnm pore extraction
Loading...
Searching...
No Matches
test_superficial_velocity_mpi.cpp
Go to the documentation of this file.
1// flow — set_superficial_velocity under MPI: the on-device superficial-velocity
2// (prescribed-net-flux) constraint.
3//
4// `setSuperficialVelocity(true, axis, U)` adds, at the end of every step(), one uniform shift to
5// every inner face velocity of component `axis` so that its volume mean is U. It replaces a Python
6// driver that did `u = s.get_u(); s.set_velocity(0, u - u.mean())` after each step (the
7// bubble-column benchmark's closed column): the same operation, but a device reduction (a global
8// Allreduce under MPI) instead of a full-field round trip through the host.
9//
10// Case: 32^3, x and y periodic, no-slip walls on +-z (np = 4 is 2x2x1: the ORB cuts x and y, never
11// the wall-normal axis), a uniform body force f_x plus six Gaussian force_x blobs (a 3-D field, so
12// the reduction has something to reorder), constant rho, MG-PCG pinned at rtol 1e-12, STEPS steps,
13// target superficial velocity U = 0.02 (not 0, so the conversion of the target is exercised too).
14//
15// Gates:
16// * `driver` — the device constraint against the host-driver semantics it replaces: a
17// single-rank reference WITHOUT the constraint whose velocity is shifted on the
18// host between steps (getVelocity -> subtract mean - U -> setVelocity). np = 1 at
19// the reduction-order floor (host sum vs device tree), np > 1 at the MG-PCG floor
20// of test_cellforce_mpi (1e-11 of max|u|).
21// * `mean` — the global volume mean of u after the last step equals U to 1e-13 of max|u|.
22// * `active` — the constraint had something to remove: |last shift| > 1e-3 (the force drives a
23// net flow of order 1e-1 per step).
24// * `refuse` — the constraint on the wall-bounded z axis makes step() raise on every rank.
25#include <mpi.h>
26
27#include <cmath>
28#include <cstdio>
29#include <cstring>
30#include <Kokkos_Core.hpp>
31#include <stdexcept>
32#include <string>
33#include <vector>
34
35#include "flow_ibm.hpp"
36#include "peclet/core/common/types.hpp"
37#include "peclet/core/decomp/block_decomposer.hpp"
38
40
41static constexpr int N = 32, STEPS = 3;
42static constexpr std::size_t GCELLS = (std::size_t)N * N * N;
43static constexpr double U_SUP = 0.02;
44
45// Six blobs along x at (i + 1/2) N/6, y = 12, z = 10 (off-centre: the flow is fully 3-D).
46static std::vector<double> blobs() {
47 std::vector<double> f(GCELLS, 0.0);
48 for (int z = 0; z < N; ++z)
49 for (int y = 0; y < N; ++y)
50 for (int x = 0; x < N; ++x) {
51 double s = 0.0;
52 for (int i = 0; i < 6; ++i) {
53 const double dx = x + 0.5 - (i + 0.5) * N / 6.0, dy = y + 0.5 - 12.0, dz = z + 0.5 - 10.0;
54 s += 0.5 * std::exp(-(dx * dx + dy * dy + dz * dz) / 8.0);
55 }
56 f[(std::size_t)x + (std::size_t)y * N + (std::size_t)z * N * N] = s;
57 }
58 return f;
59}
60
61static std::vector<double> slice(const std::vector<double>& g, int ox, int oy, int oz, int lnx,
62 int lny, int lnz) {
63 std::vector<double> l((std::size_t)lnx * lny * lnz);
64 for (int z = 0; z < lnz; ++z)
65 for (int y = 0; y < lny; ++y)
66 for (int x = 0; x < lnx; ++x)
67 l[(std::size_t)x + (std::size_t)y * lnx + (std::size_t)z * lnx * lny] =
68 g[(std::size_t)(x + ox) + (std::size_t)(y + oy) * N + (std::size_t)(z + oz) * N * N];
69 return l;
70}
71
72// BCs first (they are folded into the operators the geometry builds), then the geometry.
73static void build(IbmSolver& s, std::size_t cells) {
74 s.setDomainBc(4, 1, 0.0, 0.0, 0.0); // -z wall
75 s.setDomainBc(5, 1, 0.0, 0.0, 0.0); // +z wall
76 s.setPressureGeometry(std::vector<double>(cells, 10.0));
77}
78static void configure(IbmSolver& s, const std::vector<double>& forceLocal) {
79 s.setRho(1.0);
80 s.setMu(0.05);
81 s.setDt(0.5);
82 s.setBodyForce(0.02, 0.0, 0.0);
83 s.setPressurePcg(true, 200, 1e-12);
85 s.setField("force_x", forceLocal);
86}
87
88static std::vector<double> gatherGlobal(const std::vector<double>& local, int ox, int oy, int oz,
89 int lnx, int lny, int lnz, int rank, int size) {
90 std::vector<double> global;
91 if (rank == 0)
92 global.assign(GCELLS, 0.0);
93 for (int r = 0; r < size; ++r) {
94 int meta[6] = {ox, oy, oz, lnx, lny, lnz};
95 if (r == 0) {
96 if (rank == 0)
97 for (int z = 0; z < lnz; ++z)
98 for (int y = 0; y < lny; ++y)
99 std::memcpy(&global[(std::size_t)ox + (std::size_t)(y + oy) * N +
100 (std::size_t)(z + oz) * N * N],
101 &local[(std::size_t)y * lnx + (std::size_t)z * lnx * lny],
102 (std::size_t)lnx * sizeof(double));
103 continue;
104 }
105 if (rank == r) {
106 MPI_Send(meta, 6, MPI_INT, 0, 100 + r, MPI_COMM_WORLD);
107 MPI_Send(local.data(), (int)local.size(), MPI_DOUBLE, 0, 200 + r, MPI_COMM_WORLD);
108 } else if (rank == 0) {
110 std::vector<double> buf((std::size_t)meta[3] * meta[4] * meta[5]);
111 MPI_Recv(buf.data(), (int)buf.size(), MPI_DOUBLE, r, 200 + r, MPI_COMM_WORLD,
113 for (int z = 0; z < meta[5]; ++z)
114 for (int y = 0; y < meta[4]; ++y)
115 std::memcpy(&global[(std::size_t)meta[0] + (std::size_t)(y + meta[1]) * N +
116 (std::size_t)(z + meta[2]) * N * N],
117 &buf[(std::size_t)y * meta[3] + (std::size_t)z * meta[3] * meta[4]],
118 (std::size_t)meta[3] * sizeof(double));
119 }
120 }
121 return global;
122}
123
124// NaN-propagating (std::fmax drops a NaN; see test_bodyforce_ghost_mpi).
125static double maxAbsDiff(const std::vector<double>& a, const std::vector<double>& b) {
126 double m = 0;
127 for (std::size_t i = 0; i < b.size(); ++i) {
128 const double d = std::fabs(a[i] - b[i]);
129 if (!(d == d))
130 return d;
131 m = std::fmax(m, d);
132 }
133 return m;
134}
135static double maxAbs(const std::vector<double>& a) {
136 double m = 0;
137 for (double v : a)
138 m = std::fmax(m, std::fabs(v));
139 return m;
140}
141static double mean(const std::vector<double>& a) {
142 double s = 0;
143 for (double v : a)
144 s += v;
145 return s / (double)a.size();
146}
147
148int main(int argc, char** argv) {
149 MPI_Init(&argc, &argv);
150 Kokkos::initialize(argc, argv);
151 int fail = 0;
152 {
153 int rank = 0, size = 1;
156 auto dec = peclet::flow::CutcellMG::decomposition(static_cast<std::size_t>(size), N, N, N);
157 auto blk = dec.block(rank);
158 const int ox = (int)blk.origin[0], oy = (int)blk.origin[1], oz = (int)blk.origin[2];
159 const int lnx = (int)blk.size[0], lny = (int)blk.size[1], lnz = (int)blk.size[2];
160 const std::size_t lcells = (std::size_t)lnx * lny * lnz;
161 if (rank == 0)
162 std::printf("SUPERFICIAL VELOCITY MPI np=%d grid %d^3 block %dx%dx%d\n", size, N, lnx, lny,
163 lnz);
164 const std::vector<double> fg = blobs();
165
166 // ---- driver + mean + active -------------------------------------------------------------
168 sd.initMpi(dec, MPI_COMM_WORLD);
169 build(sd, lcells);
170 configure(sd, slice(fg, ox, oy, oz, lnx, lny, lnz));
172 for (int it = 0; it < STEPS; ++it)
173 sd.step();
174 const double shift = sd.lastSuperficialVelocityShift();
175 std::vector<double> gu[3];
176 for (int c = 0; c < 3; ++c)
177 gu[c] = gatherGlobal(sd.getVelocity(c), ox, oy, oz, lnx, lny, lnz, rank, size);
178
179 if (rank == 0) {
180 IbmSolver ref(N, N, N);
181 build(ref, GCELLS);
182 configure(ref, fg);
183 for (int it = 0; it < STEPS; ++it) {
184 ref.step();
185 std::vector<double> u = ref.getVelocity(0);
186 const double ub = mean(u) - U_SUP;
187 for (double& v : u)
188 v -= ub;
189 ref.setVelocity(0, u);
190 }
191 double du = 0, umag = 0;
192 for (int c = 0; c < 3; ++c) {
193 du = std::fmax(du, maxAbsDiff(gu[c], ref.getVelocity(c)));
194 umag = std::fmax(umag, maxAbs(ref.getVelocity(c)));
195 }
196 const double tol = (size == 1 ? 1e-13 : 1e-11) * umag;
197 const bool okD = du <= tol && umag > 1e-2;
198 std::printf(" [driver np=%d] max|du| = %.3e (tol %.1e) max|u| = %.3e %s\n", size, du, tol,
199 umag, okD ? "OK" : "FAIL");
200 const double dm = std::fabs(mean(gu[0]) - U_SUP);
201 const bool okM = dm <= 1e-13 * umag;
202 std::printf(" [mean np=%d] |<u> - U| = %.3e %s\n", size, dm, okM ? "OK" : "FAIL");
203 const bool okA = std::fabs(shift) > 1e-3;
204 std::printf(" [active np=%d] last shift = %.6e %s\n", size, shift, okA ? "OK" : "FAIL");
205 if (!(okD && okM && okA))
206 fail = 1;
207 }
209
210 // ---- refuse: the wall-bounded axis --------------------------------------------------------
211 {
212 IbmSolver s(lnx, lny, lnz);
213 s.initMpi(dec, MPI_COMM_WORLD);
214 build(s, lcells);
215 configure(s, slice(fg, ox, oy, oz, lnx, lny, lnz));
216 s.setSuperficialVelocity(true, 2, 0.0);
217 bool threw = false;
218 try {
219 s.step();
220 } catch (const std::runtime_error&) {
221 threw = true;
222 }
223 int t = threw ? 1 : 0, all = 0;
225 if (rank == 0)
226 std::printf(" [refuse np=%d] wall-bounded axis %s %s\n", size,
227 all ? "raised" : "was ACCEPTED", all ? "OK" : "FAIL");
228 if (!all)
229 fail = 1;
230 }
231 if (rank == 0)
232 std::printf("SUPERFICIAL VELOCITY MPI (np=%d): %s\n", size, fail ? "FAIL" : "PASS");
233 }
234 Kokkos::finalize();
235 MPI_Finalize();
236 return fail;
237}
void setDomainBc(int face, int type, double vx, double vy, double vz)
void setPressureGeometry(const std::vector< double > &sdfInner)
void setField(const std::string &name, const std::vector< double > &v)
double lastSuperficialVelocityShift() const
The shift the last step() applied (caller's velocity units; 0 when off).
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 setRho(double r)
Fluid density, in the caller's units.
void setSuperficialVelocity(bool on, int axis, double velocity)
Hold the SUPERFICIAL velocity along a periodic axis: at the end of every step() one uniform shift is ...
int main()
Definition dbg_scene.cpp:13
flow — host-facing Kokkos IBM Navier-Stokes solver (drop-in flow-style API).
static double maxAbs(const std::vector< double > &a)
static constexpr double U_SUP
static void build(IbmSolver &s, std::size_t cells)
static void configure(IbmSolver &s, const std::vector< double > &forceLocal)
static double mean(const std::vector< double > &a)
static std::vector< double > gatherGlobal(const std::vector< double > &local, int ox, int oy, int oz, int lnx, int lny, int lnz, int rank, int size)
static constexpr int STEPS
static constexpr int N
static double maxAbsDiff(const std::vector< double > &a, const std::vector< double > &b)
static std::vector< double > slice(const std::vector< double > &g, int ox, int oy, int oz, int lnx, int lny, int lnz)
static std::vector< double > blobs()
static constexpr std::size_t GCELLS