24#ifndef PECLET_AMR_DISTRIBUTED_VIEW_HPP
25#define PECLET_AMR_DISTRIBUTED_VIEW_HPP
34#include "peclet/core/common/mpi.hpp"
35#include "peclet/core/common/view.hpp"
36#include "peclet/core/halo/grid_halo.hpp"
43template <
int Dim,
unsigned Bits>
48 void init(
const DO& d,
const typename DO::GatherHaloTopology&
t) {
51 nLocal_ =
static_cast<Index>(
t.localSlot.size());
52 nSend_ =
static_cast<Index>(
t.sendLeaf.size());
53 nRecv_ =
static_cast<Index>(
t.recvSlot.size());
54 d_localSlot_ =
toDevice(
t.localSlot,
"dgh::localSlot");
55 d_localLeaf_ =
toDevice(
t.localLeaf,
"dgh::localLeaf");
56 d_sendLeaf_ =
toDevice(
t.sendLeaf,
"dgh::sendLeaf");
57 d_recvSlot_ =
toDevice(
t.recvSlot,
"dgh::recvSlot");
58 sendRanks_ =
t.sendRanks;
59 sendCounts_ =
t.sendCounts;
60 recvRanks_ =
t.recvRanks;
61 recvCounts_ =
t.recvCounts;
62 sendOff_.assign(sendCounts_.size() + 1, 0);
63 for (std::size_t k = 0; k < sendCounts_.size(); ++k)
64 sendOff_[k + 1] = sendOff_[k] + sendCounts_[k];
65 recvOff_.assign(recvCounts_.size() + 1, 0);
66 for (std::size_t k = 0; k < recvCounts_.size(); ++k)
67 recvOff_[k + 1] = recvOff_[k] + recvCounts_[k];
68 d_sendBuf_ =
View<double>(Kokkos::view_alloc(
"dgh::sendBuf", Kokkos::WithoutInitializing),
69 static_cast<std::size_t
>(nSend_));
70 d_recvBuf_ =
View<double>(Kokkos::view_alloc(
"dgh::recvBuf", Kokkos::WithoutInitializing),
71 static_cast<std::size_t
>(nRecv_));
72 h_sendBuf_ = Kokkos::create_mirror_view(d_sendBuf_);
73 h_recvBuf_ = Kokkos::create_mirror_view(d_recvBuf_);
88 const bool aware = peclet::core::halo::detail::gpuAwareMpi();
94 "dgh::local", Kokkos::RangePolicy<ExecSpace>(0, nLocal_),
100 Kokkos::parallel_for(
101 "dgh::pack", Kokkos::RangePolicy<ExecSpace>(0, nSend_),
104 Kokkos::deep_copy(h_sendBuf_, d_sendBuf_);
107 double*
sendBase =
aware ? d_sendBuf_.data() : h_sendBuf_.data();
108 double*
recvBase =
aware ? d_recvBuf_.data() : h_recvBuf_.data();
109 std::vector<MPI_Request>
reqs;
110 reqs.reserve(recvRanks_.size() + sendRanks_.size());
111 for (std::size_t k = 0; k < recvRanks_.size(); ++k) {
114 recvRanks_[k],
tag, comm_, &
reqs.back());
116 for (std::size_t k = 0; k < sendRanks_.size(); ++k) {
119 sendRanks_[k],
tag, comm_, &
reqs.back());
125 Kokkos::deep_copy(d_recvBuf_, h_recvBuf_);
128 Kokkos::parallel_for(
129 "dgh::scatter", Kokkos::RangePolicy<ExecSpace>(0, nRecv_),
137 Index nFaces_ = 0, nLocal_ = 0, nSend_ = 0, nRecv_ = 0;
138 IndexView d_localSlot_, d_localLeaf_, d_sendLeaf_, d_recvSlot_;
139 std::vector<int> sendRanks_, sendCounts_, sendOff_, recvRanks_, recvCounts_, recvOff_;
147template <
int Dim,
unsigned Bits = (Dim == 2 ? 32u : (Dim == 3 ? 21u : 16u))>
157 g_ =
View<double>(Kokkos::view_alloc(
"dpd::g", Kokkos::WithoutInitializing),
158 static_cast<std::size_t
>(n_) * (2 * Dim));
159 scratch_ =
View<double>(Kokkos::view_alloc(
"dpd::scratch", Kokkos::WithoutInitializing),
160 static_cast<std::size_t
>(n_));
169 const int F = 2 * Dim;
172 Kokkos::Array<double, Dim>
inv;
173 for (
int d = 0; d < Dim; ++d)
174 inv[d] = 1.0 / (h0_[d] * h0_[d]);
176 Kokkos::parallel_for(
179 for (
int f = 0;
f <
F; ++
f)
187 const int F = 2 * Dim;
190 for (
int d = 0; d < Dim; ++d)
191 diag += 2.0 / (h0_[d] * h0_[d]);
195 Kokkos::parallel_for(
196 "dpd::jacobi_update", Kokkos::RangePolicy<ExecSpace>(0, n_),
206 Kokkos::parallel_reduce(
207 "dpd::residual", Kokkos::RangePolicy<ExecSpace>(0, n_),
209 double r = b(
i) -
ax(
i);
223 Kokkos::parallel_reduce(
224 "dpd::resnorm", Kokkos::RangePolicy<ExecSpace>(0, n_),
226 double r = b(
i) -
ax(
i);
237 Vec<Dim> h0_ = detail::filledVec<Dim>(1.0);
247template <
int Dim,
unsigned Bits = (Dim == 2 ? 32u : (Dim == 3 ? 21u : 16u))>
253 const std::array<bool, Dim>& periodic,
MPI_Comm comm) {
261 auto lvl = std::make_unique<Level>();
264 lvl->d.init(g, 0,
lg, periodic, comm);
266 lvl->n =
lvl->d.local().numLeaves();
270 levels_.push_back(std::move(
lvl));
274 for (
int d = 0; d < Dim; ++d) {
275 if (g[d] % 2 != 0 || g[d] / 2 < 2)
283 for (
int d = 0; d < Dim; ++d)
289 for (std::size_t
L = 0;
L + 1 < levels_.size(); ++
L) {
290 auto&
fine = levels_[
L]->d;
291 auto&
coarse = levels_[
L + 1]->d;
294 std::vector<Index> c2p(
static_cast<std::size_t
>(
nf), -1);
295 std::vector<Index>
cnt(
static_cast<std::size_t
>(
nc), 0);
298 for (
int d = 0; d < Dim; ++d)
301 c2p[
static_cast<std::size_t
>(
i)] =
p;
303 ++
cnt[
static_cast<std::size_t
>(
p)];
305 std::vector<Index> start(
static_cast<std::size_t
>(
nc) + 1, 0);
307 start[
static_cast<std::size_t
>(
p) + 1] =
308 start[
static_cast<std::size_t
>(
p)] +
cnt[
static_cast<std::size_t
>(
p)];
309 std::vector<Index> idx(
static_cast<std::size_t
>(start[
static_cast<std::size_t
>(
nc)]));
310 std::vector<Index>
cur(start.begin(), start.end() - 1);
312 Index p = c2p[
static_cast<std::size_t
>(
i)];
314 idx[
static_cast<std::size_t
>(
cur[
static_cast<std::size_t
>(
p)]++)] =
i;
316 levels_[
L]->c2p =
toDevice(c2p,
"dmg::c2p");
317 levels_[
L]->childStart =
toDevice(start,
"dmg::cstart");
318 levels_[
L]->childIdx =
toDevice(idx,
"dmg::cidx");
322 std::size_t
numLevels()
const {
return levels_.size(); }
333 Level&
lv = *levels_[
L];
334 if (
L + 1 == levels_.size()) {
335 lv.op.jacobi(
x,
b, bottom);
340 Level&
cl = *levels_[
L + 1];
343 Kokkos::deep_copy(
cl.x, 0.0);
357 std::vector<std::unique_ptr<Level>> levels_;
Value-only, device-resident face-neighbour gather over a fixed topology (C2).
void init(const DO &d, const typename DO::GatherHaloTopology &t)
void gather(View< const double > x, View< double > g, int tag=41) const
g (size nFaces) := the face-neighbour values of x.
Device-resident distributed geometric multigrid for the plain Laplacian (the device analogue of the h...
DistributedOctree< Dim, Bits > Octree
void build(const IVec< Dim > &g0, const AmrGeometry< Dim > &geo, const std::array< bool, Dim > &periodic, MPI_Comm comm)
void vcycle(View< double > x, View< const double > b, int pre=2, int post=2, int bottom=30, std::size_t L=0)
One V-cycle of A x = b on level L (correction scheme); entirely device-resident bar the compact gathe...
View< double > b(std::size_t L=0)
std::size_t numLevels() const
Octree & octree(std::size_t L=0)
Index numLeaves(std::size_t L=0) const
View< double > x(std::size_t L=0)
DistributedPoissonView< Dim, Bits > & op(std::size_t L=0)
FaceGatherPlan buildFaceGatherPlan() const
GatherHaloTopology buildGatherHaloTopology(const FaceGatherPlan &plan) const
Build the value-only gather topology from a FaceGatherPlan: classify each remote coord by owner (owne...
Device-resident distributed plain-Laplacian Poisson operator: y = ∇²x with cross-block neighbours fro...
void init(DistributedOctree< Dim, Bits > &d, const Vec< Dim > &h0)
void init(DistributedOctree< Dim, Bits > &d, double h0)
double residual(View< const double > x, View< const double > b, View< double > res) const
res = b − L x (device); returns the global L2 norm (the only host scalar).
double residualNorm(View< const double > x, View< const double > b) const
void apply(View< const double > x, View< double > y) const
y = L x (L = ∇², periodic).
void jacobi(View< double > x, View< const double > b, int sweeps, double omega=0.8) const
sweeps damped-Jacobi relaxations of L u = b (in place). Reads only the previous iterate.
void restrictField(View< const Index > childStart, View< const Index > childIdx, View< const double > fine, View< double > coarse, Index nCoarse)
Restrict: coarse(p) = mean over p's children (CSR fixed order ⇒ deterministic).
void prolongAdd(View< const Index > c2p, View< const double > coarse, View< double > fine, Index nFine)
Prolong (piecewise-constant) + correct: fine(i) += coarse(c2p(i)).
std::vector< std::array< double, Dim > > transferFieldGradients(const BlockOctree< Dim, Bits > &oldT, const std::vector< double > &oldF)
Per-old-leaf minmod prolongation gradients (per fine-coordinate unit) — transferField's stencil,...
World-space placement of a block-local octree: fine coordinate (0,..,0) sits at origin,...