26#ifndef PECLET_CORE_AMR_DISTRIBUTED_FV_HPP
27#define PECLET_CORE_AMR_DISTRIBUTED_FV_HPP
29#ifdef PECLET_CORE_HAVE_MORTON
44template <
int Dim,
unsigned Bits = (Dim == 2 ? 32u : (Dim == 3 ? 21u : 16u))>
63 template <
class OpenFn>
71 invVol_.assign(
static_cast<std::size_t
>(n), 0.0);
72 diag_.assign(
static_cast<std::size_t
>(n), 0.0);
79 for (
int d2 = 0;
d2 < Dim; ++
d2)
83 :
org[
d2] + (
static_cast<double>(
flo[
d2]) + 0.5 *
static_cast<double>(
s)) * h0_;
85 return a < 0.0 ? 0.0 : (
a > 1.0 ? 1.0 :
a);
87 auto gLo = [&](
const std::array<Coord, Dim>&
lc) {
88 std::array<long, Dim> g{};
89 for (
int d2 = 0;
d2 < Dim; ++
d2)
90 g[
d2] =
static_cast<long>(
lc[
d2]) +
static_cast<long>(
bfo[
d2]);
98 std::array<long, Dim>
flo{};
99 flo[axis] = (dir > 0) ?
clo[axis] +
static_cast<long>(
si) :
clo[axis] -
static_cast<long>(
sj);
101 for (
int t = 0;
t < Dim; ++
t) {
104 flo[
t] =
clo[
t] + (((k >>
bit) & 1) ?
static_cast<long>(
sj) : 0
L);
107 for (
int d2 = 0;
d2 < Dim; ++
d2) {
123 std::vector<std::vector<E>>
ent(
static_cast<std::size_t
>(n));
125 std::array<Coord, Dim> gc{};
130 std::array<long, Dim>
iLo{};
132 std::vector<RFace>
rfaces;
135 const int Li =
static_cast<int>(
t.level(
i));
137 invVol_[
static_cast<std::size_t
>(
i)] = 1.0 / cellVol(
Li);
138 auto b =
t.bounds(
i);
139 const auto&
lo = b[0];
140 for (
int axis = 0; axis < Dim; ++axis)
141 for (
int dir = -1; dir <= 1; dir += 2) {
149 const int Lj =
static_cast<int>(
t.level(
j));
156 const long pc = (dir > 0) ?
static_cast<long>(
lo[axis]) +
static_cast<long>(
si)
157 :
static_cast<long>(
lo[axis]) - 1;
158 const int nsub = 1 << (Dim - 1);
159 for (
int k = 0; k <
nsub; ++k) {
160 std::array<Coord, Dim>
q =
lo;
163 for (
int tt = 0;
tt < Dim; ++
tt) {
178 e.rfId =
static_cast<int>(
rfaces.size());
189 for (std::size_t
r = 0;
r <
rfaces.size(); ++
r)
196 auto ghostRef = [&](
const std::array<Coord, Dim>& gc) ->
Index {
206 start_.assign(
static_cast<std::size_t
>(n) + 1, 0);
208 for (
const E&
e :
ent[
static_cast<std::size_t
>(
i)]) {
210 ref_.push_back(
e.localRef);
212 diag_[
static_cast<std::size_t
>(
i)] +=
e.w;
215 const int Lj =
coverLv[
static_cast<std::size_t
>(
e.rfId)];
223 diag_[
static_cast<std::size_t
>(
i)] +=
ww;
226 const int nsub = 1 << (Dim - 1);
227 for (
int k = 0; k <
nsub; ++k) {
228 std::array<Coord, Dim>
sc = subCoord(
rf.gc,
rf.axis, k,
sj);
231 const double ww =
a * coeff(
rf.si,
sj);
234 diag_[
static_cast<std::size_t
>(
i)] +=
ww;
239 start_[
static_cast<std::size_t
>(
i) + 1] =
static_cast<Index>(ref_.size());
248 void apply(
const std::vector<double>&
u, std::vector<double>&
Lu)
const {
249 std::vector<double> g = d_->
coverValues(ghostCoords_,
u);
250 Lu.assign(
static_cast<std::size_t
>(n_), 0.0);
252 const double ui =
u[
static_cast<std::size_t
>(
i)];
254 for (
Index k = start_[
static_cast<std::size_t
>(
i)];
256 const Index r = ref_[
static_cast<std::size_t
>(k)];
259 acc += w_[
static_cast<std::size_t
>(k)] * (
val -
ui);
261 Lu[
static_cast<std::size_t
>(
i)] = invVol_[
static_cast<std::size_t
>(
i)] *
acc;
266 double residual(
const std::vector<double>&
u,
const std::vector<double>&
rhs,
267 std::vector<double>& res)
const {
268 std::vector<double>
lu;
270 res.assign(
static_cast<std::size_t
>(n_), 0.0);
273 double r =
rhs[
static_cast<std::size_t
>(
i)] -
lu[
static_cast<std::size_t
>(
i)];
274 res[
static_cast<std::size_t
>(
i)] =
r;
275 s +=
r *
r / invVol_[
static_cast<std::size_t
>(
i)];
283 std::vector<double> res;
291 double omega = 0.8)
const {
292 std::vector<double> tmp(
static_cast<std::size_t
>(n_));
294 std::vector<double> g = d_->
coverValues(ghostCoords_,
u);
297 for (
Index k = start_[
static_cast<std::size_t
>(
i)];
299 const Index r = ref_[
static_cast<std::size_t
>(k)];
302 sumOff += w_[
static_cast<std::size_t
>(k)] *
val;
304 const double Vrhs =
rhs[
static_cast<std::size_t
>(
i)] / invVol_[
static_cast<std::size_t
>(
i)];
305 tmp[
static_cast<std::size_t
>(
i)] =
306 (diag_[
static_cast<std::size_t
>(
i)] != 0.0)
307 ? (
sumOff -
Vrhs) / diag_[
static_cast<std::size_t
>(
i)]
308 :
u[
static_cast<std::size_t
>(
i)];
311 u[
static_cast<std::size_t
>(
i)] = (1.0 -
omega) *
u[
static_cast<std::size_t
>(
i)] +
312 omega * tmp[
static_cast<std::size_t
>(
i)];
317 double cellWidth(
int level)
const {
return h0_ *
static_cast<double>(
Index(1) << level); }
318 double cellVol(
int level)
const {
319 double w = cellWidth(level),
v = 1.0;
320 for (
int d = 0; d < Dim; ++d)
324 double areaOf(
Coord s)
const {
326 for (
int d = 0; d < Dim - 1; ++d)
327 a *=
static_cast<double>(
s) * h0_;
331 double dist = 0.5 * (
static_cast<double>(
si) +
static_cast<double>(
sj)) * h0_;
332 return areaOf(
si <
sj ?
si :
sj) / dist;
336 std::array<Coord, Dim> subCoord(
const std::array<Coord, Dim>& gc,
int axis,
int k,
338 std::array<Coord, Dim>
q = gc;
340 for (
int tt = 0;
tt < Dim; ++
tt) {
344 long v =
static_cast<long>(gc[
tt]) +
static_cast<long>(off);
357 std::vector<double> invVol_, diag_, w_;
358 std::vector<Index> start_, ref_;
359 std::vector<std::array<Coord, Dim>> ghostCoords_;
379template <
int Dim,
unsigned Bits = (Dim == 2 ? 32u : (Dim == 3 ? 21u : 16u))>
389 buildImpl(
finest, [](
const Vec<Dim>&,
int) {
return 1.0; },
false);
396 template <
class OpenFn>
402 template <
class OpenFn>
407 const unsigned lmax =
finest.lmax();
412 auto l0 = std::make_unique<Level>();
414 levels_.push_back(std::move(
l0));
416 BO coarse = levels_.back()->d.local();
418 coarse.coarsenIf([](
typename DO::Code,
unsigned) {
return true; });
421 auto lv = std::make_unique<Level>();
422 lv->d.init(g, lmax,
geo,
per, comm);
424 levels_.push_back(std::move(
lv));
436 int localLevels =
static_cast<int>(levels_.size());
439 while (
static_cast<int>(levels_.size()) <
globalLevels) {
440 auto lv = std::make_unique<Level>();
441 lv->d.init(g, lmax,
geo,
per, comm);
442 lv->d.local() = levels_.back()->d.local();
443 levels_.push_back(std::move(
lv));
446 for (
auto&
lv : levels_) {
448 const Index n =
lv->d.local().numLeaves();
449 lv->x.assign(
static_cast<std::size_t
>(n), 0.0);
450 lv->b.assign(
static_cast<std::size_t
>(n), 0.0);
451 lv->res.assign(
static_cast<std::size_t
>(n), 0.0);
454 for (std::size_t
L = 0;
L + 1 < levels_.size(); ++
L) {
455 const BO&
f = levels_[
L]->d.local();
456 const BO&
c = levels_[
L + 1]->d.local();
458 auto& c2p = levels_[
L]->c2p;
459 c2p.assign(
static_cast<std::size_t
>(
nf), -1);
461 c2p[
static_cast<std::size_t
>(
i)] =
c.find(
f.code(
i));
473 ig.h0 =
geo.h0 *
static_cast<double>(
Index(1) << lmax);
474 inner_ = std::make_unique<DistributedMultigrid<Dim, Bits>>();
475 inner_->build(g,
ig,
per, comm);
476 DO& coarsest = levels_.back()->d;
477 nCoarse_ = coarsest.local().numLeaves();
478 innerMap_.assign(
static_cast<std::size_t
>(nCoarse_), -1);
479 for (
Index i = 0;
i < nCoarse_; ++
i)
480 innerMap_[
static_cast<std::size_t
>(
i)] =
481 inner_->octree(0).findGlobalRoot(coarsest.globalRootOf(
i));
486 std::size_t
numLevels()
const {
return levels_.size(); }
493 void vcycle(std::vector<double>& x,
const std::vector<double>& b,
int pre = 2,
int post = 2,
495 auto&
lv = *levels_[
L];
496 if (
L + 1 == levels_.size()) {
500 lv.op.jacobi(x, b, bottomSweeps_,
omega);
503 lv.op.jacobi(x, b, pre,
omega);
504 std::vector<double> res;
505 lv.op.residual(x, b, res);
507 auto&
cl = *levels_[
L + 1];
508 const Index nc =
cl.d.local().numLeaves();
509 std::vector<double>
cb(
static_cast<std::size_t
>(nc), 0.0),
510 cn(
static_cast<std::size_t
>(nc), 0.0);
511 const auto& c2p =
lv.c2p;
512 const Index nf =
lv.d.local().numLeaves();
514 Index p = c2p[
static_cast<std::size_t
>(
i)];
517 cb[
static_cast<std::size_t
>(p)] += res[
static_cast<std::size_t
>(
i)];
518 cn[
static_cast<std::size_t
>(p)] += 1.0;
520 for (
Index p = 0; p < nc; ++p)
521 if (
cn[
static_cast<std::size_t
>(p)] > 0.0)
522 cb[
static_cast<std::size_t
>(p)] /=
cn[
static_cast<std::size_t
>(p)];
523 std::vector<double>
cx(
static_cast<std::size_t
>(nc), 0.0);
526 Index p = c2p[
static_cast<std::size_t
>(
i)];
528 x[
static_cast<std::size_t
>(
i)] +=
cx[
static_cast<std::size_t
>(p)];
530 lv.op.jacobi(x, b, post,
omega);
539 const std::vector<double>& b,
int cycles) {
540 std::vector<double> res;
542 const Index ni = inner_->numLeaves(0);
543 std::vector<double>
bi(
static_cast<std::size_t
>(
ni), 0.0),
544 ei(
static_cast<std::size_t
>(
ni), 0.0);
545 for (
Index i = 0;
i < nCoarse_; ++
i) {
546 Index m = innerMap_[
static_cast<std::size_t
>(
i)];
548 bi[
static_cast<std::size_t
>(m)] = res[
static_cast<std::size_t
>(
i)];
551 inner_->vcycle(
ei,
bi);
552 for (
Index i = 0;
i < nCoarse_; ++
i) {
553 Index m = innerMap_[
static_cast<std::size_t
>(
i)];
555 x[
static_cast<std::size_t
>(
i)] +=
ei[
static_cast<std::size_t
>(m)];
561 DistributedFvOperator<Dim, Bits> op;
562 std::vector<Index> c2p;
563 std::vector<double> x, b, res;
565 std::vector<std::unique_ptr<Level>> levels_;
566 std::unique_ptr<DistributedMultigrid<Dim, Bits>> inner_;
567 std::vector<Index> innerMap_;
569 bool hasOpen_ =
false;
570 int bottomSweeps_ = 400;
void init(DO &d, OpenFn &&openFn)
Build with cut-cell openness: w_f = α_f · A_f/d_f, where α_f = openFn(face centroid,...
DistributedOctree< Dim, Bits > DO
void init(DO &d)
Build the consistent face stencil, openness-free (w_f = A_f/d_f).
void apply(const std::vector< double > &u, std::vector< double > &Lu) const
Lu = L u (consistent conservative FV Laplacian, negative-definite).
void jacobi(std::vector< double > &u, const std::vector< double > &rhs, int sweeps, double omega=0.8) const
sweeps weighted-Jacobi relaxations of L u = rhs (point solve u_i ← (Σ w u_nb − V_i rhs_i)/Σ w).
double residualNorm(const std::vector< double > &u, const std::vector< double > &rhs) const
double residual(const std::vector< double > &u, const std::vector< double > &rhs, std::vector< double > &res) const
res = rhs − L u; returns the global L2 norm (volume-weighted).
const AmrGeometry< Dim > & globalGeometry() const
BlockOctree< Dim, Bits > Octree
typename Octree::Code Code
FaceInfo faceAcross(Index i, int axis, int dir) const
const IVec< Dim > & globalFineSize() const
std::vector< double > coverValues(const std::vector< std::array< Coord, Dim > > &coords, const std::vector< double > &field, double sentinel=kNoNeighbor) const
For each global fine coord, the covering leaf's field value on its owner (sentinel if none).
std::vector< int > coverLevels(const std::vector< std::array< Coord, Dim > > &coords) const
For each global fine coord (already wrapped into the domain), the level of the covering leaf on its o...
const IVec< Dim > & blockFineOrigin() const
typename Octree::Coord Coord
Geometric-multigrid V-cycle on a graded distributed octree, built on DistributedFvOperator.
void vcycle(std::vector< double > &x, const std::vector< double > &b, int pre=2, int post=2, int innerCycles=6, double omega=0.8, std::size_t L=0)
One V-cycle of L u = rhs on level L (default finest), correction scheme.
void build(const DO &finest, OpenFn &&openFn)
Build with cut-cell openness openFn on every level (each level re-samples the geometry at its own fac...
std::size_t numLevels() const
void build(const DO &finest)
Build the hierarchy from an already-graded, 2:1-balanced finest octree (openness-free; the coarsest i...
Index numLeaves(std::size_t L=0) const
DistributedOctree< Dim, Bits > DO
DistributedFvOperator< Dim, Bits > & op(std::size_t L=0)
int MPI_Allreduce(const void *sbuf, void *rbuf, int count, MPI_Datatype dt, MPI_Op, MPI_Comm)
Kokkos::View< T *, MemSpace > View
1D device array.
std::int64_t Index
Signed index type for grids and particles (supersedes block_decomposer's long int IndxT).