flow 1.3.0
Kokkos cut-cell IBM incompressible Navier-Stokes solver + pnm pore extraction
Loading...
Searching...
No Matches
test_distributed_diffusion.cpp
Go to the documentation of this file.
1// cfd-gpu — distributed (MPI) Kokkos grid-halo integration: the foundational pattern for the
2// multi-rank Kokkos sdflow.
3//
4// Solves a periodic implicit-diffusion system ((idiag)I - beta*Lap) u = b by Red-Black
5// Gauss-Seidel, with the field decomposed over MPI ranks (ORB) and the per-colour ghost exchange
6// done by transport-core's portable GPU-resident halo (GridHalo) instead of a single-process
7// periodic fill. The cfd CCField and peclet::core::View<double> are the SAME Kokkos type
8// (x-fastest, default memory space), so the solver field exchanges directly. The distributed RB-GS
9// is algebraically identical to the single-rank sweep (the halo supplies the same neighbour values
10// + the GLOBAL red-black parity via the block's global origin), so the multi-rank result must equal
11// a serial reference to machine precision. Validates the consumption pattern (replace fillGhosts
12// with halo.exchange) the full sdflow MPI port will follow. Runs on any backend.
13#include <mpi.h>
14
15#include <array>
16#include <cmath>
17#include <cstdio>
18#include <Kokkos_Core.hpp>
19#include <vector>
20
21#include "mac_stencils.hpp" // peclet::flow::diffSmoothColor, I3, SField
22#include "peclet/core/common/types.hpp"
23#include "peclet/core/common/view.hpp"
24#include "peclet/core/decomp/block_decomposer.hpp"
25#include "peclet/core/halo/grid_halo.hpp"
26#include "peclet/core/halo/grid_halo_topology.hpp"
27
28using peclet::core::Index;
29using peclet::core::IVec;
30using peclet::core::wrap;
31using peclet::core::decomp::BlockDecomposer;
32using peclet::core::halo::GridHalo;
33using peclet::core::halo::GridHaloTopology;
37
38static constexpr int kDim = 3;
39static constexpr int G = 1; // diffusion stencil reach
40static constexpr double IDIAG = 1.0, BETA = 0.2, AC = IDIAG + 6.0 * BETA;
41static constexpr int SWEEPS = 60;
42
43// deterministic periodic source over the global grid.
44static double source(int gx, int gy, int gz, IVec<kDim> gs) {
45 return std::sin(2.0 * M_PI * gx / gs[0]) * std::cos(2.0 * M_PI * gy / gs[1]) +
46 0.5 * std::sin(2.0 * M_PI * gz / gs[2]);
47}
48
49// periodic ghost fill (3 axes, width G) of a single-block extended field on device.
50static void periodicFill(SField f, I3 e) {
52 long st[3] = {1, e.x, (long)e.x * e.y};
53 int dims[3] = {e.x, e.y, e.z};
54 int N3[3] = {e.x - 2 * G, e.y - 2 * G, e.z - 2 * G};
55 for (int a = 0; a < 3; ++a) {
56 const int b = (a + 1) % 3, c = (a + 2) % 3;
57 const long sa = st[a], sb = st[b], sc = st[c];
58 const int N = N3[a];
59 Kokkos::parallel_for(
60 "ref_pfill", peclet::flow::MDRange2<peclet::flow::SExec>(sp, {0, 0}, {dims[b], dims[c]}),
61 KOKKOS_LAMBDA(int p0, int p1) {
62 const long base = (long)p0 * sb + (long)p1 * sc;
63 for (int gl = 0; gl < G; ++gl) {
64 f(base + (long)gl * sa) = f(base + (long)(gl + N) * sa);
65 f(base + (long)(G + N + gl) * sa) = f(base + (long)(G + gl) * sa);
66 }
67 });
68 sp.fence();
69 }
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 {
77 MPI_Comm_rank(MPI_COMM_WORLD, &rank);
78 MPI_Comm_size(MPI_COMM_WORLD, &size);
79 IVec<kDim> gs{24, 20, 16};
80 std::array<bool, kDim> periodic{true, true, true};
81
82 // ---- distributed solve ----
83 BlockDecomposer<kDim> dec(static_cast<std::size_t>(size), gs);
84 GridHaloTopology<kDim> halo;
85 halo.buildTopology(dec, rank, G, periodic, MPI_COMM_WORLD);
86 const auto& idx = halo.indexer();
87 const Index n = idx.numCellsInclGhost();
88 const auto eg = idx.sizeInclGhost(), og = idx.originInclGhost();
89 const I3 e{(int)eg[0], (int)eg[1], (int)eg[2]}, ogv{(int)og[0], (int)og[1], (int)og[2]};
90
91 SField u("u", n), b("b", n);
92 auto hb = Kokkos::create_mirror_view(b);
93 auto hu = Kokkos::create_mirror_view(u);
94 for (Index c = 0; c < n; ++c) {
95 hb(c) = 0.0;
96 hu(c) = 0.0;
97 }
98 idx.forEachInner([&](const IVec<kDim>& l) {
99 hb(idx.localMdToLocal(l)) = source(l[0] + og[0], l[1] + og[1], l[2] + og[2], gs);
100 });
101 Kokkos::deep_copy(b, hb);
102 Kokkos::deep_copy(u, hu);
103
104 GridHalo<double> dev;
105 dev.init(halo);
106 SField empty;
107 for (int k = 0; k < SWEEPS; ++k)
108 for (int color = 0; color < 2; ++color) {
109 dev.exchange(
110 u); // cross-rank + periodic ghost exchange (replaces the single-process fillGhosts)
111 diffSmoothColor(u, b, e, ogv, G, BETA, AC, color, empty);
112 }
113 Kokkos::deep_copy(hu, u);
114
115 // ---- serial reference on the full global grid (every rank, redundant; compare its own block)
116 // ----
117 const I3 ge{(int)gs[0] + 2 * G, (int)gs[1] + 2 * G, (int)gs[2] + 2 * G}, geo{-G, -G, -G};
118 const Index gn = (Index)ge.x * ge.y * ge.z;
119 SField ur("ur", gn), br("br", gn);
120 auto hbr = Kokkos::create_mirror_view(br);
121 for (Index c = 0; c < gn; ++c)
122 hbr(c) = 0.0;
123 for (int z = 0; z < gs[2]; ++z)
124 for (int y = 0; y < gs[1]; ++y)
125 for (int x = 0; x < gs[0]; ++x)
126 hbr((long)(x + G) + (long)(y + G) * ge.x + (long)(z + G) * (long)ge.x * ge.y) =
127 source(x, y, z, gs);
128 Kokkos::deep_copy(br, hbr);
129 Kokkos::deep_copy(ur, 0.0);
130 for (int k = 0; k < SWEEPS; ++k)
131 for (int color = 0; color < 2; ++color) {
132 periodicFill(ur, ge);
133 diffSmoothColor(ur, br, ge, geo, G, BETA, AC, color, empty);
134 }
135 auto hur = Kokkos::create_mirror_view(ur);
136 Kokkos::deep_copy(hur, ur);
137
138 // compare this rank's inner block against the global reference.
139 double maxdiff = 0.0;
140 idx.forEachInner([&](const IVec<kDim>& l) {
141 const int gx = l[0] + og[0], gy = l[1] + og[1], gz = l[2] + og[2];
142 const double a = hu(idx.localMdToLocal(l));
143 const double r =
144 hur((long)(gx + G) + (long)(gy + G) * ge.x + (long)(gz + G) * (long)ge.x * ge.y);
145 const double d = std::fabs(a - r);
146 if (d > maxdiff)
147 maxdiff = d;
148 });
149 if (maxdiff > 1e-12) {
150 ++fail;
151 std::fprintf(stderr, "[rank %d] max|distributed - serial| = %.3e\n", rank, maxdiff);
152 }
153 }
154
155 int totalFail = 0;
156 MPI_Allreduce(&fail, &totalFail, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD);
157 if (rank == 0) {
158 if (totalFail == 0)
159 std::printf("OK (np=%d): distributed RB-GS diffusion == serial reference\n", size);
160 else
161 std::fprintf(stderr, "FAILED (np=%d): %d ranks differ\n", size, totalFail);
162 }
163 Kokkos::finalize();
164 MPI_Finalize();
165 return totalFail == 0 ? 0 : 1;
166}
int main()
Definition dbg_scene.cpp:13
flow — portable (Kokkos) MAC stencil operators: Red-Black Gauss-Seidel smoothers + divergence.
Kokkos::DefaultExecutionSpace SExec
void diffSmoothColor(SField c, SConst b, I3 e, I3 og, int g, double bx, double by, double bz, double Ac, int color, SConst dcorr, bool aniso)
Kokkos::View< double *, SMem > SField
static constexpr int N
static constexpr int kDim
static constexpr double IDIAG
static constexpr int SWEEPS
static constexpr double AC
static constexpr int G
static double source(int gx, int gy, int gz, IVec< kDim > gs)
static constexpr double BETA
static void periodicFill(SField f, I3 e)