flow 1.3.0
Kokkos cut-cell IBM incompressible Navier-Stokes solver + pnm pore extraction
Loading...
Searching...
No Matches
test_velocitymg_bc_mpi.cpp
Go to the documentation of this file.
1// The MIXED velocity multigrid (immersed solid + domain BCs) distributed: the momentum solve of a
2// sphere in a channel with an inflow, an outflow and four no-slip walls, at a stiff time step
3// (nu*dt/dx^2 = 1e3, the packed-bed-with-inlet regime where the production RB-GS hits its sweep
4// cap). Three solves of the same problem with the REAL IbmSolver:
5// (a) distributed, velocity MG (VelocityMG::setStaircaseBc on the solver's ORB, in place);
6// (b) single-rank, velocity MG -- (a) must equal (b): np=1 bit-tight, np>1 to the MG-PCG
7// pressure solve's reduction-order floor (the momentum V-cycle itself has no reductions
8// except the update-norm stop, which is a max and therefore order-independent);
9// (c) single-rank, RB-GS to a tight update tolerance -- the accuracy gate: the V-cycle must
10// converge to the SAME fixed point (the fine operator is the sharp cut-cell stencil in both).
11// Run twice: with four no-slip walls, and with the +-z walls replaced by FREE-SLIP (symmetry,
12// set_domain_bc type 4) faces -- the same three-way gate exercises the type-4 velocity ghosts,
13// the mixed V-cycle's slip fold and the pressure MG's Neumann rows on a slip face, distributed.
14#include <mpi.h>
15
16#include <cmath>
17#include <cstdio>
18#include <cstdlib>
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
26using peclet::core::IVec;
28
29static constexpr int N = 32, STEPS = 4;
30static constexpr double RHO = 1.0, MU = 1.0, UIN = 1e-3;
31// beta = MU*DT/dx^2; default 1e3 (the packed-bed-with-inlet regime), TEST_DT overrides for sweeps
32static const double DT = std::getenv("TEST_DT") ? std::atof(std::getenv("TEST_DT")) : 1e3;
33
34static std::vector<double> sphereSdf() {
35 std::vector<double> sdf((std::size_t)N * N * N);
36 const double R = 0.22 * N, cx = 0.5 * N, cy = 0.5 * N, cz = 0.5 * N;
37 for (int z = 0; z < N; ++z)
38 for (int y = 0; y < N; ++y)
39 for (int x = 0; x < N; ++x) {
40 const double dx = x + 0.5 - cx, dy = y + 0.5 - cy, dz = z + 0.5 - cz;
41 sdf[(std::size_t)x + (std::size_t)y * N + (std::size_t)z * N * N] =
42 std::sqrt(dx * dx + dy * dy + dz * dz) - R;
43 }
44 return sdf;
45}
46
47static void configure(IbmSolver& s, bool vmg, bool slipZ) {
48 s.setRho(RHO);
49 s.setMu(MU);
50 s.setDt(DT);
51 s.setAdvection(false);
52 s.setDomainBc(0, 2, UIN, 0, 0); // -x inflow
53 s.setDomainBc(1, 3, 0, 0, 0); // +x outflow
54 for (int f = 2; f < 6; ++f)
55 s.setDomainBc(f, (slipZ && f >= 4) ? 4 : 1, 0, 0, 0); // walls (+-z free-slip in pass 2)
56 s.setPressureLevels(4);
57 s.setPressurePcg(true, 300, 1e-10);
58 // Both solvers to the same TIGHT residual (the solver default, 1e-5, would pin the solution
59 // only to ~5e-4 on this operator -- rows span 1e3 -- and the fixed-point gate needs better).
60 s.setVelocityResidualTolerance(1e-10);
61 if (vmg) {
62 s.setVelocityMultigrid(true, 4, 60); // up to 60 V-cycles
63 } else {
64 s.setVelocityIterations(20000); // RB-GS to the same tolerance
65 }
66}
67
68int main(int argc, char** argv) {
69 MPI_Init(&argc, &argv);
70 Kokkos::initialize(argc, argv);
71 int fail = 0, size = 1, rank = 0;
72 MPI_Comm_rank(MPI_COMM_WORLD, &rank);
73 MPI_Comm_size(MPI_COMM_WORLD, &size);
74 for (int pass = 0; pass < 2; ++pass) {
75 const bool slipZ = (pass == 1);
76 if (rank == 0)
77 std::printf("[velocitymg_bc] pass %d: +-z %s\n", pass,
78 slipZ ? "FREE-SLIP (type 4)" : "no-slip walls");
79 const std::vector<double> gsdf = sphereSdf();
80
81 // (a) distributed, velocity MG
82 peclet::core::decomp::BlockDecomposer<3> dec =
83 peclet::flow::CutcellMG::decomposition(static_cast<std::size_t>(size), N, N, N);
84 auto blk = dec.block(rank);
85 const int ox = (int)blk.origin[0], oy = (int)blk.origin[1], oz = (int)blk.origin[2];
86 const int lnx = (int)blk.size[0], lny = (int)blk.size[1], lnz = (int)blk.size[2];
87 std::vector<double> lsdf((std::size_t)lnx * lny * lnz);
88 for (int z = 0; z < lnz; ++z)
89 for (int y = 0; y < lny; ++y)
90 for (int x = 0; x < lnx; ++x)
91 lsdf[(std::size_t)x + (std::size_t)y * lnx + (std::size_t)z * lnx * lny] =
92 gsdf[(std::size_t)(x + ox) + (std::size_t)(y + oy) * N +
93 (std::size_t)(z + oz) * N * N];
94 IbmSolver sd(lnx, lny, lnz);
95 sd.initMpi(N, N, N, MPI_COMM_WORLD);
96 configure(sd, true, slipZ);
97 sd.setSolid(lsdf, true);
98 long cycles = 0;
99 for (int it = 0; it < STEPS; ++it) {
100 sd.step();
101 cycles += sd.lastMomentumSweeps();
102 }
103 const std::vector<double> ud = sd.getVelocity(0);
104 const double divd = sd.maxOpenDivergence();
105
106 // (b) + (c) single-rank on rank 0, broadcast
107 std::vector<double> ub((std::size_t)N * N * N), uc((std::size_t)N * N * N);
108 long cyclesRef = 0, sweepsRef = 0;
109 if (rank == 0) {
110 IbmSolver rb(N, N, N);
111 configure(rb, true, slipZ);
112 rb.setSolid(gsdf, true);
113 for (int it = 0; it < STEPS; ++it) {
114 rb.step();
115 cyclesRef += rb.lastMomentumSweeps();
116 }
117 ub = rb.getVelocity(0);
118 IbmSolver rc(N, N, N);
119 configure(rc, false, slipZ);
120 rc.setSolid(gsdf, true);
121 for (int it = 0; it < STEPS; ++it) {
122 rc.step();
123 sweepsRef += rc.lastMomentumSweeps();
124 }
125 uc = rc.getVelocity(0);
126 }
127 MPI_Bcast(ub.data(), (int)ub.size(), MPI_DOUBLE, 0, MPI_COMM_WORLD);
128 MPI_Bcast(uc.data(), (int)uc.size(), MPI_DOUBLE, 0, MPI_COMM_WORLD);
129 MPI_Bcast(&cyclesRef, 1, MPI_LONG, 0, MPI_COMM_WORLD);
130 MPI_Bcast(&sweepsRef, 1, MPI_LONG, 0, MPI_COMM_WORLD);
131
132 double dab = 0, dbc = 0, umax = 0;
133 for (int z = 0; z < lnz; ++z)
134 for (int y = 0; y < lny; ++y)
135 for (int x = 0; x < lnx; ++x) {
136 const std::size_t li = (std::size_t)x + (std::size_t)y * lnx + (std::size_t)z * lnx * lny;
137 const std::size_t gi =
138 (std::size_t)(x + ox) + (std::size_t)(y + oy) * N + (std::size_t)(z + oz) * N * N;
139 dab = std::max(dab, std::fabs(ud[li] - ub[gi]));
140 dbc = std::max(dbc, std::fabs(ub[gi] - uc[gi]));
141 umax = std::max(umax, std::fabs(uc[gi]));
142 }
143 double g[3] = {dab, dbc, umax}, gg[3];
144 MPI_Allreduce(g, gg, 3, MPI_DOUBLE, MPI_MAX, MPI_COMM_WORLD);
145 const double relAB = gg[0] / gg[2], relBC = gg[1] / gg[2];
146 const double tolAB = (size == 1) ? 1e-12 : 1e-7; // np>1: the pressure PCG's reduction floor
147 const double tolBC = 1e-5; // V-cycle vs converged RB-GS fixed point
148 if (rank == 0)
149 std::printf(
150 " dist-vs-single (vmg) rel %.2e (tol %.0e) vmg-vs-RBGS rel %.2e (tol %.0e) "
151 "div %.2e cycles/step %.1f (single %.1f) vs RB-GS sweeps/step %.1f (np=%d)\n",
152 relAB, tolAB, relBC, tolBC, divd, cycles / (double)STEPS, cyclesRef / (double)STEPS,
153 sweepsRef / (double)STEPS, size);
154 if (!(relAB <= tolAB) || !(relBC <= tolBC) ||
155 !(divd < 1e-3)) // div: sanity (open-boundary level)
156 fail = 1;
157 if (slipZ) {
158 // the slip faces are impermeable: the wall-normal component on the +-z boundary planes
159 // must be exactly 0 on every rank that owns them (w(k = 0) is the -z face; the +z face is
160 // the ghost plane the gather does not return, so the inner check covers the -z side)
161 const std::vector<double> wd = sd.getVelocity(2);
162 double wmax = 0.0;
163 if (oz == 0)
164 for (int y = 0; y < lny; ++y)
165 for (int x = 0; x < lnx; ++x)
166 wmax = std::max(wmax, std::fabs(wd[(std::size_t)x + (std::size_t)y * lnx]));
167 double wg = 0.0;
168 MPI_Allreduce(&wmax, &wg, 1, MPI_DOUBLE, MPI_MAX, MPI_COMM_WORLD);
169 if (rank == 0)
170 std::printf(" slip face -z: max|w| on the boundary plane %.2e (impermeable)\n", wg);
171 if (!(wg == 0.0))
172 fail = 1;
173 }
174 }
175 int totalFail = 0;
176 MPI_Allreduce(&fail, &totalFail, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD);
177 if (rank == 0) {
178 if (totalFail == 0)
179 std::printf(
180 "OK (np=%d): mixed (solid + domain-BC) velocity MG distributed == single-rank == RB-GS "
181 "(walls, and +-z free-slip)\n",
182 size);
183 else
184 std::fprintf(stderr, "FAILED (np=%d)\n", size);
185 }
186 Kokkos::finalize();
187 MPI_Finalize();
188 return totalFail == 0 ? 0 : 1;
189}
void setSolid(const std::vector< double > &sdfInner, bool cutcellPressure)
Host entry point: upload the inner SDF once and delegate.
long lastMomentumSweeps() const
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 constexpr double UIN
static constexpr double RHO
static constexpr double MU
static void configure(IbmSolver &s, bool vmg, bool slipZ)
static constexpr int STEPS
static constexpr int N
static std::vector< double > sphereSdf()
static const double DT