flow 1.3.0
Kokkos cut-cell IBM incompressible Navier-Stokes solver + pnm pore extraction
Loading...
Searching...
No Matches
test_redistribute_mpi.cpp
Go to the documentation of this file.
1// Dynamic load balancing: Solver::redistribute moves the distributed solver's state to a NEW ORB
2// partition mid-simulation. Stokes flow through a sphere packing is run distributed on the default
3// (equal-cell) decomposition for half the steps, then REDISTRIBUTED to a weighted decomposition
4// (block boundaries move) and run to completion. The permeability must still match the single-rank
5// reference to the same tolerance as a never-redistributed distributed run — proving the migration
6// (bit-exact field movement) + the rebuild (halo / openness / IBM / MG from the migrated SDF) are
7// correct. Build with -DPECLET_FLOW_MPI.
8#include <mpi.h>
9
10#include <cmath>
11#include <cstdio>
12#include <Kokkos_Core.hpp>
13#include <vector>
14
15#include "flow_ibm.hpp"
16#include "peclet/core/common/types.hpp"
17#include "peclet/core/decomp/block_decomposer.hpp"
18
19using peclet::core::IVec;
20using peclet::core::decomp::BlockDecomposer;
22
23static constexpr int N = 32, STEPS = 120;
24static constexpr double RHO = 1.0, MU = 0.1, F = 1e-3, DT = 60.0;
25
26static std::vector<double> packingSdf(double rfrac = 0.18) {
27 const double R = rfrac * N;
28 std::vector<double> sdf((std::size_t)N * N * N);
29 const double cs[2] = {0.25 * N, 0.75 * N};
30 for (int z = 0; z < N; ++z)
31 for (int y = 0; y < N; ++y)
32 for (int x = 0; x < N; ++x) {
33 double best = 1e30;
34 for (double sx : cs)
35 for (double sy : cs)
36 for (double sz : cs) {
37 auto wrap = [](double d) { return d - N * std::round(d / N); };
38 const double dx = wrap(x - sx), dy = wrap(y - sy), dz = wrap(z - sz);
39 best = std::min(best, std::sqrt(dx * dx + dy * dy + dz * dz) - R);
40 }
41 sdf[(std::size_t)x + (std::size_t)y * N + (std::size_t)z * N * N] = best;
42 }
43 return sdf;
44}
45
46static void configure(IbmSolver& s) {
47 s.setRho(RHO);
48 s.setMu(MU);
49 s.setDt(DT);
50 s.setBodyForce(F, 0, 0);
51 s.setAdvection(false);
53 // Pure RB-GS pressure (levels=1): decomposition-agnostic, so redistribute onto a WEIGHTED
54 // partition works (the geometric coarse levels assume clean-coarsening; weighted-ORB co-decomp +
55 // multilevel MG is the GraphAMG follow-up).
57 s.setPressurePcg(true, 600, 1e-10);
58}
59
60// local SDF block for a decomposition's block on this rank.
61static std::vector<double> localSdf(const std::vector<double>& gsdf, const BlockDecomposer<3>& dec,
62 int rank) {
63 auto b = dec.block(rank);
64 const int ox = (int)b.origin[0], oy = (int)b.origin[1], oz = (int)b.origin[2];
65 const int lnx = (int)b.size[0], lny = (int)b.size[1], lnz = (int)b.size[2];
66 std::vector<double> l((std::size_t)lnx * lny * lnz);
67 for (int z = 0; z < lnz; ++z)
68 for (int y = 0; y < lny; ++y)
69 for (int x = 0; x < lnx; ++x)
70 l[(std::size_t)x + (std::size_t)y * lnx + (std::size_t)z * lnx * lny] =
71 gsdf[(std::size_t)(x + ox) + (std::size_t)(y + oy) * N + (std::size_t)(z + oz) * N * N];
72 return l;
73}
74
75static double localUSum(IbmSolver& s) {
76 auto u = s.getVelocity(0);
77 double sum = 0;
78 for (double v : u)
79 sum += v;
80 return sum;
81}
82
83int main(int argc, char** argv) {
84 MPI_Init(&argc, &argv);
85 Kokkos::initialize(argc, argv);
86 int fail = 0, size = 1, rank = 0;
87 {
90 const std::vector<double> gsdf = packingSdf();
91 const double gcells = (double)N * N * N;
92
93 // D1 = default equal-cell ORB; D2 = weighted ORB (heavy low-x half -> boundaries move).
94 BlockDecomposer<3> D1((std::size_t)size, IVec<3>{N, N, N});
95 std::vector<peclet::core::Real> w((std::size_t)N * N * N, 1.0);
96 for (int z = 0; z < N; ++z)
97 for (int y = 0; y < N; ++y)
98 for (int x = 0; x < N; ++x)
99 if (x < N / 2)
100 w[(std::size_t)x + (std::size_t)y * N + (std::size_t)z * N * N] = 6.0;
101 BlockDecomposer<3> D2((std::size_t)size, IVec<3>{N, N, N}, w);
102
103 // distributed on D1, half the steps, REDISTRIBUTE to D2, finish.
104 auto b1 = D1.block(rank);
105 IbmSolver sd((int)b1.size[0], (int)b1.size[1], (int)b1.size[2]);
106 sd.initMpi(D1, MPI_COMM_WORLD);
107 configure(sd);
108 sd.setSolid(localSdf(gsdf, D1, rank), true);
109 for (int it = 0; it < STEPS / 2; ++it)
110 sd.step();
111 double uPre = 0, uPost = 0;
112 {
113 double l = localUSum(sd);
115 }
116 const double div_pre = sd.maxOpenDivergence();
117 if (rank == 0)
118 std::printf(" div PRE-redistribute = %.2e\n", div_pre);
119 sd.redistribute(D2);
120 {
121 double l = localUSum(sd);
123 }
124 if (rank == 0)
125 std::printf(" global-u sum pre=%.12e post=%.12e |d|=%.2e (redistribute data movement)\n",
126 uPre, uPost, std::fabs(uPre - uPost));
127 for (int it = STEPS / 2; it < STEPS; ++it)
128 sd.step();
129 double lsum = localUSum(sd), gsum = 0;
131 const double k_dist = MU * (gsum / gcells) / F;
132 const double div_dist = sd.maxOpenDivergence();
133
134 // single-rank reference (validated path) on rank 0.
135 double k_ref = 0.0;
136 if (rank == 0) {
137 IbmSolver ref(N, N, N);
138 configure(ref);
139 ref.setSolid(gsdf, true);
140 for (int it = 0; it < STEPS; ++it)
141 ref.step();
142 double rsum = 0;
143 for (double v : ref.getVelocity(0))
144 rsum += v;
145 k_ref = MU * (rsum / gcells) / F;
146 }
148
149 const double reld = std::fabs(k_dist - k_ref) / (std::fabs(k_ref) + 1e-30);
150 const double tol = (size == 1) ? 1e-12 : 2e-5; // np=1 bit-exact; np>1 MG-PCG reduction floor
151 if (rank == 0)
152 std::printf(
153 " k_dist=%.8e k_ref=%.8e rel=%.2e div=%.2e (np=%d, tol %.0e, redistributed)\n",
154 k_dist, k_ref, reld, div_dist, size, tol);
155 if (reld > tol || !(div_dist < 1e-5))
156 fail = 1;
157 }
158 int totalFail = 0;
160 if (rank == 0)
161 std::printf(totalFail == 0 ? "OK (np=%d): mid-run redistribute preserves the solution\n"
162 : "FAILED (np=%d)\n",
163 size);
164 Kokkos::finalize();
165 MPI_Finalize();
166 return totalFail == 0 ? 0 : 1;
167}
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 std::vector< double > localSdf(const std::vector< double > &gsdf, const BlockDecomposer< 3 > &dec, int rank)
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)