25#ifndef PECLET_CORE_AMR_POISSON_HPP
26#define PECLET_CORE_AMR_POISSON_HPP
28#ifdef PECLET_CORE_HAVE_MORTON
42template <
int Dim,
unsigned Bits = (Dim == 2 ? 32u : (Dim == 3 ? 21u : 16u))>
58 for (
int d = 0; d < Dim; ++d)
59 fineExt_[d] =
static_cast<Coord>(
t.brick()[d] * (
Index(1) <<
t.lmax()));
65 static constexpr int kFaces = 2 * Dim;
66 static int faceIndex(
int axis,
int dir) {
return 2 * axis + (dir > 0 ? 0 : 1); }
76 const std::vector<double>&
opennessRaw()
const {
return alpha_; }
78 alpha_ = std::move(
a);
85 template <
class OpenFn>
88 alpha_.assign(
static_cast<std::size_t
>(n) *
kFaces, 1.0);
92 const auto&
lo = b[0];
94 for (
int axis = 0; axis < Dim; ++axis)
95 for (
int dir = -1; dir <= 1; dir += 2) {
96 const long plane = (dir > 0) ?
static_cast<long>(
lo[axis]) +
static_cast<long>(
s)
97 :
static_cast<long>(
lo[axis]);
99 for (
int d = 0; d < Dim; ++d)
100 fc[d] = (d == axis) ? origin_[d] +
static_cast<Real>(
plane) * h0_
102 (
static_cast<Real>(
lo[d]) + 0.5 *
static_cast<Real>(
s)) * h0_;
103 double a =
static_cast<double>(
openFn(
fc, axis));
104 a =
a < 0.0 ? 0.0 : (
a > 1.0 ? 1.0 :
a);
134 if (periodic_ && !immersedWall_)
137 const auto&
lo = b[0];
139 const double wall = areaOf(
si) / (0.5 *
static_cast<Real>(
si) * h0_);
141 for (
int axis = 0; axis < Dim; ++axis)
142 for (
int dir = -1; dir <= 1; dir += 2) {
143 const long pc = (dir > 0) ?
static_cast<long>(
lo[axis]) +
static_cast<long>(
si)
144 :
static_cast<long>(
lo[axis]) - 1;
146 !periodic_ && (
pc < 0 || pc >=
static_cast<long>(fineExt_[axis]));
149 else if (immersedWall_)
159 for (
int d = 0; d < Dim; ++d)
170 const auto&
lo = b[0];
173 for (
int axis = 0; axis < Dim; ++axis)
174 for (
int dir = -1; dir <= 1; dir += 2) {
175 const long pc = (dir > 0) ?
static_cast<long>(
lo[axis]) +
static_cast<long>(
si)
176 :
static_cast<long>(
lo[axis]) - 1;
179 if (!periodic_ && (
pc < 0 || pc >=
static_cast<long>(fineExt_[axis])))
181 std::array<Coord, Dim> p =
lo;
182 p[axis] = wrap(
pc, axis);
192 const int nsub = 1 << (Dim - 1);
193 for (
int k = 0; k <
nsub; ++k) {
194 std::array<Coord, Dim>
q =
lo;
195 q[axis] = wrap(
pc, axis);
197 for (
int t = 0;
t < Dim; ++
t) {
201 q[
t] = wrap(
static_cast<long>(
lo[
t]) +
static_cast<long>(off),
t);
222 const auto&
lo = b[0];
225 for (
int axis = 0; axis < Dim; ++axis)
226 for (
int dir = -1; dir <= 1; dir += 2) {
227 const long pc = (dir > 0) ?
static_cast<long>(
lo[axis]) +
static_cast<long>(
si)
228 :
static_cast<long>(
lo[axis]) - 1;
229 std::array<Coord, Dim> p =
lo;
230 p[axis] = wrap(
pc, axis);
235 fn(
j, axis, dir, areaOf(
si), 0.5 * (
static_cast<Real>(
si) +
static_cast<Real>(
sj)) * h0_,
239 const int nsub = 1 << (Dim - 1);
240 for (
int k = 0; k <
nsub; ++k) {
241 std::array<Coord, Dim>
q =
lo;
242 q[axis] = wrap(
pc, axis);
244 for (
int t = 0;
t < Dim; ++
t) {
248 q[
t] = wrap(
static_cast<long>(
lo[
t]) +
static_cast<long>(off),
t);
252 fn(
jj, axis, dir, areaOf(
sj),
253 0.5 * (
static_cast<Real>(
si) +
static_cast<Real>(
sj)) * h0_,
263 const auto&
lo = b[0];
265 const long pc = (dir > 0) ?
static_cast<long>(
lo[axis]) +
static_cast<long>(
si)
266 :
static_cast<long>(
lo[axis]) - 1;
267 std::array<Coord, Dim> p =
lo;
268 p[axis] = wrap(
pc, axis);
269 return t_->
find(M::encode(p).code());
280 const double uc =
u[
static_cast<std::size_t
>(
coarse)];
287 for (
int t = 0;
t < Dim; ++
t) {
290 const double dt = ((
static_cast<double>(
bf[0][
t]) + 0.5 *
sf) -
291 (
static_cast<double>(
bc[0][
t]) + 0.5 *
sc)) *
295 if (
cp < 0 ||
cm < 0)
304 const double up =
u[
static_cast<std::size_t
>(
cp)];
305 const double um =
u[
static_cast<std::size_t
>(
cm)];
306 const double Dt = (
up -
um) / (2.0 *
H);
316 out.assign(
static_cast<std::size_t
>(n), 0.0);
318 const double ui =
u[
static_cast<std::size_t
>(
i)];
323 double uj =
u[
static_cast<std::size_t
>(
j)];
337 std::vector<double>& res)
const {
338 std::vector<double>
lu;
341 res.assign(
static_cast<std::size_t
>(n), 0.0);
344 double r =
rhs[
static_cast<std::size_t
>(
i)] -
lu[
static_cast<std::size_t
>(
i)];
345 res[
static_cast<std::size_t
>(
i)] =
r;
365 A.
start.assign(
static_cast<std::size_t
>(n) + 1, 0);
369 A.
start[
static_cast<std::size_t
>(
i) + 1] = A.
start[
static_cast<std::size_t
>(
i)] +
cnt;
372 A.
nbr.resize(
static_cast<std::size_t
>(
nf));
373 A.
coef.resize(
static_cast<std::size_t
>(
nf));
374 A.
invVol.resize(
static_cast<std::size_t
>(n));
375 A.
bcDiag.resize(
static_cast<std::size_t
>(n));
381 A.
nbr[
static_cast<std::size_t
>(k)] =
j;
382 A.
coef[
static_cast<std::size_t
>(k)] =
a *
c;
406 out.assign(
static_cast<std::size_t
>(n), 0.0);
415 out.assign(
static_cast<std::size_t
>(n), 0.0);
417 const double ui =
u[
static_cast<std::size_t
>(
i)];
420 acc +=
a *
c * (
u[
static_cast<std::size_t
>(
j)] -
ui);
427 double residual(
const std::vector<double>&
u,
const std::vector<double>&
rhs,
428 std::vector<double>& res)
const {
430 res.assign(
static_cast<std::size_t
>(n), 0.0);
433 const double ui =
u[
static_cast<std::size_t
>(
i)];
436 acc +=
a *
c * (
u[
static_cast<std::size_t
>(
j)] -
ui);
439 res[
static_cast<std::size_t
>(
i)] =
r;
450 double sumOff = 0.0, diag = 0.0;
452 sumOff +=
a *
c *
u[
static_cast<std::size_t
>(
j)];
456 u[
static_cast<std::size_t
>(
i)] =
464 double sum = 0.0,
vol = 0.0;
469 double m = sum /
vol;
471 u[
static_cast<std::size_t
>(
i)] -= m;
478 const std::array<Coord, Dim>&
fineExt()
const {
return fineExt_; }
482 Coord wrap(
long c,
int axis)
const {
483 long e =
static_cast<long>(fineExt_[axis]);
484 return static_cast<Coord>(((
c %
e) +
e) %
e);
489 return areaOf(
si <
sj ?
si :
sj) / dist;
494 for (
int d = 0; d < Dim - 1; ++d)
499 const Octree* t_ =
nullptr;
501 std::array<Coord, Dim> fineExt_{};
503 std::vector<double> alpha_;
504 bool hasOpen_ =
false;
505 bool periodic_ =
true;
506 bool immersedWall_ =
false;
510template <
int Dim,
unsigned Bits = (Dim == 2 ? 32u : (Dim == 3 ? 21u : 16u))>
521 levels_.push_back(
finest);
525 if (
merged == 0 ||
c.numLeaves() == levels_.back().numLeaves())
527 levels_.push_back(
c);
528 if (
c.numLeaves() == 1)
531 ops_.resize(levels_.size());
534 for (std::size_t
L = 0;
L < levels_.size(); ++
L)
535 ops_[
L].init(levels_[
L], h0);
537 c2p_.assign(levels_.size() ? levels_.size() - 1 : 0, {});
538 for (std::size_t
L = 0;
L + 1 < levels_.size(); ++
L) {
541 c2p_[
L].resize(
static_cast<std::size_t
>(
f.numLeaves()));
542 for (
Index i = 0;
i <
f.numLeaves(); ++
i) {
543 Code parent = M::from_code(
f.code(
i)).ancestor(
f.level(
i) + 1).code();
544 c2p_[
L][
static_cast<std::size_t
>(
i)] =
c.find(
parent);
549 std::size_t
numLevels()
const {
return levels_.size(); }
564 o.setImmersedWall(
w);
568 void vcycle(std::size_t
L, std::vector<double>&
u,
const std::vector<double>&
rhs,
int pre = 2,
570 if (
L + 1 == levels_.size()) {
571 ops_[
L].gaussSeidel(
u,
rhs, 40);
572 ops_[
L].removeMean(
u);
575 ops_[
L].gaussSeidel(
u,
rhs, pre);
576 std::vector<double> res;
577 ops_[
L].residual(
u,
rhs, res);
582 std::vector<double>
crhs(
static_cast<std::size_t
>(
c.numLeaves()), 0.0);
583 std::vector<double>
cvol(
static_cast<std::size_t
>(
c.numLeaves()), 0.0);
584 for (
Index i = 0;
i <
f.numLeaves(); ++
i) {
585 Index p = c2p_[
L][
static_cast<std::size_t
>(
i)];
588 Real v = ops_[
L].cellVolume(
i);
589 crhs[
static_cast<std::size_t
>(p)] +=
v * res[
static_cast<std::size_t
>(
i)];
590 cvol[
static_cast<std::size_t
>(p)] +=
v;
592 for (
Index p = 0; p <
c.numLeaves(); ++p)
593 if (
cvol[
static_cast<std::size_t
>(p)] > 0)
594 crhs[
static_cast<std::size_t
>(p)] /=
cvol[
static_cast<std::size_t
>(p)];
596 std::vector<double>
ccorr(
static_cast<std::size_t
>(
c.numLeaves()), 0.0);
600 for (
Index i = 0;
i <
f.numLeaves(); ++
i) {
601 Index p = c2p_[
L][
static_cast<std::size_t
>(
i)];
603 u[
static_cast<std::size_t
>(
i)] +=
ccorr[
static_cast<std::size_t
>(p)];
605 ops_[
L].gaussSeidel(
u,
rhs, post);
606 ops_[
L].removeMean(
u);
616 const std::size_t n =
u.size();
617 std::vector<double>
lq,
ls,
rhsp(n), res;
620 P.applyLaplacianQuad(
u,
lq);
621 P.applyLaplacian(
u,
ls);
622 for (std::size_t
i = 0;
i < n; ++
i)
626 r =
P.residualQuad(
u,
rhs, res);
637 template <
class OpenFn>
641 ops_[0].buildOpenness(
openFn);
642 for (std::size_t
L = 0;
L + 1 < levels_.size(); ++
L)
647 static int faceIdx(
int axis,
int dir) {
return 2 * axis + (dir > 0 ? 0 : 1); }
651 void coarsenOpenness(std::size_t
L) {
654 const int F = 2 * Dim;
655 std::vector<double>
ca(
static_cast<std::size_t
>(
c.numLeaves()) *
F, 0.0);
656 std::vector<int>
cnt(
static_cast<std::size_t
>(
c.numLeaves()) *
F, 0);
657 for (
Index i = 0;
i <
f.numLeaves(); ++
i) {
658 Index p = c2p_[
L][
static_cast<std::size_t
>(
i)];
661 const std::size_t
base =
static_cast<std::size_t
>(p) *
F;
662 if (
c.level(p) ==
f.level(
i)) {
664 for (
int axis = 0; axis < Dim; ++axis)
665 for (
int dir = -1; dir <= 1; dir += 2) {
666 int fi = faceIdx(axis, dir);
667 ca[
base +
fi] += ops_[
L].faceOpenness(
i, axis, dir);
672 unsigned oct = M::from_code(
f.code(
i)).child_index(
f.level(
i));
673 for (
int axis = 0; axis < Dim; ++axis) {
674 int dir = ((
oct >> axis) & 1) ? +1 : -1;
675 int fi = faceIdx(axis, dir);
676 ca[
base +
fi] += ops_[
L].faceOpenness(
i, axis, dir);
681 for (std::size_t k = 0; k <
ca.size(); ++k)
683 ops_[
L + 1].setOpennessRaw(std::move(
ca));
686 std::vector<Octree> levels_;
687 std::vector<AmrPoisson<Dim, Bits>> ops_;
688 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.
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
const std::vector< double > & opennessRaw() const
Real cellWidth(Index i) const
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).
static constexpr int kFaces
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).
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.
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 ) ).
Kokkos::View< T *, MemSpace > View
1D device array.
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)