flow 1.3.0
Kokkos cut-cell IBM incompressible Navier-Stokes solver + pnm pore extraction
Loading...
Searching...
No Matches
test_openbc_solid_mpi.cpp
Go to the documentation of this file.
1// Immersed solid TOGETHER with inflow/outflow domain BCs, DISTRIBUTED (SCALING_ISSUES #3; the
2// single-rank sibling is tests/kokkos/test_openbc_solid.cpp). Every part of the fix is rank-aware
3// and had to be: the SDF ghost band outside a non-periodic face is repaired only on the rank that
4// owns that global face, and the boundary-face APERTURE PLANE is a ghost index that the openness
5// halo exchange, the per-level fill and (across a telescope point) the plane gather all wrap over.
6// That last one is why this test earns its keep -- it caught a third defect, visible AT np = 1,
7// which the fix's first two halves had made matter: the exchange handed the outlet the INLET's
8// aperture (1.8e-02 / 6.6e-01 from single-rank; 0.00e+00 after).
9//
10// So the gate is the usual one: the distributed run reproduces the single-rank run on the block it
11// owns -- for a bed clear of the open faces, beds cutting each of them, and a bed cutting a wall
12// (the control that says the SDF ghost extension itself is rank-consistent). The geometry is a
13// local computation, so the openness fields must agree EXACTLY, not to a tolerance.
14#include <mpi.h>
15
16#include <algorithm>
17#include <cmath>
18#include <cstdio>
19#include <Kokkos_Core.hpp>
20#include <vector>
21
22#include "flow_ibm.hpp"
23#include "peclet/core/common/types.hpp"
24#include "peclet/core/decomp/block_decomposer.hpp"
25
27
28static constexpr int N = 32, STEPS = 4, PMAXIT = 200;
29static constexpr double RHO = 1.0, MU = 1.0, UIN = 1.0, DT = 0.5, R = 0.27 * N;
30
31static std::vector<double> sphereSdf(double cx) {
32 std::vector<double> sdf((std::size_t)N * N * N);
33 const double cy = 0.5 * (N - 1), cz = 0.5 * (N - 1);
34 for (int z = 0; z < N; ++z)
35 for (int y = 0; y < N; ++y)
36 for (int x = 0; x < N; ++x) {
37 const double dx = x - cx, dy = y - cy, dz = z - cz;
38 sdf[(std::size_t)x + (std::size_t)y * N + (std::size_t)z * N * N] =
39 std::sqrt(dx * dx + dy * dy + dz * dz) - R;
40 }
41 return sdf;
42}
43
44static std::vector<double> sphereSdfY(double cx) { // centred ON the -y wall plane
45 std::vector<double> sdf((std::size_t)N * N * N);
46 const double cy = -0.5, cz = 0.5 * (N - 1);
47 for (int z = 0; z < N; ++z)
48 for (int y = 0; y < N; ++y)
49 for (int x = 0; x < N; ++x) {
50 const double dx = x - cx, dy = y - cy, dz = z - cz;
51 sdf[(std::size_t)x + (std::size_t)y * N + (std::size_t)z * N * N] =
52 std::sqrt(dx * dx + dy * dy + dz * dz) - R;
53 }
54 return sdf;
55}
56
57static void configure(IbmSolver& s) {
58 s.setRho(RHO);
59 s.setMu(MU);
60 s.setDt(DT);
61 s.setAdvection(false);
62 s.setVelocityIterations(200);
63 s.setVelocityResidualTolerance(1e-10);
64 s.setPressureLevels(3);
65 s.setPressurePcg(true, PMAXIT, 1e-10);
66 s.setDomainBc(0, 2, UIN, 0, 0); // -x inflow
67 s.setDomainBc(1, 3, 0, 0, 0); // +x outflow
68 for (int f = 2; f < 6; ++f)
69 s.setDomainBc(f, 1, 0, 0, 0); // y/z no-slip walls
70}
71
72int main(int argc, char** argv) {
73 MPI_Init(&argc, &argv);
74 Kokkos::initialize(argc, argv);
75 int fail = 0, size = 1, rank = 0;
76 MPI_Comm_rank(MPI_COMM_WORLD, &rank);
77 MPI_Comm_size(MPI_COMM_WORLD, &size);
78 {
79 const double centres[4] = {0.5 * N, N - 0.5, -0.5, 0.5 * N};
80 const int cutWallY[4] = {0, 0, 0, 1}; // case 3: the sphere is cut by the -y WALL instead
81 const char* label[4] = {"clear of the open faces", "cutting the OUTFLOW face",
82 "cutting the INFLOW face", "cutting the -y WALL"};
83 peclet::core::decomp::BlockDecomposer<3> dec =
84 peclet::flow::CutcellMG::decomposition(static_cast<std::size_t>(size), N, N, N);
85 auto blk = dec.block(rank);
86 const int ox = (int)blk.origin[0], oy = (int)blk.origin[1], oz = (int)blk.origin[2];
87 const int lnx = (int)blk.size[0], lny = (int)blk.size[1], lnz = (int)blk.size[2];
88 for (int cs = 0; cs < 4; ++cs) {
89 const std::vector<double> gsdf =
90 cutWallY[cs] ? sphereSdfY(centres[cs]) : sphereSdf(centres[cs]);
91 std::vector<double> lsdf((std::size_t)lnx * lny * lnz);
92 for (int z = 0; z < lnz; ++z)
93 for (int y = 0; y < lny; ++y)
94 for (int x = 0; x < lnx; ++x)
95 lsdf[(std::size_t)x + (std::size_t)y * lnx + (std::size_t)z * lnx * lny] =
96 gsdf[(std::size_t)(x + ox) + (std::size_t)(y + oy) * N +
97 (std::size_t)(z + oz) * N * N];
98
99 IbmSolver sd(lnx, lny, lnz);
100 sd.initMpi(N, N, N, MPI_COMM_WORLD);
101 configure(sd);
102 sd.setSolid(lsdf, true);
103 long itersD = 0;
104 for (int it = 0; it < STEPS; ++it) {
105 sd.step();
106 itersD = std::max(itersD, sd.lastPressureIterations());
107 }
108 const double divD = sd.maxOpenDivergenceProjected();
109 const std::vector<double> ud = sd.getVelocity(0);
110 std::vector<double> od[3] = {sd.getOpenness(0), sd.getOpenness(1), sd.getOpenness(2)};
111
112 std::vector<double> ur((std::size_t)N * N * N);
113 std::vector<double> orr[3] = {std::vector<double>((std::size_t)N * N * N),
114 std::vector<double>((std::size_t)N * N * N),
115 std::vector<double>((std::size_t)N * N * N)};
116 long itersR = 0;
117 double divR = 0;
118 if (rank == 0) {
119 IbmSolver sr(N, N, N);
120 configure(sr);
121 sr.setSolid(gsdf, true);
122 for (int it = 0; it < STEPS; ++it) {
123 sr.step();
124 itersR = std::max(itersR, sr.lastPressureIterations());
125 }
126 divR = sr.maxOpenDivergenceProjected();
127 ur = sr.getVelocity(0);
128 orr[0] = sr.getOpenness(0);
129 orr[1] = sr.getOpenness(1);
130 orr[2] = sr.getOpenness(2);
131 }
132 MPI_Bcast(ur.data(), (int)ur.size(), MPI_DOUBLE, 0, MPI_COMM_WORLD);
133 MPI_Bcast(&itersR, 1, MPI_LONG, 0, MPI_COMM_WORLD);
134 MPI_Bcast(&divR, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD);
135 for (int q = 0; q < 3; ++q)
136 MPI_Bcast(orr[q].data(), (int)orr[q].size(), MPI_DOUBLE, 0, MPI_COMM_WORLD);
137 {
138 double dop = 0;
139 for (int z = 0; z < lnz; ++z)
140 for (int y = 0; y < lny; ++y)
141 for (int x = 0; x < lnx; ++x)
142 for (int q = 0; q < 3; ++q) {
143 const std::size_t li =
144 (std::size_t)x + (std::size_t)y * lnx + (std::size_t)z * lnx * lny;
145 const std::size_t gi = (std::size_t)(x + ox) + (std::size_t)(y + oy) * N +
146 (std::size_t)(z + oz) * N * N;
147 dop = std::max(dop, std::fabs(od[q][li] - orr[q][gi]));
148 }
149 double gdop = 0;
150 MPI_Allreduce(&dop, &gdop, 1, MPI_DOUBLE, MPI_MAX, MPI_COMM_WORLD);
151 if (gdop != 0.0) { // the geometry is a local computation: it must agree EXACTLY
152 if (rank == 0)
153 std::printf(" openness dist-vs-single max diff %.3e (must be 0)\n", gdop);
154 fail = 1;
155 }
156 }
157
158 double d = 0, umax = 0;
159 for (int z = 0; z < lnz; ++z)
160 for (int y = 0; y < lny; ++y)
161 for (int x = 0; x < lnx; ++x) {
162 const std::size_t li =
163 (std::size_t)x + (std::size_t)y * lnx + (std::size_t)z * lnx * lny;
164 const std::size_t gi =
165 (std::size_t)(x + ox) + (std::size_t)(y + oy) * N + (std::size_t)(z + oz) * N * N;
166 d = std::max(d, std::fabs(ud[li] - ur[gi]));
167 umax = std::max(umax, std::fabs(ur[gi]));
168 }
169 double g[2] = {d, umax}, gg[2];
170 MPI_Allreduce(g, gg, 2, MPI_DOUBLE, MPI_MAX, MPI_COMM_WORLD);
171 const double rel = gg[0] / gg[1];
172 const double tol = (size == 1) ? 1e-13 : 1e-7; // np>1: the PCG's reduction-order floor
173 if (rank == 0)
174 std::printf(
175 "[openbc-solid-mpi] np=%d sphere %-24s dist-vs-single rel %.2e (tol %.0e) "
176 "iters %ld/%ld proj max|div| %.2e/%.2e\n",
177 size, label[cs], rel, tol, itersD, itersR, divD, divR);
178 if (!(rel <= tol))
179 fail = 1;
180 if (itersD >= PMAXIT || itersR >= PMAXIT) // the defect capped MG-PCG on the cut beds
181 fail = 1;
182 if (!(divD < 1e-6) || !(divR < 1e-6)) // converged; the defect sat at 1.0 and 6.7e-03
183 fail = 1;
184 }
185 }
186 int gfail = 0;
187 MPI_Allreduce(&fail, &gfail, 1, MPI_INT, MPI_MAX, MPI_COMM_WORLD);
188 if (rank == 0)
189 std::printf("[openbc-solid-mpi] %s\n", gfail ? "FAILED" : "OK");
190 Kokkos::finalize();
191 MPI_Finalize();
192 return gfail;
193}
void setSolid(const std::vector< double > &sdfInner, bool cutcellPressure)
Host entry point: upload the inner SDF once and delegate.
std::vector< double > getOpenness(int c)
std::vector< double > getVelocity(int c)
int main()
Definition dbg_scene.cpp:13
flow — host-facing Kokkos IBM Navier-Stokes solver (drop-in flow-style API).
Solver< Staggered > IbmSolver
static std::vector< double > sphereSdf()
static constexpr double UIN
static constexpr double RHO
static constexpr double MU
static constexpr double R
static constexpr int STEPS
static constexpr int N
static constexpr double DT
static constexpr int PMAXIT
static std::vector< double > sphereSdfY(double cx)
static void configure(IbmSolver &s)