25#ifndef PECLET_AMR_MULTIGRID_HPP
26#define PECLET_AMR_MULTIGRID_HPP
40#include "peclet/core/common/host_parallel.hpp"
41#include "peclet/core/common/view.hpp"
50 const Index a = childStart(
p),
z = childStart(
p + 1);
53 s +=
fine(childIdx(k));
54 coarse(
p) = (
z >
a) ?
s /
static_cast<double>(
z -
a) : 0.0;
69 const Index a = childStart(
p),
z = childStart(
p + 1);
70 double sw = 0.0,
swv = 0.0;
73 const double w = kappa(
ch);
110 Kokkos::parallel_for(
117template <
int Dim,
unsigned Bits = (Dim == 2 ? 32u : (Dim == 3 ? 21u : 16u))>
145 hmg_ = std::make_unique<AmrMultigrid<Dim, Bits>>();
154 template <
class OpenFn>
157 build(
finest, detail::filledVec<Dim>(h0), std::forward<OpenFn>(
openFn), periodic, immersedWall,
164 template <
class OpenFn>
168 hmg_ = std::make_unique<AmrMultigrid<Dim, Bits>>();
170 hmg_->setOpenness(std::forward<OpenFn>(
openFn));
171 hmg_->setPeriodic(periodic);
172 hmg_->setImmersedWall(immersedWall);
185 hmg_ = std::make_unique<AmrMultigrid<Dim, Bits>>();
187 hmg_->setOpennessRaw(std::move(
alphaRaw));
188 hmg_->setPeriodic(periodic);
209 for (
auto&
lv : levels_) {
216 if (c0 != 0.0 || cD != 1.0)
233 std::string
bottomName()
const {
return amgOn_ ?
"amg" :
"jacobi"; }
254 std::size_t
numLevels()
const {
return levels_.size(); }
259 const FvOp&
op(std::size_t
L = 0)
const {
return levels_[
L].op; }
267 Level&
lv = levels_[
L];
269 if (
L + 1 == levels_.size()) {
271 bottomSolveExact(
lv);
280 for (
int s = 0;
s <
pre; ++
s)
283 Level&
cl = levels_[
L + 1];
289 Kokkos::deep_copy(
cl.x, 0.0);
305 Level&
lv = levels_[0];
306 Kokkos::deep_copy(b0true_,
lv.b);
314 Kokkos::parallel_for(
325 Kokkos::parallel_reduce(
326 "amr::quad_resnorm",
lv.n,
328 double rr = res(
i) -
dq(
i);
333 Kokkos::deep_copy(
lv.b, b0true_);
350 void buildFromHostMg() {
351 const std::size_t
nl = hmg_->numLevels();
354 for (std::size_t
L = 0;
L <
nl; ++
L) {
357 Level&
lv = levels_[
L];
358 lv.n =
oct.numLeaves();
359 buildFaceCsr(ap,
oct,
lv);
362 std::vector<double>
kap(
static_cast<std::size_t
>(
lv.n));
365 for (int axis = 0; axis < Dim; ++axis)
366 for (int dir = -1; dir <= 1; dir += 2)
367 s += ap.faceOpenness(i, axis, dir);
368 kap[static_cast<std::size_t>(i)] = s / static_cast<double>(2 * Dim);
376 for (std::size_t
L = 0;
L + 1 <
nl; ++
L) {
377 const Octree&
f = hmg_->op(
L).octree();
378 const Octree&
c = hmg_->op(
L + 1).octree();
379 const Index nf =
f.numLeaves(),
nc =
c.numLeaves();
380 std::vector<Index> c2p(
static_cast<std::size_t
>(
nf));
381 std::vector<Index>
cnt(
static_cast<std::size_t
>(
nc), 0);
391 const Index p = c2p[
static_cast<std::size_t
>(
i)];
393 ++
cnt[
static_cast<std::size_t
>(
p)];
395 std::vector<Index> start(
static_cast<std::size_t
>(
nc) + 1, 0);
397 start[
static_cast<std::size_t
>(
p) + 1] =
398 start[
static_cast<std::size_t
>(
p)] +
cnt[
static_cast<std::size_t
>(
p)];
399 std::vector<Index> idx(
static_cast<std::size_t
>(
nf));
400 std::vector<Index>
cur(start.begin(), start.end() - 1);
402 Index p = c2p[
static_cast<std::size_t
>(
i)];
404 idx[
static_cast<std::size_t
>(
cur[
static_cast<std::size_t
>(
p)]++)] =
i;
406 levels_[
L].c2p =
toDevice(c2p,
"mg_c2p");
407 levels_[
L].childStart =
toDevice(start,
"mg_cstart");
408 levels_[
L].childIdx =
toDevice(idx,
"mg_cidx");
412 buildQuadCsr(hmg_->op(0), hmg_->op(0).octree());
432 if (!hmg_ || levels_.empty())
434 const std::size_t
Lc = levels_.size() - 1;
437 for (
int d = 0; d < Dim; ++d)
438 E = std::max(
E, ap.octree().brick()[d]);
445 const auto A = ap.assembleFv();
447 for (
double d :
A.bcDiag)
452 amg_ = std::make_unique<AmgBottom<Dim, Bits>>();
459 void bottomSolveExact(Level&
lv) {
466 double rm = 0.0,
bm = 0.0;
467 Kokkos::parallel_reduce(
468 "amr::bottom_check",
lv.n,
470 const double r = res(
i) < 0.0 ? -res(
i) : res(
i);
471 const double b =
bb(
i) < 0.0 ? -
bb(
i) :
bb(
i);
477 Kokkos::Max<double>(
rm), Kokkos::Max<double>(
bm));
478 bottomRel_ = (
bm > 0.0) ?
rm /
bm : 0.0;
482 const double c0 =
lv.op.c0, cD =
lv.op.cD;
483 BlockOctreeView<Dim, Bits> ov;
497 const std::size_t
nl = hmg_->numLevels();
498 for (std::size_t
L = 0;
L <
nl &&
L < levels_.size(); ++
L)
499 buildFaceCsr(hmg_->op(
L), hmg_->op(
L).octree(), levels_[
L]);
507 const Index n =
t.numLeaves();
508 std::vector<std::vector<std::pair<Index, double>>>
per(
static_cast<std::size_t
>(n));
511 const unsigned Li =
t.level(
i);
512 const double invV = 1.0 / ap.cellVolume(
i);
513 ap.forEachFaceNeighbor(
i, [&](
Index j,
Real c,
int axis,
double a) {
514 const unsigned Lj =
t.level(
j);
524 std::vector<Index>
qs(
static_cast<std::size_t
>(n) + 1, 0);
526 qs[
static_cast<std::size_t
>(
i) + 1] =
527 qs[
static_cast<std::size_t
>(
i)] +
528 static_cast<Index>(
per[
static_cast<std::size_t
>(
i)].size());
529 const Index nq =
qs[
static_cast<std::size_t
>(n)];
530 std::vector<Index> slot(
static_cast<std::size_t
>(
nq));
531 std::vector<double> coef(
static_cast<std::size_t
>(
nq));
535 slot[
static_cast<std::size_t
>(k)] =
e.first;
536 coef[
static_cast<std::size_t
>(k)] =
e.second;
540 qSlot_ =
toDevice(slot,
"mg_qslot");
541 qCoef_ =
toDevice(coef,
"mg_qcoef");
547 static void addCoarseStarStencil(
const Poisson& ap,
const Octree&
t,
552 const double sc =
static_cast<double>(
Index(1) <<
t.level(
coarse));
553 const double sf =
static_cast<double>(
Index(1) <<
t.level(
fine));
554 for (
int tt = 0;
tt < Dim; ++
tt) {
559 const double H = ap.cellWidth(
coarse,
tt);
560 const double dt = ((
static_cast<double>(
bf[0][
tt]) + 0.5 *
sf) -
561 (
static_cast<double>(
bc[0][
tt]) + 0.5 *
sc)) *
565 if (
cp < 0 ||
cm < 0)
569 if (ap.faceOpenness(
coarse,
tt, +1) < 0.5 || ap.faceOpenness(
coarse,
tt, -1) < 0.5)
571 const double cUp =
dt / (2.0 *
H) + 0.5 *
dt *
dt / (
H *
H);
572 const double cUm = -
dt / (2.0 *
H) + 0.5 *
dt *
dt / (
H *
H);
583 std::unique_ptr<AmrMultigrid<Dim, Bits>> hmg_;
584 std::vector<Level> levels_;
585 bool kappaRestrict_ =
false;
586 bool removeMean_ =
false;
588 Index bottomExtent_ = 4;
589 bool amgOn_ =
false, bottomCheck_ =
false;
590 double bottomRel_ = 0.0;
591 std::unique_ptr<AmgBottom<Dim, Bits>> amg_;
morton::Morton< Dim, Bits > M
typename M::code_type Code
BlockOctree< Dim, Bits > Octree
const FvOp & op(std::size_t L=0) const
View< Index > quadSlot() const
AmrPoisson< Dim, Bits > Poisson
View< Index > quadStart() const
void setHelmholtz(double c0, double cD)
Turn every level's operator into the Helmholtz form H = c0·I + cD·L (default c0=0,...
Code octreeCode(std::size_t L, Index i) const
int bottomIters() const
Inner CG iterations of the last exact bottom solve (0 without it).
Bottom bottom() const
The selection in force (Auto unless setBottom said otherwise).
void reassembleOperators()
Re-assemble every level's operator ON THE DEVICE from the (host) hierarchy's current geometry — the d...
Index numLeaves(std::size_t L=0) const
Index bottomExtent() const
The extent (cells per axis) the last build stopped the ladder at and Auto compares with.
int bottomComponents() const
Connected fluid components of the coarsest level (0 without the exact bottom).
View< double > b(std::size_t L=0)
double solveQuad(int outer=20, int cyclesPerOuter=1, int pre=2, int post=2, int bottom=40, double omega=0.8)
Solve L_quad u = rhs (the 2nd-order graded operator) by deferred correction: each outer step solves L...
typename Octree::Code Code
void build(const Octree &finest, const Vec< Dim > &h0, OpenFn &&openFn, bool periodic=true, bool immersedWall=false, bool liftRoot=true, Index bottomExtent=4)
The per-axis form of the openness build (see the scalar-h0 overload above).
void vcycle(int pre=2, int post=2, int bottom=40, double omega=0.8, std::size_t L=0)
One V-cycle on level L (default finest) of the standard (consistent conservative) operator,...
double bottomResidual() const
max|b − L x| / max|b| of the last checked bottom solve (0 unless setBottomCheck(true)).
void build(const Octree &finest, const Vec< Dim > &h0, bool liftRoot=true, Index bottomExtent=4)
The per-axis form: h0 is the finest spacing (dx, dy, dz); every level inherits the root aspect ratio ...
void build(const Octree &finest, double h0, bool liftRoot=true, Index bottomExtent=4)
Build + upload the hierarchy from a finest octree (uniform coarsening), openness- free.
void build(const Octree &finest, double h0, OpenFn &&openFn, bool periodic=true, bool immersedWall=false, bool liftRoot=true, Index bottomExtent=4)
Build with cut-cell openness openFn(faceCentreWorld, axis) → [0,1], coarsened to every level by area-...
Bottom
What solves the coarsest level (docs/amr_mg_depth.md §6.6).
void setBottom(Bottom b)
Select what solves the coarsest level (§6.6).
View< double > x(std::size_t L=0)
void setBottomCheck(bool on)
Opt-in §6.6 consistency gate (flow's AGMG_DEBUG): after every exact bottom solve, recompute |b − L x|...
void buildRaw(const Octree &finest, const Vec< Dim > &h0, std::vector< double > alphaRaw, bool periodic=true, bool liftRoot=true, Index bottomExtent=4)
Build from a finest octree whose face openness is handed over ALREADY EVALUATED — alphaRaw in the Amr...
std::string bottomName() const
"jacobi" | "amg" — what the coarsest level ACTUALLY runs.
void setRemoveMean(bool on)
Opt-in (default off): project the correction to mean-zero over fluid cells at every V-cycle level — t...
Index bottomIdentityRows() const
Fully closed coarsest cells the exact bottom solves as identity rows (0 without it).
bool bottomOnDevice() const
Whether the exact bottom's per-V-cycle solve runs on the device (§5.5's 10^4-row rule).
std::size_t numLevels() const
View< double > quadCoef() const
Index bottomSize() const
Rows of the exact bottom's matrix (0 when the sweeps run instead).
void setKappaRestrict(bool on)
Opt-in: use κ-weighted (fluid-fraction) restriction instead of the default plain volume-average.
void zeroMasked(View< double > v, View< const char > excl, Index n)
Zero v at excluded cells (excl != 0).
void removeMeanFv(const FvOp &op, View< double > u)
Project u to volume-weighted-mean-zero over the ACTIVE (fluid) cells of the operator — the constant-n...
void residualFv(const FvOp &op, View< const double > u, View< const double > rhs, View< double > res)
res = rhs − H u.
void restrictKappa(View< const Index > childStart, View< const Index > childIdx, View< const double > fine, View< const double > kappa, View< double > coarse, Index nCoarse)
Restrict, κ-weighted: coarse(p) = Σ_child κ_c·fine_c / Σ_child κ_c, with κ the fine cell's fluid-frac...
void jacobiFv(const FvOp &op, View< double > u, View< const double > rhs, View< double > tmp, double omega)
One weighted-Jacobi sweep of H u = rhs (in place).
void prolongAddMasked(View< const Index > c2p, View< const double > coarse, View< const char > excl, View< double > fine, Index nFine)
Masked piecewise-constant prolong + correct: fine(i) += coarse(c2p(i)) only on non-excluded fine cell...
void quadDelta(View< const Index > qStart, View< const Index > qSlot, View< const double > qCoef, View< const double > u, View< double > dq, Index n)
dq = (L_quad − L_std) u, the quadratic coarse-fine correction as its own SpMV over a precomputed CSR ...
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,...