peclet-amr 0.2.0
Block-local-Morton AMR octree + collocated cut-cell Navier–Stokes solver (header-only C++20, Kokkos + MPI)
Loading...
Searching...
No Matches
mg_stage.hpp
Go to the documentation of this file.
1// amr — a multigrid STAGE: move a level, at its own resolution, onto a new decomposition of that
2// level's grid, and continue the ladder there. docs/amr_mg_depth.md §5.6 / §6.5, and
3// suite/docs/archive/MG_TELESCOPING_PLAN.md §4.0, whose primitive this is.
4//
5// The in-place ladder (§6.2) lifts the root while every rank's ORB block stays even in origin and
6// size. It stops somewhere — grid-limited on a badly factored brick mesh, decomposition-limited as
7// soon as a block turns odd, which under a WEIGHTED (brick-granular, DEM-shared) partition is
8// typically the root brick level itself. Below that point the hierarchy does not stop: the level
9// MOVES onto a coarser decomposition of its own grid and the ladder continues there. §5.6's three
10// instantiations, in the order the trigger tries them:
11//
12// * sibling merge — target `BlockDecomposer::agglomerated(d)`, group gather; proportional trees;
13// * repartition — target a fresh proportional ORB on np_L <= np ranks, moved point-to-point
14// over the box intersections; weighted trees, where no d > 0 is liftable;
15// * REPLICATED — target one block on every rank, moved by `Allgatherv`; the bottom on the
16// last sub-communicator, and the fallback. THIS FILE implements this one.
17//
18// All three take their target, communicators and movement from core (`peclet::core::decomp`:
19// `chooseStageTarget`, `makeStageComm`, `RedistributeTopology`; docs/amr_mg_core_boundary.md). The
20// other two are WO4b's `DistributedStage` (distributed_flow_mg.hpp, since its continued ladder is
21// a `DistributedFlowMultigrid`). They are why this is an interface rather than a function: what
22// differs between them is only the target decomposition and the two movement steps (`moveUp` /
23// `moveDown`); the continued ladder, the per-level communicator and the way `vcycle` calls the
24// stage are shared. `DistributedFlowMultigrid::vcycle` never sees a message — it calls `apply` at
25// the stage point.
26#ifndef PECLET_AMR_MG_STAGE_HPP
27#define PECLET_AMR_MG_STAGE_HPP
28
29#include <algorithm>
30#include <array>
31#include <memory>
32#include <stdexcept>
33#include <string>
34#include <vector>
35
37#include "peclet/amr/common.hpp"
41#include "peclet/core/common/mpi.hpp"
42#include "peclet/core/common/view.hpp"
43#include "peclet/core/decomp/block_decomposer.hpp"
44#include "peclet/core/decomp/redistribute_topology.hpp"
45#include "peclet/core/decomp/stage_comm.hpp"
46#include "peclet/core/decomp/stage_target.hpp"
47
48namespace peclet::amr {
49
59template <int Dim, unsigned Bits = (Dim == 2 ? 32u : (Dim == 3 ? 21u : 16u))>
60class MgStage {
61 public:
63 using Decomposition = core::decomp::BlockDecomposer<Dim>;
64
65 virtual ~MgStage() = default;
66
67 // ---- what the stage is ---------------------------------------------------------------------
69 virtual const char* kind() const = 0;
72 virtual const char* diagnosticSuffix() const = 0;
74 virtual const Decomposition& targetDecomposition() const = 0;
76 virtual MPI_Comm targetComm() const = 0;
79 virtual bool active() const = 0;
81 virtual const Octree& targetOctree() const = 0;
82
83 // ---- the continued ladder (its level 0 IS the moved level) ---------------------------------
85 virtual std::size_t numLevels() const = 0;
88 virtual Index numLeaves(std::size_t L) const = 0;
91 virtual std::string bottomName() const = 0;
94 virtual void setRemoveMean(bool on) = 0;
97 virtual void setBottom(typename Multigrid<Dim, Bits>::Bottom b) = 0;
100 virtual View<double> targetX() = 0;
101
102 // ---- the movement: the ONLY part the three instantiations differ in ------------------------
107 virtual void cycle(int pre, int post, int bottom, double omega) = 0;
109 virtual void moveDown(View<double> dst, Index nDst) = 0;
110
118 int bottom, double omega) {
119 moveUp(residual, n);
120 if (active())
121 cycle(pre, post, bottom, omega);
123 }
124};
125
137template <int Dim, unsigned Bits = (Dim == 2 ? 32u : (Dim == 3 ? 21u : 16u))>
138class ReplicatedTailStage final : public MgStage<Dim, Bits> {
139 public:
141 using Octree = typename Base::Octree;
145
162 void build(const DO& d, const Vec<Dim>& h0, const Poisson& ap, Index n, Index nExt,
163 Index bottomExtent, MPI_Comm comm) {
164 namespace cd = core::decomp;
165 G_ = d.globalRootSize();
166 nt_ = 1;
167 for (int a = 0; a < Dim; ++a)
168 nt_ *= G_[a];
169 for (int a = 0; a < Dim; ++a)
170 if (!d.periodic()[a])
171 throw std::runtime_error(
172 "amr::ReplicatedTailStage: periodic-only (as the whole distributed pressure path is); "
173 "a non-periodic bottom must be gated the way flow gates its anchored path");
174 Index blockCells = 1;
175 for (int a = 0; a < Dim; ++a)
176 blockCells *= d.blockBrick()[a];
177 if (n != blockCells || n != d.local().numLeaves())
178 throw std::runtime_error(
179 "amr::ReplicatedTailStage: the moved level does not tile its block with root cells (the "
180 "lift is not nested)");
181 target_.kind = cd::StageKind::Replicated; // ONE block, the whole level, on every rank
182 target_.dec.init(1, G_);
183 target_.ownerOf = {0};
184 target_.groupOf.clear();
185 oct_.init(G_, d.lmax(), IVec<Dim>{});
186 const Index W = d.rootSpan();
187
188 // The two index maps core's topology calls once per cell: a global cell of the moved level's
189 // grid → this rank's leaf holding it, and → the tail octree's row. Every leaf of the moved
190 // level is a root cell of the lifted root grid, which is what nesting buys.
191 auto srcIndex = [&](const IVec<Dim>& g) -> Index {
192 const Index i = d.findGlobalRoot(g);
193 if (i < 0 || d.local().level(i) != d.lmax())
194 throw std::runtime_error(
195 "amr::ReplicatedTailStage: a cell of this rank's block is not one of its root leaves "
196 "(the lift is not nested)");
197 return i;
198 };
199 auto dstIndex = [&](const IVec<Dim>& g) -> Index {
200 std::array<typename Octree::Coord, Dim> f{};
201 for (int a = 0; a < Dim; ++a)
202 f[a] = static_cast<typename Octree::Coord>(g[a] * W);
203 return oct_.find(f);
204 };
205 comm_ = cd::makeStageComm<Dim>(comm, target_);
206 topo_.build(d.decomposition(), target_, comm_, srcIndex, dstIndex);
207
208 // The moved level's α rows, one field per face, gathered into target leaf order.
209 const int F = 2 * Dim;
210 const std::vector<double>& aloc = ap.opennessRaw();
211 std::vector<std::vector<double>> aCol(static_cast<std::size_t>(F),
212 std::vector<double>(static_cast<std::size_t>(n), 1.0));
213 if (!aloc.empty())
214 for (Index i = 0; i < n; ++i)
215 for (int f = 0; f < F; ++f)
216 aCol[static_cast<std::size_t>(f)][static_cast<std::size_t>(i)] =
217 aloc[static_cast<std::size_t>(i) * F + static_cast<std::size_t>(f)];
218 std::vector<std::vector<double>> tCol(static_cast<std::size_t>(F),
219 std::vector<double>(static_cast<std::size_t>(nt_), 1.0));
220 std::vector<const double*> aSrc(static_cast<std::size_t>(F));
221 std::vector<double*> aDst(static_cast<std::size_t>(F));
222 for (int f = 0; f < F; ++f) {
223 aSrc[static_cast<std::size_t>(f)] = aCol[static_cast<std::size_t>(f)].data();
224 aDst[static_cast<std::size_t>(f)] = tCol[static_cast<std::size_t>(f)].data();
225 }
226 topo_.forward(aSrc, aDst);
227 std::vector<double> aTail(static_cast<std::size_t>(nt_) * F);
228 for (Index r = 0; r < nt_; ++r)
229 for (int f = 0; f < F; ++f)
230 aTail[static_cast<std::size_t>(r) * F + static_cast<std::size_t>(f)] =
231 tCol[static_cast<std::size_t>(f)][static_cast<std::size_t>(r)];
232
233 mg_ = std::make_unique<Multigrid<Dim, Bits>>();
234 mg_->buildRaw(oct_, h0, std::move(aTail), /*periodic=*/true, /*liftRoot=*/true, bottomExtent);
235
236 (void)nExt;
237 // Sized by the level's OWN cell count, and staged through device scratch, so the caller may
238 // hand in a longer view (the distributed levels carry a ghost tail) without a subview.
239 dSrc_ = View<double>("stage_dsrc", static_cast<std::size_t>(n));
240 dDst_ = View<double>("stage_ddst", static_cast<std::size_t>(n));
241 srcMirror_ = Kokkos::View<double*, Kokkos::HostSpace>("stage_src", static_cast<std::size_t>(n));
242 dstMirror_ = Kokkos::View<double*, Kokkos::HostSpace>("stage_dst", static_cast<std::size_t>(n));
243 tbMirror_ = Kokkos::View<double*, Kokkos::HostSpace>("stage_tb", static_cast<std::size_t>(nt_));
244 txMirror_ = Kokkos::View<double*, Kokkos::HostSpace>("stage_tx", static_cast<std::size_t>(nt_));
245 }
246
247 const char* kind() const override { return "replicated"; }
248 const char* diagnosticSuffix() const override { return "+tail"; }
249 const Decomposition& targetDecomposition() const override { return target_.dec; }
250 MPI_Comm targetComm() const override { return comm_.sub; }
251 bool active() const override { return true; } // replicated: every rank owns the whole level
252 const Octree& targetOctree() const override { return oct_; }
253
254 std::size_t numLevels() const override { return mg_->numLevels(); }
255 Index numLeaves(std::size_t L) const override { return mg_->numLeaves(L); }
256 std::string bottomName() const override { return mg_->bottomName(); }
257 void setRemoveMean(bool on) override { mg_->setRemoveMean(on); }
258 void setBottom(typename Multigrid<Dim, Bits>::Bottom b) override { mg_->setBottom(b); }
259 View<double> targetX() override { return mg_->x(0); }
260
262 Multigrid<Dim, Bits>& multigrid() { return *mg_; }
263
265 auto ds = dSrc_;
266 Kokkos::parallel_for("amr::stage_pack", nSrc, KOKKOS_LAMBDA(const Index i) { ds(i) = src(i); });
267 Kokkos::deep_copy(srcMirror_, dSrc_);
268 topo_.forward({srcMirror_.data()}, {tbMirror_.data()});
269 Kokkos::deep_copy(mg_->b(0), tbMirror_);
270 Kokkos::deep_copy(mg_->x(0), 0.0);
271 }
272
273 void cycle(int pre, int post, int bottom, double omega) override {
274 mg_->vcycle(pre, post, bottom, omega);
275 }
276
278 Kokkos::deep_copy(txMirror_, mg_->x(0));
279 topo_.backward({txMirror_.data()}, {dstMirror_.data()}); // a local pick: no communication
280 Kokkos::deep_copy(dDst_, dstMirror_);
281 auto dd = dDst_;
282 Kokkos::parallel_for(
283 "amr::stage_scatter", nDst, KOKKOS_LAMBDA(const Index i) { dst(i) = dd(i); });
284 }
285
286 private:
287 IVec<Dim> G_{};
288 Index nt_ = 0;
289 core::decomp::StageTarget<Dim> target_;
290 core::decomp::StageComm comm_; // owns its communicators; must outlive topo_'s use of them
291 core::decomp::RedistributeTopology<Dim, double> topo_;
292 Octree oct_;
293 std::unique_ptr<Multigrid<Dim, Bits>> mg_;
294 View<double> dSrc_, dDst_;
297 Kokkos::View<double*, Kokkos::HostSpace> srcMirror_, dstMirror_, tbMirror_, txMirror_;
298};
299
300} // namespace peclet::amr
301
302#endif // PECLET_AMR_MG_STAGE_HPP
const std::vector< double > & opennessRaw() const
Definition poisson.hpp:157
unsigned level(Index i) const
const IVec< Dim > & globalRootSize() const
const std::array< bool, Dim > & periodic() const
Index findGlobalRoot(const IVec< Dim > &g) const
Local leaf covering global root-cell g, or -1 if not owned by this rank (lmax==0).
const decomp::BlockDecomposer< Dim > & decomposition() const
The ORB over the global ROOT grid in the CURRENT root units — rank r owns block r.
const IVec< Dim > & blockBrick() const
One stage of the pressure hierarchy (docs/amr_mg_depth.md §6.5).
Definition mg_stage.hpp:60
virtual View< double > targetX()=0
The continued ladder's level-0 solution — what moveDown reads.
virtual void moveDown(View< double > dst, Index nDst)=0
The target's level-0 solution → the first nDst rows of dst.
void apply(View< const double > residual, View< double > correction, Index n, int pre, int post, int bottom, double omega)
The whole stage for one V-cycle, in the CORRECTION SCHEME §6.5 prescribes: the caller hands in the re...
Definition mg_stage.hpp:117
BlockOctree< Dim, Bits > Octree
a level's cells on one block
Definition mg_stage.hpp:62
virtual void moveUp(View< const double > src, Index nSrc)=0
src (this rank's rows of the moved level, device; may be longer than nSrc) → the target's level-0 rhs...
virtual const char * kind() const =0
"replicated" | "sibling" | "repartition".
virtual std::string bottomName() const =0
"jacobi" | "amg": what solves the continued ladder's coarsest level, WITHOUT the stage's diagnosticSu...
virtual bool active() const =0
Whether THIS rank owns a target block, i.e.
virtual ~MgStage()=default
virtual const Octree & targetOctree() const =0
The moved level's cells on this rank's target block.
virtual void cycle(int pre, int post, int bottom, double omega)=0
One cycle of the continued ladder on targetComm(). Called on active ranks only.
virtual void setRemoveMean(bool on)=0
Per-level constant-nullspace projection for the singular periodic pressure; the owner forwards its ow...
virtual Index numLeaves(std::size_t L) const =0
Cells of continued-ladder level L on this rank's target block — the GLOBAL count for the replicated s...
virtual std::size_t numLevels() const =0
Levels of the continued ladder, its level 0 (the moved level) included. Local.
core::decomp::BlockDecomposer< Dim > Decomposition
the target partition's type
Definition mg_stage.hpp:63
virtual void setBottom(typename Multigrid< Dim, Bits >::Bottom b)=0
What solves the continued ladder's coarsest level (docs/amr_mg_depth.md §6.6).
virtual MPI_Comm targetComm() const =0
The communicator every collective of the continued ladder runs on.
virtual const Decomposition & targetDecomposition() const =0
The decomposition of the moved level's grid this stage targets.
virtual const char * diagnosticSuffix() const =0
What pressure_mg_bottom appends for this stage — "+tail" for the replicated one, which is the spellin...
Bottom
What solves the coarsest level (docs/amr_mg_depth.md §6.6).
The REPLICATED stage (docs/amr_mg_depth.md §6.5.1): the target decomposition is one block — the whole...
Definition mg_stage.hpp:138
void moveUp(View< const double > src, Index nSrc) override
src (this rank's rows of the moved level, device; may be longer than nSrc) → the target's level-0 rhs...
Definition mg_stage.hpp:264
MPI_Comm targetComm() const override
The communicator every collective of the continued ladder runs on.
Definition mg_stage.hpp:250
std::size_t numLevels() const override
Levels of the continued ladder, its level 0 (the moved level) included. Local.
Definition mg_stage.hpp:254
Index numLeaves(std::size_t L) const override
Cells of continued-ladder level L on this rank's target block — the GLOBAL count for the replicated s...
Definition mg_stage.hpp:255
void cycle(int pre, int post, int bottom, double omega) override
One cycle of the continued ladder on targetComm(). Called on active ranks only.
Definition mg_stage.hpp:273
typename Base::Octree Octree
the gathered level, one block
Definition mg_stage.hpp:141
bool active() const override
Whether THIS rank owns a target block, i.e.
Definition mg_stage.hpp:251
void setBottom(typename Multigrid< Dim, Bits >::Bottom b) override
What solves the continued ladder's coarsest level (docs/amr_mg_depth.md §6.6).
Definition mg_stage.hpp:258
const Octree & targetOctree() const override
The moved level's cells on this rank's target block.
Definition mg_stage.hpp:252
const Decomposition & targetDecomposition() const override
The decomposition of the moved level's grid this stage targets.
Definition mg_stage.hpp:249
void moveDown(View< double > dst, Index nDst) override
The target's level-0 solution → the first nDst rows of dst.
Definition mg_stage.hpp:277
std::string bottomName() const override
"jacobi" | "amg": what solves the continued ladder's coarsest level, WITHOUT the stage's diagnosticSu...
Definition mg_stage.hpp:256
void setRemoveMean(bool on) override
Per-level constant-nullspace projection for the singular periodic pressure; the owner forwards its ow...
Definition mg_stage.hpp:257
const char * diagnosticSuffix() const override
What pressure_mg_bottom appends for this stage — "+tail" for the replicated one, which is the spellin...
Definition mg_stage.hpp:248
Multigrid< Dim, Bits > & multigrid()
The continued ladder itself — a single-rank Multigrid, exposed for the WO5 bottom selector.
Definition mg_stage.hpp:262
void build(const DO &d, const Vec< Dim > &h0, const Poisson &ap, Index n, Index nExt, Index bottomExtent, MPI_Comm comm)
Build the stage from the level it fires at: d is that level's distributed octree (lifted,...
Definition mg_stage.hpp:162
typename Base::Decomposition Decomposition
one block: the whole level
Definition mg_stage.hpp:142
View< double > targetX() override
The continued ladder's level-0 solution — what moveDown reads.
Definition mg_stage.hpp:259
const char * kind() const override
"replicated" | "sibling" | "repartition".
Definition mg_stage.hpp:247
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,...
Definition adapt.hpp:51