flow 1.3.0
Kokkos cut-cell IBM incompressible Navier-Stokes solver + pnm pore extraction
Loading...
Searching...
No Matches
test_sdflow_mpi.cpp
Go to the documentation of this file.
1// cfd-gpu — the ASSEMBLED multi-rank IbmSolver step (the capstone of the cfd MPI port).
2//
3// Solves creeping (Stokes) flow through a periodic 2x2x2 sphere packing with the REAL IbmSolver
4// solver, two ways: single-rank (the validated single-GPU path) on the full grid, and distributed
5// -- each rank constructs IbmSolver with its ORB block dims, calls initMpi (g=2 velocity-block halo
6// + global-origin red-black parity
7// + the distributed pressure MG via CutcellMG::initMpi), and setSolid with its LOCAL SDF block
8// (ghosts halo- exchanged). The superficial velocity <u> (hence the permeability k = mu*<u>/F) is
9// reduced over ranks. The distributed solve must equal single-rank: np=1 bit-exact, np>1 to the
10// MG-PCG reduction-order floor (the inner-product Allreduce reorders the Krylov path -- same as
11// test_cutcellmg_mpi). Build with -DCFD_MPI.
12#include <mpi.h>
13
14#include <cmath>
15#include <cstdio>
16#include <Kokkos_Core.hpp>
17#include <vector>
18
19#include "flow_ibm.hpp"
20#include "peclet/core/common/types.hpp"
21#include "peclet/core/decomp/block_decomposer.hpp"
22
23using peclet::core::IVec;
25
26static constexpr int N = 32, STEPS = 120;
27static constexpr double RHO = 1.0, MU = 0.1, F = 1e-3, DT = 60.0;
28
29// global sphere-packing SDF (flat x-fastest, negative inside), 2x2x2 spheres, periodic
30// min-distance.
31static std::vector<double> packingSdf(double rfrac = 0.18) {
32 const double R = rfrac * N;
33 std::vector<double> sdf((std::size_t)N * N * N);
34 const double cs[2] = {0.25 * N, 0.75 * N};
35 for (int z = 0; z < N; ++z)
36 for (int y = 0; y < N; ++y)
37 for (int x = 0; x < N; ++x) {
38 double best = 1e30;
39 for (double sx : cs)
40 for (double sy : cs)
41 for (double sz : cs) {
42 auto wrap = [](double d) { return d - N * std::round(d / N); };
43 const double dx = wrap(x - sx), dy = wrap(y - sy), dz = wrap(z - sz);
44 best = std::min(best, std::sqrt(dx * dx + dy * dy + dz * dz) - R);
45 }
46 sdf[(std::size_t)x + (std::size_t)y * N + (std::size_t)z * N * N] = best;
47 }
48 return sdf;
49}
50
51static void configure(IbmSolver& s) {
52 s.setRho(RHO);
53 s.setMu(MU);
54 s.setDt(DT);
55 s.setBodyForce(F, 0, 0);
56 s.setAdvection(false);
59 s.setPressurePcg(true, 200, 1e-9);
60}
61
62// local sum of u over this solver's inner block (getVelocity(0) returns the inner block,
63// x-fastest).
64static double localUSum(IbmSolver& s) {
65 auto u = s.getVelocity(0);
66 double sum = 0;
67 for (double v : u)
68 sum += v;
69 return sum;
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 {
79 const std::vector<double> gsdf = packingSdf();
80 const double gcells = (double)N * N * N;
81
82 // --- distributed solve ---
83 peclet::core::decomp::BlockDecomposer<3> dec(static_cast<std::size_t>(size), IVec<3>{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
96 sd.initMpi(N, N, N, MPI_COMM_WORLD);
98 sd.setSolid(lsdf, /*cutcell_pressure=*/true);
99 for (int it = 0; it < STEPS; ++it)
100 sd.step();
101 double lsum = localUSum(sd), gsum = 0;
103 const double k_dist = MU * (gsum / gcells) / F;
104 const double div_dist = sd.maxOpenDivergence();
105
106 // --- single-rank reference (full grid, the validated single-GPU path) on rank 0 ---
107 double k_ref = 0.0;
108 if (rank == 0) {
109 IbmSolver ref(N, N, N);
110 configure(ref);
111 ref.setSolid(gsdf, true);
112 for (int it = 0; it < STEPS; ++it)
113 ref.step();
114 double rsum = 0;
115 {
116 auto u = ref.getVelocity(0);
117 for (double v : u)
118 rsum += v;
119 }
120 k_ref = MU * (rsum / gcells) / F;
121 }
123
124 const double reld = std::fabs(k_dist - k_ref) / (std::fabs(k_ref) + 1e-30);
125 const double tol =
126 (size == 1) ? 1e-12 : 2e-5; // np=1 bit-exact; np>1 the MG-PCG reduction-order floor
127 if (rank == 0)
128 std::printf(" k_dist=%.8e k_ref=%.8e rel=%.2e div=%.2e (np=%d, tol %.0e)\n", k_dist,
129 k_ref, reld, div_dist, size, tol);
130 if (reld > tol || !(div_dist < 1e-5))
131 fail = 1;
132 }
133 int totalFail = 0;
135 if (rank == 0) {
136 if (totalFail == 0)
137 std::printf("OK (np=%d): distributed IbmSolver Stokes permeability == single-rank\n", size);
138 else
139 std::fprintf(stderr, "FAILED (np=%d)\n", size);
140 }
141 Kokkos::finalize();
142 MPI_Finalize();
143 return totalFail == 0 ? 0 : 1;
144}
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 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 double RHO
static constexpr double MU
static double localUSum(IbmSolver &s)
static constexpr int STEPS
static constexpr int N
static constexpr double DT
static constexpr double F
static void configure(IbmSolver &s)
static std::vector< double > packingSdf(double rfrac=0.18)