25#ifndef PECLET_AMR_DISTRIBUTED_FV_HPP
26#define PECLET_AMR_DISTRIBUTED_FV_HPP
37#include "peclet/core/common/mpi.hpp"
38#include "peclet/core/common/types.hpp"
42template <
int Dim,
unsigned Bits = (Dim == 2 ? 32u : (Dim == 3 ? 21u : 16u))>
61 template <
class OpenFn>
69 invVol_.assign(
static_cast<std::size_t
>(n), 0.0);
70 diag_.assign(
static_cast<std::size_t
>(n), 0.0);
77 for (
int d2 = 0;
d2 < Dim; ++
d2)
81 :
org[
d2] + (
static_cast<double>(
flo[
d2]) + 0.5 *
static_cast<double>(
s)) * h0_[
d2];
83 return a < 0.0 ? 0.0 : (
a > 1.0 ? 1.0 :
a);
85 auto gLo = [&](
const std::array<Coord, Dim>&
lc) {
86 std::array<long, Dim> g{};
87 for (
int d2 = 0;
d2 < Dim; ++
d2)
88 g[
d2] =
static_cast<long>(
lc[
d2]) +
static_cast<long>(
bfo[
d2]);
96 std::array<long, Dim>
flo{};
97 flo[axis] = (dir > 0) ?
clo[axis] +
static_cast<long>(
si) :
clo[axis] -
static_cast<long>(
sj);
99 for (
int t = 0;
t < Dim; ++
t) {
102 flo[
t] =
clo[
t] + (((k >>
bit) & 1) ?
static_cast<long>(
sj) : 0
L);
105 for (
int d2 = 0;
d2 < Dim; ++
d2) {
121 std::vector<std::vector<E>>
ent(
static_cast<std::size_t
>(n));
123 std::array<Coord, Dim> gc{};
128 std::array<long, Dim> iLo{};
130 std::vector<RFace>
rfaces;
133 const int Li =
static_cast<int>(
t.level(
i));
135 invVol_[
static_cast<std::size_t
>(
i)] = 1.0 / cellVol(
Li);
136 auto b =
t.bounds(
i);
137 const auto&
lo = b[0];
138 for (
int axis = 0; axis < Dim; ++axis)
139 for (
int dir = -1; dir <= 1; dir += 2) {
147 const int Lj =
static_cast<int>(
t.level(
j));
154 const long pc = (dir > 0) ?
static_cast<long>(
lo[axis]) +
static_cast<long>(
si)
155 :
static_cast<long>(
lo[axis]) - 1;
156 const int nsub = 1 << (Dim - 1);
157 for (
int k = 0; k <
nsub; ++k) {
158 std::array<Coord, Dim>
q =
lo;
161 for (
int tt = 0;
tt < Dim; ++
tt) {
176 e.rfId =
static_cast<int>(
rfaces.size());
187 for (std::size_t
r = 0;
r <
rfaces.size(); ++
r)
194 auto ghostRef = [&](
const std::array<Coord, Dim>& gc) ->
Index {
204 start_.assign(
static_cast<std::size_t
>(n) + 1, 0);
206 for (
const E&
e :
ent[
static_cast<std::size_t
>(
i)]) {
208 ref_.push_back(
e.localRef);
210 diag_[
static_cast<std::size_t
>(
i)] +=
e.w;
213 const int Lj =
coverLv[
static_cast<std::size_t
>(
e.rfId)];
221 diag_[
static_cast<std::size_t
>(
i)] +=
ww;
224 const int nsub = 1 << (Dim - 1);
225 for (
int k = 0; k <
nsub; ++k) {
226 std::array<Coord, Dim>
sc = subCoord(
rf.gc,
rf.axis, k,
sj);
229 const double ww =
a * coeff(
rf.si,
sj,
rf.axis);
232 diag_[
static_cast<std::size_t
>(
i)] +=
ww;
237 start_[
static_cast<std::size_t
>(
i) + 1] =
static_cast<Index>(ref_.size());
246 void apply(
const std::vector<double>&
u, std::vector<double>&
Lu)
const {
247 std::vector<double> g = d_->
coverValues(ghostCoords_,
u);
248 Lu.assign(
static_cast<std::size_t
>(n_), 0.0);
250 const double ui =
u[
static_cast<std::size_t
>(
i)];
252 for (
Index k = start_[
static_cast<std::size_t
>(
i)];
254 const Index r = ref_[
static_cast<std::size_t
>(k)];
257 acc += w_[
static_cast<std::size_t
>(k)] * (
val -
ui);
259 Lu[
static_cast<std::size_t
>(
i)] = invVol_[
static_cast<std::size_t
>(
i)] *
acc;
264 double residual(
const std::vector<double>&
u,
const std::vector<double>&
rhs,
265 std::vector<double>& res)
const {
266 std::vector<double>
lu;
268 res.assign(
static_cast<std::size_t
>(n_), 0.0);
271 double r =
rhs[
static_cast<std::size_t
>(
i)] -
lu[
static_cast<std::size_t
>(
i)];
272 res[
static_cast<std::size_t
>(
i)] =
r;
273 s +=
r *
r / invVol_[
static_cast<std::size_t
>(
i)];
281 std::vector<double> res;
289 double omega = 0.8)
const {
290 std::vector<double> tmp(
static_cast<std::size_t
>(n_));
292 std::vector<double> g = d_->
coverValues(ghostCoords_,
u);
295 for (
Index k = start_[
static_cast<std::size_t
>(
i)];
297 const Index r = ref_[
static_cast<std::size_t
>(k)];
300 sumOff += w_[
static_cast<std::size_t
>(k)] *
val;
302 const double Vrhs =
rhs[
static_cast<std::size_t
>(
i)] / invVol_[
static_cast<std::size_t
>(
i)];
303 tmp[
static_cast<std::size_t
>(
i)] =
304 (diag_[
static_cast<std::size_t
>(
i)] != 0.0)
305 ? (
sumOff -
Vrhs) / diag_[
static_cast<std::size_t
>(
i)]
306 :
u[
static_cast<std::size_t
>(
i)];
309 u[
static_cast<std::size_t
>(
i)] = (1.0 -
omega) *
u[
static_cast<std::size_t
>(
i)] +
310 omega * tmp[
static_cast<std::size_t
>(
i)];
316 double cellWidth(
int level,
int axis)
const {
317 return h0_[axis] *
static_cast<double>(
Index(1) << level);
319 double cellWidth(
int level)
const {
return cellWidth(level, 0); }
320 double cellVol(
int level)
const {
321 const double f =
static_cast<double>(
Index(1) << level);
323 for (
int d = 0; d < Dim; ++d)
328 double areaOf(
Coord s,
int axis)
const {
330 for (
int d = 0; d < Dim; ++d)
332 a *=
static_cast<double>(
s) * h0_[d];
335 double areaOf(
Coord s)
const {
return areaOf(
s, 0); }
337 double dist = 0.5 * (
static_cast<double>(
si) +
static_cast<double>(
sj)) * h0_[axis];
338 return areaOf(
si <
sj ?
si :
sj, axis) / dist;
343 std::array<Coord, Dim> subCoord(
const std::array<Coord, Dim>& gc,
int axis,
int k,
345 std::array<Coord, Dim>
q = gc;
347 for (
int tt = 0;
tt < Dim; ++
tt) {
351 long v =
static_cast<long>(gc[
tt]) +
static_cast<long>(off);
362 Vec<Dim> h0_ = detail::filledVec<Dim>(1.0);
364 std::vector<double> invVol_, diag_, w_;
365 std::vector<Index> start_, ref_;
366 std::vector<std::array<Coord, Dim>> ghostCoords_;
386template <
int Dim,
unsigned Bits = (Dim == 2 ? 32u : (Dim == 3 ? 21u : 16u))>
396 buildImpl(
finest, [](
const Vec<Dim>&,
int) {
return 1.0; },
false);
403 template <
class OpenFn>
409 template <
class OpenFn>
414 const unsigned lmax =
finest.lmax();
419 auto l0 = std::make_unique<Level>();
421 levels_.push_back(std::move(
l0));
423 BO coarse = levels_.back()->d.local();
425 coarse.coarsenIf([](
typename DO::Code,
unsigned) {
return true; });
428 auto lv = std::make_unique<Level>();
429 lv->d.init(g, lmax,
geo,
per, comm);
431 levels_.push_back(std::move(
lv));
443 int localLevels =
static_cast<int>(levels_.size());
446 while (
static_cast<int>(levels_.size()) <
globalLevels) {
447 auto lv = std::make_unique<Level>();
448 lv->d.init(g, lmax,
geo,
per, comm);
449 lv->d.local() = levels_.back()->d.local();
450 levels_.push_back(std::move(
lv));
453 for (
auto&
lv : levels_) {
455 const Index n =
lv->d.local().numLeaves();
456 lv->x.assign(
static_cast<std::size_t
>(n), 0.0);
457 lv->b.assign(
static_cast<std::size_t
>(n), 0.0);
458 lv->res.assign(
static_cast<std::size_t
>(n), 0.0);
461 for (std::size_t
L = 0;
L + 1 < levels_.size(); ++
L) {
462 const BO&
f = levels_[
L]->d.local();
463 const BO&
c = levels_[
L + 1]->d.local();
465 auto& c2p = levels_[
L]->c2p;
466 c2p.assign(
static_cast<std::size_t
>(
nf), -1);
468 c2p[
static_cast<std::size_t
>(
i)] =
c.find(
f.code(
i));
480 for (
int d = 0; d < Dim; ++d)
481 ig.h0[d] =
geo.h0[d] *
static_cast<double>(
Index(1) << lmax);
482 inner_ = std::make_unique<DistributedMultigrid<Dim, Bits>>();
483 inner_->build(g,
ig,
per, comm);
485 nCoarse_ =
coarsest.local().numLeaves();
486 innerMap_.assign(
static_cast<std::size_t
>(nCoarse_), -1);
487 for (
Index i = 0;
i < nCoarse_; ++
i)
488 innerMap_[
static_cast<std::size_t
>(
i)] =
489 inner_->octree(0).findGlobalRoot(
coarsest.globalRootOf(
i));
494 std::size_t
numLevels()
const {
return levels_.size(); }
501 void vcycle(std::vector<double>& x,
const std::vector<double>& b,
int pre = 2,
int post = 2,
503 auto&
lv = *levels_[
L];
504 if (
L + 1 == levels_.size()) {
508 lv.op.jacobi(x, b, bottomSweeps_,
omega);
512 std::vector<double> res;
513 lv.op.residual(x, b, res);
515 auto&
cl = *levels_[
L + 1];
516 const Index nc =
cl.d.local().numLeaves();
517 std::vector<double>
cb(
static_cast<std::size_t
>(
nc), 0.0),
518 cn(
static_cast<std::size_t
>(
nc), 0.0);
519 const auto& c2p =
lv.c2p;
520 const Index nf =
lv.d.local().numLeaves();
522 Index p = c2p[
static_cast<std::size_t
>(
i)];
525 cb[
static_cast<std::size_t
>(
p)] += res[
static_cast<std::size_t
>(
i)];
526 cn[
static_cast<std::size_t
>(
p)] += 1.0;
529 if (
cn[
static_cast<std::size_t
>(
p)] > 0.0)
530 cb[
static_cast<std::size_t
>(
p)] /=
cn[
static_cast<std::size_t
>(
p)];
531 std::vector<double>
cx(
static_cast<std::size_t
>(
nc), 0.0);
534 Index p = c2p[
static_cast<std::size_t
>(
i)];
536 x[
static_cast<std::size_t
>(
i)] +=
cx[
static_cast<std::size_t
>(
p)];
547 const std::vector<double>& b,
int cycles) {
548 std::vector<double> res;
550 const Index ni = inner_->numLeaves(0);
551 std::vector<double>
bi(
static_cast<std::size_t
>(
ni), 0.0),
552 ei(
static_cast<std::size_t
>(
ni), 0.0);
553 for (
Index i = 0;
i < nCoarse_; ++
i) {
554 Index m = innerMap_[
static_cast<std::size_t
>(
i)];
556 bi[
static_cast<std::size_t
>(m)] = res[
static_cast<std::size_t
>(
i)];
559 inner_->vcycle(
ei,
bi);
560 for (
Index i = 0;
i < nCoarse_; ++
i) {
561 Index m = innerMap_[
static_cast<std::size_t
>(
i)];
563 x[
static_cast<std::size_t
>(
i)] +=
ei[
static_cast<std::size_t
>(m)];
569 DistributedFvOperator<Dim, Bits> op;
570 std::vector<Index> c2p;
571 std::vector<double> x, b, res;
573 std::vector<std::unique_ptr<Level>> levels_;
574 std::unique_ptr<DistributedMultigrid<Dim, Bits>> inner_;
575 std::vector<Index> innerMap_;
577 bool hasOpen_ =
false;
578 int bottomSweeps_ = 400;
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).
void init(DO &d, OpenFn &&openFn)
Build with cut-cell openness: w_f = α_f · A_f/d_f, where α_f = openFn(face centroid,...
void init(DO &d)
Build the consistent face stencil, openness-free (w_f = A_f/d_f).
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).
DistributedOctree< Dim, Bits > DO
void apply(const std::vector< double > &u, std::vector< double > &Lu) const
Lu = L u (consistent conservative FV Laplacian, negative-definite).
typename Octree::Coord Coord
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 Vec< Dim > & h0() const
Phase 3: the FINEST spacing per axis (docs/amr_anisotropic.md §2).
const AmrGeometry< Dim > & globalGeometry() 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).
const IVec< Dim > & blockFineOrigin() const
FaceInfo faceAcross(Index i, int axis, int dir) const
typename Octree::Code Code
const IVec< Dim > & globalFineSize() const
BlockOctree< Dim, Bits > Octree
Geometric-multigrid V-cycle on a graded distributed octree, built on DistributedFvOperator.
Index numLeaves(std::size_t L=0) const
DistributedFvOperator< Dim, Bits > & op(std::size_t L=0)
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...
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.
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...
DistributedOctree< Dim, Bits > DO
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,...
World-space placement of a block-local octree: fine coordinate (0,..,0) sits at origin,...