flow
Kokkos cut-cell IBM incompressible Navier-Stokes solver + pnm pore extraction
Loading...
Searching...
No Matches
test_graphamg_mpi.cpp
Go to the documentation of this file.
1// Decomposition-agnostic pressure solve via the agglomerated GraphAMG bottom solve.
2//
3// The geometric coarse hierarchy needs a cleanly-coarsening (equal-weight) ORB, so under a WEIGHTED
4// decomposition only pure RB-GS (nLevels==1) is available -- and RB-GS alone is NOT
5// mesh-independent. With set_pressure_graph_amg(true), the coarsest level (== the whole grid at
6// nLevels==1) is solved by a mesh-agnostic smoothed-aggregation AMG on the operator gathered to
7// rank 0: decomposition-agnostic AND mesh-independent. This test runs Stokes flow through a sphere
8// packing on a WEIGHTED ORB and checks the permeability equals the single-rank reference (np=1
9// bit-exact-ish; np>1 to the reduction-order floor), for BOTH the RB-GS and the GraphAMG bottom.
10// Build with -DPECLET_FLOW_MPI.
11#include <mpi.h>
12
13#include <cmath>
14#include <cstdio>
15#include <Kokkos_Core.hpp>
16#include <vector>
17
18#include "flow_ibm.hpp"
19#include "peclet/core/common/types.hpp"
20#include "peclet/core/decomp/block_decomposer.hpp"
21
22using peclet::core::IVec;
23using peclet::core::decomp::BlockDecomposer;
25
26static constexpr int N = 32, STEPS = 100;
27static constexpr double RHO = 1.0, MU = 0.1, F = 1e-3, DT = 60.0;
28
29static std::vector<double> packingSdf(double rfrac = 0.18) {
30 const double R = rfrac * N;
31 std::vector<double> sdf((std::size_t)N * N * N);
32 const double cs[2] = {0.25 * N, 0.75 * N};
33 for (int z = 0; z < N; ++z)
34 for (int y = 0; y < N; ++y)
35 for (int x = 0; x < N; ++x) {
36 double best = 1e30;
37 for (double sx : cs)
38 for (double sy : cs)
39 for (double sz : cs) {
40 auto wrap = [](double d) { return d - N * std::round(d / N); };
41 const double dx = wrap(x - sx), dy = wrap(y - sy), dz = wrap(z - sz);
42 best = std::min(best, std::sqrt(dx * dx + dy * dy + dz * dz) - R);
43 }
44 sdf[(std::size_t)x + (std::size_t)y * N + (std::size_t)z * N * N] = best;
45 }
46 return sdf;
47}
48
49static void configure(IbmSolver& s, bool amg) {
50 s.setRho(RHO);
51 s.setMu(MU);
52 s.setDt(DT);
53 s.setBodyForce(F, 0, 0);
54 s.setAdvection(false);
55 s.setVelocityIterations(80);
56 s.setPressureLevels(1); // pure RB-GS geometrically (decomposition-agnostic)
57 s.setPressureGraphAmg(amg); // ... upgraded to a mesh-agnostic algebraic coarse solve
58 s.setPressurePcg(true, 400, 1e-9);
59}
60
61static std::vector<double> localSdf(const std::vector<double>& g, 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 g[(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 perm(const std::vector<double>& gsdf, const BlockDecomposer<3>& dec, int rank,
76 bool amg, MPI_Comm comm) {
77 auto b = dec.block(rank);
78 IbmSolver s((int)b.size[0], (int)b.size[1], (int)b.size[2]);
79 s.initMpi(dec, comm);
80 configure(s, amg);
81 s.setSolid(localSdf(gsdf, dec, rank), true);
82 for (int it = 0; it < STEPS; ++it)
83 s.step();
84 double lsum = 0;
85 for (double v : s.getVelocity(0))
86 lsum += v;
87 double gsum = 0;
88 MPI_Allreduce(&lsum, &gsum, 1, MPI_DOUBLE, MPI_SUM, comm);
89 return MU * (gsum / ((double)N * N * N)) / F;
90}
91
92int main(int argc, char** argv) {
93 MPI_Init(&argc, &argv);
94 Kokkos::initialize(argc, argv);
95 int fail = 0, size = 1, rank = 0;
96 {
97 MPI_Comm_rank(MPI_COMM_WORLD, &rank);
98 MPI_Comm_size(MPI_COMM_WORLD, &size);
99 const std::vector<double> gsdf = packingSdf();
100
101 // WEIGHTED ORB (heavy low-x): the geometric multilevel path is unavailable; GraphAMG bottom
102 // makes the (nLevels==1) solve mesh-agnostic on it.
103 std::vector<peclet::core::Real> w((std::size_t)N * N * N, 1.0);
104 for (int z = 0; z < N; ++z)
105 for (int y = 0; y < N; ++y)
106 for (int x = 0; x < N; ++x)
107 if (x < N / 2)
108 w[(std::size_t)x + (std::size_t)y * N + (std::size_t)z * N * N] = 6.0;
109 BlockDecomposer<3> dec((std::size_t)size, IVec<3>{N, N, N}, w);
110
111 const double k_amg = perm(gsdf, dec, rank, /*amg=*/true, MPI_COMM_WORLD);
112
113 // single-rank reference with the same GraphAMG solve.
114 double k_ref = 0.0;
115 if (rank == 0) {
116 IbmSolver ref(N, N, N);
117 configure(ref, /*amg=*/true);
118 ref.setSolid(gsdf, true);
119 for (int it = 0; it < STEPS; ++it)
120 ref.step();
121 double rs = 0;
122 for (double v : ref.getVelocity(0))
123 rs += v;
124 k_ref = MU * (rs / ((double)N * N * N)) / F;
125 }
126 MPI_Bcast(&k_ref, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD);
127
128 const double reld = std::fabs(k_amg - k_ref) / (std::fabs(k_ref) + 1e-30);
129 const double tol = (size == 1) ? 1e-9 : 2e-4;
130 if (rank == 0)
131 std::printf(" GraphAMG on weighted ORB: k=%.8e ref=%.8e rel=%.2e (np=%d, tol %.0e)\n",
132 k_amg, k_ref, reld, size, tol);
133 if (!(reld < tol) || !std::isfinite(k_amg))
134 fail = 1;
135 }
136 int tf = 0;
137 MPI_Allreduce(&fail, &tf, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD);
138 if (rank == 0)
139 std::printf(tf == 0 ? "OK (np=%d): decomposition-agnostic GraphAMG pressure solve\n"
140 : "FAILED (np=%d)\n",
141 size);
142 Kokkos::finalize();
143 MPI_Finalize();
144 return tf == 0 ? 0 : 1;
145}
flow — host-facing Kokkos IBM Navier-Stokes solver (drop-in flow-style API).
int main(int argc, char **argv)
static void configure(IbmSolver &s, bool amg)
static constexpr double RHO
static constexpr double MU
static constexpr int STEPS
static constexpr int N
static double perm(const std::vector< double > &gsdf, const BlockDecomposer< 3 > &dec, int rank, bool amg, MPI_Comm comm)
static constexpr double DT
static constexpr double F
static std::vector< double > localSdf(const std::vector< double > &g, const BlockDecomposer< 3 > &dec, int rank)
static std::vector< double > packingSdf(double rfrac=0.18)