24#ifndef PECLET_AMR_POISSON_HPP
25#define PECLET_AMR_POISSON_HPP
36#include "peclet/core/common/host_parallel.hpp"
37#include "peclet/core/common/types.hpp"
42template <
int Dim,
unsigned Bits = (Dim == 2 ? 32u : (Dim == 3 ? 21u : 16u))>
63 extResolve_ =
nullptr;
67 for (
int d = 0; d < Dim; ++d)
68 fineExt_[d] =
static_cast<Coord>(
t.brick()[d] * (
Index(1) <<
t.lmax()));
86 void setGhosts(std::vector<std::array<long, Dim>>
lo, std::vector<unsigned>
lv) {
87 ghostLo_ = std::move(
lo);
88 ghostLv_ = std::move(
lv);
100 : ghostLv_[
static_cast<std::size_t
>(slot -
numLeaves())];
104 std::array<long, Dim>
lo{};
106 auto b = t_->
bounds(slot);
107 for (
int d = 0; d < Dim; ++d)
108 lo[d] =
static_cast<long>(b[0][d]);
110 lo = ghostLo_[
static_cast<std::size_t
>(slot -
numLeaves())];
120 std::pair<Index, unsigned>
probeSlot(
const std::array<long, Dim>&
p)
const {
122 for (
int d = 0; d < Dim; ++d)
123 if (
p[d] < 0 ||
p[d] >=
static_cast<long>(fineExt_[d])) {
127 std::array<Coord, Dim>
q{};
130 const Index s = extResolve_(
p);
133 for (
int d = 0; d < Dim; ++d) {
134 const long e =
static_cast<long>(fineExt_[d]);
135 q[d] =
static_cast<Coord>(((
p[d] %
e) +
e) %
e);
138 for (
int d = 0; d < Dim; ++d)
139 q[d] =
static_cast<Coord>(
p[d]);
147 static int faceIndex(
int axis,
int dir) {
return 2 * axis + (dir > 0 ? 0 : 1); }
159 alpha_ = std::move(
a);
166 template <
class OpenFn>
170 alpha_.assign(
static_cast<std::size_t
>(n +
ng) *
kFaces, 1.0);
176 for (
int axis = 0; axis < Dim; ++axis)
177 for (
int dir = -1; dir <= 1; dir += 2) {
179 (dir > 0) ?
lo[axis] + frameShift_[axis] +
s :
lo[axis] + frameShift_[axis];
181 for (
int d = 0; d < Dim; ++d)
182 fc[d] = (d == axis) ? origin_[d] +
static_cast<Real>(
plane) * h0_[d]
183 : origin_[d] + (
static_cast<Real>(
lo[d] + frameShift_[d]) +
184 0.5 *
static_cast<Real>(
s)) *
186 double a =
static_cast<double>(
openFn(
fc, axis));
187 a =
a < 0.0 ? 0.0 : (
a > 1.0 ? 1.0 :
a);
196 std::array<long, Dim>
lo{};
197 for (
int d = 0; d < Dim; ++d)
198 lo[d] =
static_cast<long>(b[0][d]);
202 fillRow(n + g, ghostLo_[
static_cast<std::size_t
>(g)],
203 1L << ghostLv_[
static_cast<std::size_t
>(g)]);
230 if (periodic_ && !immersedWall_)
233 const auto&
lo = b[0];
236 for (
int axis = 0; axis < Dim; ++axis) {
239 const double wall = areaOf(
si, axis) / (0.5 *
static_cast<Real>(
si) * h0_[axis]);
240 for (
int dir = -1; dir <= 1; dir += 2) {
241 const long pc = (dir > 0) ?
static_cast<long>(
lo[axis]) +
static_cast<long>(
si)
242 :
static_cast<long>(
lo[axis]) - 1;
244 !periodic_ && (
pc < 0 || pc >=
static_cast<long>(fineExt_[axis]));
247 else if (immersedWall_)
265 for (
int d = 0; d < Dim; ++d)
276 const auto&
lo = b[0];
279 for (
int axis = 0; axis < Dim; ++axis)
280 for (
int dir = -1; dir <= 1; dir += 2) {
281 const long pc = (dir > 0) ?
static_cast<long>(
lo[axis]) +
static_cast<long>(
si)
282 :
static_cast<long>(
lo[axis]) - 1;
285 if (!periodic_ && (
pc < 0 || pc >=
static_cast<long>(fineExt_[axis])))
287 std::array<long, Dim>
p{};
288 for (
int d = 0; d < Dim; ++d)
289 p[d] =
static_cast<long>(
lo[d]);
301 const int nsub = 1 << (Dim - 1);
302 for (
int k = 0; k <
nsub; ++k) {
303 std::array<long, Dim>
q =
p;
305 for (
int t = 0;
t < Dim; ++
t) {
308 q[
t] =
static_cast<long>(
lo[
t]) + (((k >>
bit) & 1) ?
static_cast<long>(
sj) : 0
L);
331 const auto&
lo = b[0];
334 for (
int axis = 0; axis < Dim; ++axis)
335 for (
int dir = -1; dir <= 1; dir += 2) {
336 const long pc = (dir > 0) ?
static_cast<long>(
lo[axis]) +
static_cast<long>(
si)
337 :
static_cast<long>(
lo[axis]) - 1;
338 std::array<long, Dim>
p{};
339 for (
int d = 0; d < Dim; ++d)
340 p[d] =
static_cast<long>(
lo[d]);
347 fn(
j, axis, dir, areaOf(
si, axis),
348 0.5 * (
static_cast<Real>(
si) +
static_cast<Real>(
sj)) * h0_[axis],
352 const int nsub = 1 << (Dim - 1);
353 for (
int k = 0; k <
nsub; ++k) {
354 std::array<long, Dim>
q =
p;
356 for (
int t = 0;
t < Dim; ++
t) {
359 q[
t] =
static_cast<long>(
lo[
t]) + (((k >>
bit) & 1) ?
static_cast<long>(
sj) : 0
L);
365 fn(
jj, axis, dir, areaOf(
sj, axis),
366 0.5 * (
static_cast<Real>(
si) +
static_cast<Real>(
sj)) * h0_[axis],
376 std::array<long, Dim>
p =
loOf(
i);
378 p[axis] = (dir > 0) ?
p[axis] +
si :
p[axis] - 1;
390 const double uc =
u[
static_cast<std::size_t
>(
coarse)];
396 for (
int t = 0;
t < Dim; ++
t) {
403 ((
static_cast<double>(
bf[
t]) + 0.5 *
sf) - (
static_cast<double>(
bc[
t]) + 0.5 *
sc)) *
407 if (
cp < 0 ||
cm < 0)
416 const double up =
u[
static_cast<std::size_t
>(
cp)];
417 const double um =
u[
static_cast<std::size_t
>(
cm)];
418 const double Dt = (
up -
um) / (2.0 *
H);
428 out.assign(
static_cast<std::size_t
>(n), 0.0);
430 const double ui =
u[
static_cast<std::size_t
>(
i)];
435 double uj =
u[
static_cast<std::size_t
>(
j)];
449 std::vector<double>& res)
const {
450 std::vector<double>
lu;
453 res.assign(
static_cast<std::size_t
>(n), 0.0);
456 double r =
rhs[
static_cast<std::size_t
>(
i)] -
lu[
static_cast<std::size_t
>(
i)];
457 res[
static_cast<std::size_t
>(
i)] =
r;
477 A.
start.assign(
static_cast<std::size_t
>(n) + 1, 0);
481 A.start[
static_cast<std::size_t
>(
i) + 1] =
A.start[
static_cast<std::size_t
>(
i)] +
cnt;
483 const Index nf =
A.start[
static_cast<std::size_t
>(n)];
484 A.nbr.resize(
static_cast<std::size_t
>(
nf));
485 A.coef.resize(
static_cast<std::size_t
>(
nf));
486 A.invVol.resize(
static_cast<std::size_t
>(n));
487 A.bcDiag.resize(
static_cast<std::size_t
>(n));
491 Index k =
A.start[
static_cast<std::size_t
>(
i)];
493 A.nbr[
static_cast<std::size_t
>(k)] =
j;
494 A.coef[
static_cast<std::size_t
>(k)] =
a *
c;
518 out.assign(
static_cast<std::size_t
>(n), 0.0);
521 out[
static_cast<std::size_t
>(
i)] = fvApplyRow(op,
i,
uacc);
527 out.assign(
static_cast<std::size_t
>(n), 0.0);
529 const double ui =
u[
static_cast<std::size_t
>(
i)];
532 acc +=
a *
c * (
u[
static_cast<std::size_t
>(
j)] -
ui);
539 double residual(
const std::vector<double>&
u,
const std::vector<double>&
rhs,
540 std::vector<double>& res)
const {
542 res.assign(
static_cast<std::size_t
>(n), 0.0);
545 const double ui =
u[
static_cast<std::size_t
>(
i)];
548 acc +=
a *
c * (
u[
static_cast<std::size_t
>(
j)] -
ui);
551 res[
static_cast<std::size_t
>(
i)] =
r;
562 double sumOff = 0.0, diag = 0.0;
564 sumOff +=
a *
c *
u[
static_cast<std::size_t
>(
j)];
568 u[
static_cast<std::size_t
>(
i)] =
576 double sum = 0.0,
vol = 0.0;
581 double m = sum /
vol;
583 u[
static_cast<std::size_t
>(
i)] -= m;
592 const std::array<Coord, Dim>&
fineExt()
const {
return fineExt_; }
596 Coord wrap(
long c,
int axis)
const {
597 long e =
static_cast<long>(fineExt_[axis]);
598 return static_cast<Coord>(((
c %
e) +
e) %
e);
603 Real dist = 0.5 * (
static_cast<Real>(
si) +
static_cast<Real>(
sj)) * h0_[axis];
604 return areaOf(
si <
sj ?
si :
sj, axis) / dist;
610 for (
int d = 0; d < Dim; ++d)
616 Real areaOf(
Coord s)
const {
return areaOf(
s, 0); }
618 const Octree* t_ =
nullptr;
619 Vec<Dim> h0_ = detail::filledVec<Dim>(1.0);
620 std::array<Coord, Dim> fineExt_{};
623 std::vector<std::array<long, Dim>> ghostLo_;
624 std::vector<unsigned> ghostLv_;
625 std::array<long, Dim> frameShift_{};
626 std::vector<double> alpha_;
627 bool hasOpen_ =
false;
628 bool periodic_ =
true;
629 bool immersedWall_ =
false;
633template <
int Dim,
unsigned Bits = (Dim == 2 ? 32u : (Dim == 3 ? 21u : 16u))>
649 for (
int d = 0; d < Dim; ++d)
650 mx = std::max(
mx,
G[d]);
651 if (
mx <= bottomExtent)
653 for (
int d = 0; d < Dim; ++d)
654 if ((
G[d] % 2) != 0 || (
G[d] / 2) < 2)
656 return t.canLiftRoot();
668 build(
finest, detail::filledVec<Dim>(h0), liftRoot, bottomExtent);
674 Index bottomExtent = 4) {
676 levels_.push_back(
finest);
680 if (
merged == 0 ||
c.numLeaves() == levels_.back().numLeaves())
682 levels_.push_back(
c);
683 if (
c.numLeaves() == 1)
694 if (
merged == 0 ||
c.numLeaves() == levels_.back().numLeaves())
696 levels_.push_back(std::move(
c));
699 ops_.resize(levels_.size());
702 for (std::size_t
L = 0;
L < levels_.size(); ++
L)
703 ops_[
L].init(levels_[
L], h0);
705 c2p_.assign(levels_.size() ? levels_.size() - 1 : 0, {});
706 for (std::size_t
L = 0;
L + 1 < levels_.size(); ++
L) {
709 c2p_[
L].resize(
static_cast<std::size_t
>(
f.numLeaves()));
715 c2p_[L][static_cast<std::size_t>(i)] = c.find(f.code(i));
720 std::size_t
numLevels()
const {
return levels_.size(); }
735 o.setImmersedWall(w);
739 void vcycle(std::size_t
L, std::vector<double>&
u,
const std::vector<double>&
rhs,
int pre = 2,
741 if (
L + 1 == levels_.size()) {
742 ops_[
L].gaussSeidel(
u,
rhs, 40);
743 ops_[
L].removeMean(
u);
747 std::vector<double> res;
748 ops_[
L].residual(
u,
rhs, res);
753 std::vector<double>
crhs(
static_cast<std::size_t
>(
c.numLeaves()), 0.0);
754 std::vector<double>
cvol(
static_cast<std::size_t
>(
c.numLeaves()), 0.0);
755 for (
Index i = 0;
i <
f.numLeaves(); ++
i) {
756 Index p = c2p_[
L][
static_cast<std::size_t
>(
i)];
759 Real v = ops_[
L].cellVolume(
i);
760 crhs[
static_cast<std::size_t
>(
p)] +=
v * res[
static_cast<std::size_t
>(
i)];
761 cvol[
static_cast<std::size_t
>(
p)] +=
v;
763 for (
Index p = 0;
p <
c.numLeaves(); ++
p)
764 if (
cvol[
static_cast<std::size_t
>(
p)] > 0)
765 crhs[
static_cast<std::size_t
>(
p)] /=
cvol[
static_cast<std::size_t
>(
p)];
767 std::vector<double>
ccorr(
static_cast<std::size_t
>(
c.numLeaves()), 0.0);
771 for (
Index i = 0;
i <
f.numLeaves(); ++
i) {
772 Index p = c2p_[
L][
static_cast<std::size_t
>(
i)];
774 u[
static_cast<std::size_t
>(
i)] +=
ccorr[
static_cast<std::size_t
>(
p)];
777 ops_[
L].removeMean(
u);
787 const std::size_t n =
u.size();
788 std::vector<double>
lq,
ls,
rhsp(n), res;
791 P.applyLaplacianQuad(
u,
lq);
792 P.applyLaplacian(
u,
ls);
793 for (std::size_t
i = 0;
i < n; ++
i)
797 r =
P.residualQuad(
u,
rhs, res);
808 template <
class OpenFn>
812 ops_[0].buildOpenness(
openFn);
813 for (std::size_t
L = 0;
L + 1 < levels_.size(); ++
L)
827 ops_[0].setOpennessRaw(std::move(alpha));
828 for (std::size_t
L = 0;
L + 1 < levels_.size(); ++
L)
833 static int faceIdx(
int axis,
int dir) {
return 2 * axis + (dir > 0 ? 0 : 1); }
837 void coarsenOpenness(std::size_t
L) {
840 const int F = 2 * Dim;
841 std::vector<double>
ca(
static_cast<std::size_t
>(
c.numLeaves()) *
F, 0.0);
842 std::vector<int>
cnt(
static_cast<std::size_t
>(
c.numLeaves()) *
F, 0);
843 for (
Index i = 0;
i <
f.numLeaves(); ++
i) {
844 Index p = c2p_[
L][
static_cast<std::size_t
>(
i)];
847 const std::size_t base =
static_cast<std::size_t
>(
p) *
F;
848 if (
c.level(
p) ==
f.level(
i)) {
850 for (
int axis = 0; axis < Dim; ++axis)
851 for (
int dir = -1; dir <= 1; dir += 2) {
852 int fi = faceIdx(axis, dir);
853 ca[base +
fi] += ops_[
L].faceOpenness(
i, axis, dir);
858 unsigned oct = M::from_code(
f.code(
i)).child_index(
f.level(
i));
859 for (
int axis = 0; axis < Dim; ++axis) {
860 int dir = ((
oct >> axis) & 1) ? +1 : -1;
861 int fi = faceIdx(axis, dir);
862 ca[base +
fi] += ops_[
L].faceOpenness(
i, axis, dir);
867 for (std::size_t k = 0; k <
ca.size(); ++k)
869 ops_[
L + 1].setOpennessRaw(std::move(
ca));
872 std::vector<Octree> levels_;
873 std::vector<AmrPoisson<Dim, Bits>> ops_;
874 std::vector<std::vector<Index>> c2p_;
Geometric multigrid for AmrPoisson over a uniformly-coarsened octree hierarchy.
typename Octree::Code Code
void setOpenness(OpenFn &&openFn)
Set cut-cell face openness on the finest level from a geometry callable openFn(faceCentreWorld,...
void setOpennessRaw(std::vector< double > alpha)
Same, but the finest level's openness is handed over ALREADY EVALUATED (alpha is the AmrPoisson::open...
void setPeriodic(bool p)
Apply a boundary condition to every level (periodic default, or non-periodic homogeneous Dirichlet).
void vcycle(std::size_t L, std::vector< double > &u, const std::vector< double > &rhs, int pre=2, int post=2)
One V-cycle on level L solving L u = rhs (correction scheme).
const AmrPoisson< Dim, Bits > & op(std::size_t L=0) const
std::size_t numLevels() const
void setImmersedWall(bool w)
Enable the immersed no-slip (Dirichlet) wall on every level — the velocity operator.
static bool canLiftLevel(const Octree &t, Index bottomExtent)
The single-rank lift rule of docs/amr_mg_depth.md §6.2, evaluated on one level: may the hierarchy con...
BlockOctree< Dim, Bits > Octree
void build(const Octree &finest, Real h0, bool liftRoot=true, Index bottomExtent=4)
Build the hierarchy from a finest octree by uniform coarsening until a single leaf remains (or no ful...
double solveQuad(std::vector< double > &u, const std::vector< double > &rhs, int outer=30, int cyclesPerOuter=1)
Solve the quadratic C/F operator L_quad u = rhs by deferred correction: the standard-operator V-cycle...
void build(const Octree &finest, const Vec< Dim > &h0, bool liftRoot=true, Index bottomExtent=4)
Phase 3: every level shares the same PER-AXIS finest spacing — a coarse leaf carries a higher level,...
Cell-centered FV Poisson operator on one (periodic) block octree.
static constexpr int kFaces
const Vec< Dim > & origin() const
unsigned levelOf(Index slot) const
Octree level of an extended slot (local leaf or declared ghost).
void removeMean(std::vector< double > &u) const
Subtract the volume-weighted mean (fixes the periodic null space).
void buildOpenness(OpenFn &&openFn)
Build face openness from a geometry callable openFn(faceCentreWorld, axis) -> [0,1] (1 = fully fluid,...
Real cellWidth(Index i, int axis) const
Cell width along axis (Phase 3: per axis).
void setFrameShift(const std::array< long, Dim > &s)
Distributed frame shift: this block's global fine origin.
double residualQuad(const std::vector< double > &u, const std::vector< double > &rhs, std::vector< double > &res) const
L2 norm of rhs - L_quad u.
double boundaryDiag(Index i) const
Σ over leaf i's Dirichlet-wall faces of the wall weight A_f/(½·cellWidth), folded into the operator d...
void forEachFaceFull(Index i, Fn &&fn) const
Like forEachFaceNeighbor but exposes geometry for a consistent FV divergence/gradient: fn(neighbour,...
void applyFvShared(const std::vector< double > &u, std::vector< double > &out) const
out = L u via the SHARED face_csr.hpp FV kernel over the assembled CSR — the same arithmetic the devi...
const std::array< Coord, Dim > & fineExt() const
Per-axis fine-grid extent (brick·2^lmax) — the periodic wrap modulus.
const Vec< Dim > & h0() const
void forEachFaceNeighbor(Index i, Fn &&fn) const
Visit each face neighbour of leaf i: fn(neighbourSlot, coeff, axis, alpha) where coeff = A_f / d_f (p...
static int faceIndex(int axis, int dir)
std::pair< Index, unsigned > probeSlot(const std::array< long, Dim > &p) const
Covering slot + level of a block-local probe (longs, possibly outside the block).
bool immersedWall() const
double faceOpenness(Index i, int axis, int dir) const
Openness of leaf i's face on (axis,dir); 1 if no openness has been set.
void applyLaplacianQuad(const std::vector< double > &u, std::vector< double > &out) const
out = L u with the quadratic coarse-fine flux (2nd-order at 2:1 interfaces).
Index periodicNeighbor(Index i, int axis, int dir) const
Periodic face neighbour leaf (covering the cell just across the face).
void setGhosts(std::vector< std::array< long, Dim > > lo, std::vector< unsigned > lv)
Declare the ghost slots [n, n+nGhost): block-local lo corner (longs — ghosts lie outside the block) a...
void init(const Octree &t, const Vec< Dim > &h0)
double residual(const std::vector< double > &u, const std::vector< double > &rhs, std::vector< double > &res) const
res = rhs - L u, returns its L2 norm (sqrt(sum V_i res_i^2)).
FvCsrOpT< HostArr< double >, HostArr< Index > > hostFvOp(const FvAssembled &A) const
View an FvAssembled as the backend-agnostic FvCsrOpT (c0=0,cD=1 ⇒ pure FV Laplacian).
typename Octree::Code Code
const Octree & octree() const
void setImmersedWall(bool w)
Immersed no-slip (Dirichlet) wall mode.
typename Octree::Coord Coord
double coarseStar(const std::vector< double > &u, Index coarse, Index fine, int axis) const
Quadratic coarse-fine value: the coarse leaf coarse's field, evaluated by tangential quadratic interp...
void setOrigin(const Vec< Dim > &o)
std::function< Index(const std::array< long, Dim > &)> ExtResolver
Route probes that exit the block through an external resolver (the LeafHalo registry): fn(blockLocalP...
Real cellWidth(Index i) const
The cell width when the octree is CUBIC.
BlockOctree< Dim, Bits > Octree
void init(const Octree &t, Real h0)
Cubic-cell overload — the pre-Phase-3 spelling, and exactly the per-axis one with all three spacings ...
FvAssembled assembleFv() const
void setOpennessRaw(std::vector< double > a)
void applyLaplacian(const std::vector< double > &u, std::vector< double > &out) const
out = L u (periodic FV Laplacian).
Real h0Scalar() const
The single spacing of a CUBIC octree (h0()[0]); every anisotropic path uses h0().
void gaussSeidel(std::vector< double > &u, const std::vector< double > &rhs, int sweeps) const
sweeps lexicographic Gauss-Seidel relaxations of L u = rhs (in place).
const std::vector< double > & opennessRaw() const
AmrPoisson(const Octree &t, Real h0)
AmrPoisson(const Octree &t, const Vec< Dim > &h0)
void setResolver(ExtResolver r)
std::array< long, Dim > loOf(Index slot) const
Block-local lo corner of an extended slot as longs (ghosts may lie outside the block).
Real cellVolume(Index i) const
void setPeriodic(bool p)
Boundary condition: periodic (default) wraps every face; non-periodic treats a domain-boundary face a...
morton::Morton< Dim, Bits > M
unsigned level(Index i) const
Index find(Code p) const
Leaf containing Morton code p, or -1. Host wrapper over amrLocate.
std::array< std::array< Coord, Dim >, 2 > bounds(Index i) const
Inclusive integer bounds [lo, hi] of leaf i in fine units.
typename M::coord_type Coord
typename M::code_type Code
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,...
The assembled FV (weight-CSR) operator: per-face conductance w = A_f/d_f·openness,...
std::vector< Index > nbr
neighbour leaf per face, size nFaces
std::vector< Index > start
CSR row offsets, size n+1.
std::vector< double > bcDiag
Dirichlet boundary diagonal per cell (0 if periodic)
std::vector< double > coef
w = A_f/d_f·openness per face, size nFaces
std::vector< double > invVol
1/V_i, size n