25#ifndef PECLET_CORE_AMR_POISSON_HPP
26#define PECLET_CORE_AMR_POISSON_HPP
28#ifdef PECLET_CORE_HAVE_MORTON
43template <
int Dim,
unsigned Bits = (Dim == 2 ? 32u : (Dim == 3 ? 21u : 16u))>
59 extResolve_ =
nullptr;
63 for (
int d = 0; d < Dim; ++d)
64 fineExt_[d] =
static_cast<Coord>(
t.brick()[d] * (
Index(1) <<
t.lmax()));
82 void setGhosts(std::vector<std::array<long, Dim>>
lo, std::vector<unsigned>
lv) {
83 ghostLo_ = std::move(
lo);
84 ghostLv_ = std::move(
lv);
97 : ghostLv_[
static_cast<std::size_t
>(slot -
numLeaves())];
101 std::array<long, Dim>
lo{};
103 auto b = t_->
bounds(slot);
104 for (
int d = 0; d < Dim; ++d)
105 lo[d] =
static_cast<long>(b[0][d]);
107 lo = ghostLo_[
static_cast<std::size_t
>(slot -
numLeaves())];
117 std::pair<Index, unsigned>
probeSlot(
const std::array<long, Dim>& p)
const {
119 for (
int d = 0; d < Dim; ++d)
120 if (p[d] < 0 || p[d] >=
static_cast<long>(fineExt_[d])) {
124 std::array<Coord, Dim>
q{};
127 const Index s = extResolve_(p);
130 for (
int d = 0; d < Dim; ++d) {
131 const long e =
static_cast<long>(fineExt_[d]);
132 q[d] =
static_cast<Coord>(((p[d] %
e) +
e) %
e);
135 for (
int d = 0; d < Dim; ++d)
136 q[d] =
static_cast<Coord>(p[d]);
144 static int faceIndex(
int axis,
int dir) {
return 2 * axis + (dir > 0 ? 0 : 1); }
156 alpha_ = std::move(
a);
163 template <
class OpenFn>
167 alpha_.assign(
static_cast<std::size_t
>(n +
ng) *
kFaces, 1.0);
173 for (
int axis = 0; axis < Dim; ++axis)
174 for (
int dir = -1; dir <= 1; dir += 2) {
175 const long plane = (dir > 0) ?
lo[axis] + frameShift_[axis] +
s :
lo[axis] + frameShift_[axis];
177 for (
int d = 0; d < Dim; ++d)
179 ? origin_[d] +
static_cast<Real>(
plane) * h0_
180 : origin_[d] + (
static_cast<Real>(
lo[d] + frameShift_[d]) +
181 0.5 *
static_cast<Real>(
s)) *
183 double a =
static_cast<double>(
openFn(
fc, axis));
184 a =
a < 0.0 ? 0.0 : (
a > 1.0 ? 1.0 :
a);
190 std::array<long, Dim>
lo{};
191 for (
int d = 0; d < Dim; ++d)
192 lo[d] =
static_cast<long>(b[0][d]);
196 fillRow(n + g, ghostLo_[
static_cast<std::size_t
>(g)],
197 1L << ghostLv_[
static_cast<std::size_t
>(g)]);
224 if (periodic_ && !immersedWall_)
227 const auto&
lo = b[0];
229 const double wall = areaOf(
si) / (0.5 *
static_cast<Real>(
si) * h0_);
231 for (
int axis = 0; axis < Dim; ++axis)
232 for (
int dir = -1; dir <= 1; dir += 2) {
233 const long pc = (dir > 0) ?
static_cast<long>(
lo[axis]) +
static_cast<long>(
si)
234 :
static_cast<long>(
lo[axis]) - 1;
236 !periodic_ && (
pc < 0 || pc >=
static_cast<long>(fineExt_[axis]));
239 else if (immersedWall_)
249 for (
int d = 0; d < Dim; ++d)
260 const auto&
lo = b[0];
263 for (
int axis = 0; axis < Dim; ++axis)
264 for (
int dir = -1; dir <= 1; dir += 2) {
265 const long pc = (dir > 0) ?
static_cast<long>(
lo[axis]) +
static_cast<long>(
si)
266 :
static_cast<long>(
lo[axis]) - 1;
269 if (!periodic_ && (
pc < 0 || pc >=
static_cast<long>(fineExt_[axis])))
271 std::array<long, Dim> p{};
272 for (
int d = 0; d < Dim; ++d)
273 p[d] =
static_cast<long>(
lo[d]);
285 const int nsub = 1 << (Dim - 1);
286 for (
int k = 0; k <
nsub; ++k) {
287 std::array<long, Dim>
q = p;
289 for (
int t = 0;
t < Dim; ++
t) {
292 q[
t] =
static_cast<long>(
lo[
t]) + (((k >>
bit) & 1) ?
static_cast<long>(
sj) : 0
L);
315 const auto&
lo = b[0];
318 for (
int axis = 0; axis < Dim; ++axis)
319 for (
int dir = -1; dir <= 1; dir += 2) {
320 const long pc = (dir > 0) ?
static_cast<long>(
lo[axis]) +
static_cast<long>(
si)
321 :
static_cast<long>(
lo[axis]) - 1;
322 std::array<long, Dim> p{};
323 for (
int d = 0; d < Dim; ++d)
324 p[d] =
static_cast<long>(
lo[d]);
331 fn(
j, axis, dir, areaOf(
si), 0.5 * (
static_cast<Real>(
si) +
static_cast<Real>(
sj)) * h0_,
335 const int nsub = 1 << (Dim - 1);
336 for (
int k = 0; k <
nsub; ++k) {
337 std::array<long, Dim>
q = p;
339 for (
int t = 0;
t < Dim; ++
t) {
342 q[
t] =
static_cast<long>(
lo[
t]) + (((k >>
bit) & 1) ?
static_cast<long>(
sj) : 0
L);
348 fn(
jj, axis, dir, areaOf(
sj),
349 0.5 * (
static_cast<Real>(
si) +
static_cast<Real>(
sj)) * h0_,
359 std::array<long, Dim> p =
loOf(
i);
361 p[axis] = (dir > 0) ? p[axis] +
si : p[axis] - 1;
373 const double uc =
u[
static_cast<std::size_t
>(
coarse)];
380 for (
int t = 0;
t < Dim; ++
t) {
383 const double dt = ((
static_cast<double>(
bf[
t]) + 0.5 *
sf) -
384 (
static_cast<double>(
bc[
t]) + 0.5 *
sc)) *
388 if (
cp < 0 ||
cm < 0)
397 const double up =
u[
static_cast<std::size_t
>(
cp)];
398 const double um =
u[
static_cast<std::size_t
>(
cm)];
399 const double Dt = (
up -
um) / (2.0 *
H);
409 out.assign(
static_cast<std::size_t
>(n), 0.0);
411 const double ui =
u[
static_cast<std::size_t
>(
i)];
416 double uj =
u[
static_cast<std::size_t
>(
j)];
430 std::vector<double>& res)
const {
431 std::vector<double>
lu;
434 res.assign(
static_cast<std::size_t
>(n), 0.0);
437 double r =
rhs[
static_cast<std::size_t
>(
i)] -
lu[
static_cast<std::size_t
>(
i)];
438 res[
static_cast<std::size_t
>(
i)] =
r;
458 A.
start.assign(
static_cast<std::size_t
>(n) + 1, 0);
462 A.
start[
static_cast<std::size_t
>(
i) + 1] = A.
start[
static_cast<std::size_t
>(
i)] +
cnt;
465 A.
nbr.resize(
static_cast<std::size_t
>(
nf));
466 A.
coef.resize(
static_cast<std::size_t
>(
nf));
467 A.
invVol.resize(
static_cast<std::size_t
>(n));
468 A.
bcDiag.resize(
static_cast<std::size_t
>(n));
474 A.
nbr[
static_cast<std::size_t
>(k)] =
j;
475 A.
coef[
static_cast<std::size_t
>(k)] =
a *
c;
499 out.assign(
static_cast<std::size_t
>(n), 0.0);
508 out.assign(
static_cast<std::size_t
>(n), 0.0);
510 const double ui =
u[
static_cast<std::size_t
>(
i)];
513 acc +=
a *
c * (
u[
static_cast<std::size_t
>(
j)] -
ui);
520 double residual(
const std::vector<double>&
u,
const std::vector<double>&
rhs,
521 std::vector<double>& res)
const {
523 res.assign(
static_cast<std::size_t
>(n), 0.0);
526 const double ui =
u[
static_cast<std::size_t
>(
i)];
529 acc +=
a *
c * (
u[
static_cast<std::size_t
>(
j)] -
ui);
532 res[
static_cast<std::size_t
>(
i)] =
r;
543 double sumOff = 0.0, diag = 0.0;
545 sumOff +=
a *
c *
u[
static_cast<std::size_t
>(
j)];
549 u[
static_cast<std::size_t
>(
i)] =
557 double sum = 0.0,
vol = 0.0;
562 double m = sum /
vol;
564 u[
static_cast<std::size_t
>(
i)] -= m;
571 const std::array<Coord, Dim>&
fineExt()
const {
return fineExt_; }
575 Coord wrap(
long c,
int axis)
const {
576 long e =
static_cast<long>(fineExt_[axis]);
577 return static_cast<Coord>(((
c %
e) +
e) %
e);
582 return areaOf(
si <
sj ?
si :
sj) / dist;
587 for (
int d = 0; d < Dim - 1; ++d)
592 const Octree* t_ =
nullptr;
594 std::array<Coord, Dim> fineExt_{};
597 std::vector<std::array<long, Dim>> ghostLo_;
598 std::vector<unsigned> ghostLv_;
599 std::array<long, Dim> frameShift_{};
600 std::vector<double> alpha_;
601 bool hasOpen_ =
false;
602 bool periodic_ =
true;
603 bool immersedWall_ =
false;
607template <
int Dim,
unsigned Bits = (Dim == 2 ? 32u : (Dim == 3 ? 21u : 16u))>
618 levels_.push_back(
finest);
622 if (
merged == 0 ||
c.numLeaves() == levels_.back().numLeaves())
624 levels_.push_back(
c);
625 if (
c.numLeaves() == 1)
628 ops_.resize(levels_.size());
631 for (std::size_t
L = 0;
L < levels_.size(); ++
L)
632 ops_[
L].init(levels_[
L], h0);
634 c2p_.assign(levels_.size() ? levels_.size() - 1 : 0, {});
635 for (std::size_t
L = 0;
L + 1 < levels_.size(); ++
L) {
638 c2p_[
L].resize(
static_cast<std::size_t
>(
f.numLeaves()));
639 for (
Index i = 0;
i <
f.numLeaves(); ++
i) {
642 c2p_[
L][
static_cast<std::size_t
>(
i)] =
c.find(
f.code(
i));
647 std::size_t
numLevels()
const {
return levels_.size(); }
662 o.setImmersedWall(w);
666 void vcycle(std::size_t
L, std::vector<double>&
u,
const std::vector<double>&
rhs,
int pre = 2,
668 if (
L + 1 == levels_.size()) {
669 ops_[
L].gaussSeidel(
u,
rhs, 40);
670 ops_[
L].removeMean(
u);
673 ops_[
L].gaussSeidel(
u,
rhs, pre);
674 std::vector<double> res;
675 ops_[
L].residual(
u,
rhs, res);
680 std::vector<double>
crhs(
static_cast<std::size_t
>(
c.numLeaves()), 0.0);
681 std::vector<double>
cvol(
static_cast<std::size_t
>(
c.numLeaves()), 0.0);
682 for (
Index i = 0;
i <
f.numLeaves(); ++
i) {
683 Index p = c2p_[
L][
static_cast<std::size_t
>(
i)];
686 Real v = ops_[
L].cellVolume(
i);
687 crhs[
static_cast<std::size_t
>(p)] +=
v * res[
static_cast<std::size_t
>(
i)];
688 cvol[
static_cast<std::size_t
>(p)] +=
v;
690 for (
Index p = 0; p <
c.numLeaves(); ++p)
691 if (
cvol[
static_cast<std::size_t
>(p)] > 0)
692 crhs[
static_cast<std::size_t
>(p)] /=
cvol[
static_cast<std::size_t
>(p)];
694 std::vector<double>
ccorr(
static_cast<std::size_t
>(
c.numLeaves()), 0.0);
698 for (
Index i = 0;
i <
f.numLeaves(); ++
i) {
699 Index p = c2p_[
L][
static_cast<std::size_t
>(
i)];
701 u[
static_cast<std::size_t
>(
i)] +=
ccorr[
static_cast<std::size_t
>(p)];
703 ops_[
L].gaussSeidel(
u,
rhs, post);
704 ops_[
L].removeMean(
u);
714 const std::size_t n =
u.size();
715 std::vector<double>
lq,
ls,
rhsp(n), res;
718 P.applyLaplacianQuad(
u,
lq);
719 P.applyLaplacian(
u,
ls);
720 for (std::size_t
i = 0;
i < n; ++
i)
724 r =
P.residualQuad(
u,
rhs, res);
735 template <
class OpenFn>
739 ops_[0].buildOpenness(
openFn);
740 for (std::size_t
L = 0;
L + 1 < levels_.size(); ++
L)
745 static int faceIdx(
int axis,
int dir) {
return 2 * axis + (dir > 0 ? 0 : 1); }
749 void coarsenOpenness(std::size_t
L) {
752 const int F = 2 * Dim;
753 std::vector<double>
ca(
static_cast<std::size_t
>(
c.numLeaves()) *
F, 0.0);
754 std::vector<int>
cnt(
static_cast<std::size_t
>(
c.numLeaves()) *
F, 0);
755 for (
Index i = 0;
i <
f.numLeaves(); ++
i) {
756 Index p = c2p_[
L][
static_cast<std::size_t
>(
i)];
759 const std::size_t base =
static_cast<std::size_t
>(p) *
F;
760 if (
c.level(p) ==
f.level(
i)) {
762 for (
int axis = 0; axis < Dim; ++axis)
763 for (
int dir = -1; dir <= 1; dir += 2) {
764 int fi = faceIdx(axis, dir);
765 ca[base +
fi] += ops_[
L].faceOpenness(
i, axis, dir);
770 unsigned oct = M::from_code(
f.code(
i)).child_index(
f.level(
i));
771 for (
int axis = 0; axis < Dim; ++axis) {
772 int dir = ((
oct >> axis) & 1) ? +1 : -1;
773 int fi = faceIdx(axis, dir);
774 ca[base +
fi] += ops_[
L].faceOpenness(
i, axis, dir);
779 for (std::size_t k = 0; k <
ca.size(); ++k)
781 ops_[
L + 1].setOpennessRaw(std::move(
ca));
784 std::vector<Octree> levels_;
785 std::vector<AmrPoisson<Dim, Bits>> ops_;
786 std::vector<std::vector<Index>> c2p_;
Geometric multigrid for AmrPoisson over a uniformly-coarsened octree hierarchy.
void setPeriodic(bool p)
Apply a boundary condition to every level (periodic default, or non-periodic homogeneous Dirichlet).
void build(const Octree &finest, Real h0)
Build the hierarchy from a finest octree by uniform coarsening until a single leaf remains (or no ful...
const AmrPoisson< Dim, Bits > & op(std::size_t L=0) const
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).
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...
typename Octree::Code Code
void setOpenness(OpenFn &&openFn)
Set cut-cell face openness on the finest level from a geometry callable openFn(faceCentreWorld,...
BlockOctree< Dim, Bits > Octree
void setImmersedWall(bool w)
Enable the immersed no-slip (Dirichlet) wall on every level — the velocity operator.
std::size_t numLevels() const
Cell-centered FV Poisson operator on one (periodic) block octree.
const std::array< Coord, Dim > & fineExt() const
Per-axis fine-grid extent (brick·2^lmax) — the periodic wrap modulus.
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...
std::array< long, Dim > loOf(Index slot) const
Block-local lo corner of an extended slot as longs (ghosts may lie outside the block).
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).
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 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...
const Vec< Dim > & origin() const
const Octree & octree() const
std::function< Index(const std::array< long, Dim > &)> ExtResolver
Route probes that exit the block through an external resolver (the LeafHalo registry): fn(blockLocalP...
const std::vector< double > & opennessRaw() const
Real cellWidth(Index i) const
void setFrameShift(const std::array< long, Dim > &s)
Distributed frame shift: this block's global fine origin.
void init(const Octree &t, Real h0)
AmrPoisson(const Octree &t, Real h0)
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.
typename Octree::Coord Coord
static int faceIndex(int axis, int dir)
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...
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).
void setImmersedWall(bool w)
Immersed no-slip (Dirichlet) wall mode.
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).
void setOpennessRaw(std::vector< double > a)
void setOrigin(const Vec< Dim > &o)
double boundaryDiag(Index i) const
Σ over leaf i's Dirichlet-wall faces of the wall weight A_f/(½·cellWidth), folded into the operator d...
Index periodicNeighbor(Index i, int axis, int dir) const
Periodic face neighbour leaf (covering the cell just across the face).
unsigned levelOf(Index slot) const
Octree level of an extended slot (local leaf or declared ghost).
static constexpr int kFaces
void setResolver(ExtResolver r)
void setPeriodic(bool p)
Boundary condition: periodic (default) wraps every face; non-periodic treats a domain-boundary face a...
void removeMean(std::vector< double > &u) const
Subtract the volume-weighted mean (fixes the periodic null space).
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).
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)).
void applyLaplacian(const std::vector< double > &u, std::vector< double > &out) const
out = L u (periodic FV Laplacian).
void forEachFaceFull(Index i, Fn &&fn) const
Like forEachFaceNeighbor but exposes geometry for a consistent FV divergence/gradient: fn(neighbour,...
typename Octree::Code Code
double residualQuad(const std::vector< double > &u, const std::vector< double > &rhs, std::vector< double > &res) const
L2 norm of rhs - L_quad u.
void buildOpenness(OpenFn &&openFn)
Build face openness from a geometry callable openFn(faceCentreWorld, axis) -> [0,1] (1 = fully fluid,...
FvAssembled assembleFv() const
bool immersedWall() const
BlockOctree< Dim, Bits > Octree
Real cellVolume(Index i) const
morton::Morton< Dim, Bits > M
typename M::coord_type Coord
unsigned level(Index i) const
Index find(Code p) const
Leaf containing Morton code p, or -1. Host wrapper over amrLocate.
typename M::code_type Code
std::array< std::array< Coord, Dim >, 2 > bounds(Index i) const
Inclusive integer bounds [lo, hi] of leaf i in fine units.
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,...
MORTON_HD double fvApplyRow(const Op &op, Index i, const U &u)
(H u)_i = c0·u_i + cD·( invVol_i·( Σ w·(u_nbr − u_i) − bcDiag_i·u_i ) ).
std::array< Real, Dim > Vec
Multi-dimensional real vector.
double Real
Default host floating type. Device kernels may use float; conversions happen at the boundary.
std::int64_t Index
Signed index type for grids and particles (supersedes block_decomposer's long int IndxT).
The assembled FV (weight-CSR) operator: per-face conductance w = A_f/d_f·openness,...
std::vector< double > invVol
1/V_i, size n
std::vector< double > coef
w = A_f/d_f·openness per face, size nFaces
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)
A backend-agnostic view of an assembled FV (weight-CSR) operator.
A uniform accessor over a raw host array, giving it the operator()(i) that Kokkos::View has,...