20#ifndef PECLET_AMR_DISTRIBUTED_POISSON_HPP
21#define PECLET_AMR_DISTRIBUTED_POISSON_HPP
32#include "peclet/core/common/mpi.hpp"
33#include "peclet/core/common/types.hpp"
37template <
int Dim,
unsigned Bits = (Dim == 2 ? 32u : (Dim == 3 ? 21u : 16u))>
55 void apply(
const std::vector<double>& x, std::vector<double>&
y)
const {
58 const int F = 2 * Dim;
63 for (
int d = 0; d < Dim; ++d)
64 inv[d] = 1.0 / (h0_[d] * h0_[d]);
65 y.assign(
static_cast<std::size_t
>(n), 0.0);
68 for (
int f = 0;
f <
F; ++
f)
69 s +=
inv[
f / 2] * (g[
static_cast<std::size_t
>(
i) *
F +
f] - x[
static_cast<std::size_t
>(
i)]);
70 y[
static_cast<std::size_t
>(
i)] =
s;
78 void jacobi(std::vector<double>& x,
const std::vector<double>& b,
int sweeps,
79 double omega = 0.8)
const {
81 const int F = 2 * Dim;
85 for (
int d = 0; d < Dim; ++d)
86 diag += 2.0 / (h0_[d] * h0_[d]);
87 std::vector<double>
lx;
91 x[
static_cast<std::size_t
>(
i)] +=
92 omega * (
lx[
static_cast<std::size_t
>(
i)] - b[
static_cast<std::size_t
>(
i)]) / diag;
97 double residual(
const std::vector<double>& x,
const std::vector<double>& b,
98 std::vector<double>& res)
const {
99 std::vector<double>
ax;
102 res.assign(
static_cast<std::size_t
>(n), 0.0);
105 double r = b[
static_cast<std::size_t
>(
i)] -
ax[
static_cast<std::size_t
>(
i)];
106 res[
static_cast<std::size_t
>(
i)] =
r;
115 double residualNorm(
const std::vector<double>& x,
const std::vector<double>& b)
const {
116 std::vector<double>
ax;
120 double r = b[
static_cast<std::size_t
>(
i)] -
ax[
static_cast<std::size_t
>(
i)];
130 Vec<Dim> h0_ = detail::filledVec<Dim>(1.0);
149template <
int Dim,
unsigned Bits = (Dim == 2 ? 32u : (Dim == 3 ? 21u : 16u))>
156 const std::array<bool, Dim>& periodic,
MPI_Comm comm) {
165 auto lvl = std::make_unique<Level>();
168 lvl->d.init(g, 0,
lg, periodic, comm);
170 levels_.push_back(std::move(
lvl));
175 for (
int d = 0; d < Dim; ++d) {
176 if (g[d] % 2 != 0 || g[d] / 2 < 2)
184 for (
int d = 0; d < Dim; ++d)
188 for (std::size_t
L = 0;
L + 1 < levels_.size(); ++
L) {
189 auto&
fine = levels_[
L]->d;
190 auto&
coarse = levels_[
L + 1]->d;
192 auto& c2p = levels_[
L]->c2p;
193 c2p.assign(
static_cast<std::size_t
>(
nf), -1);
196 for (
int d = 0; d < Dim; ++d)
199 assert(
p >= 0 &&
"ORB decompositions must nest (power-of-two grid+ranks)");
200 c2p[
static_cast<std::size_t
>(
i)] =
p;
205 std::size_t
numLevels()
const {
return levels_.size(); }
211 void vcycle(std::vector<double>& x,
const std::vector<double>& b,
int pre = 2,
int post = 2,
212 int bottom = 30, std::size_t
L = 0) {
213 auto&
op = levels_[
L]->op;
214 if (
L + 1 == levels_.size()) {
219 std::vector<double> res;
222 const Index nc = levels_[
L + 1]->d.local().numLeaves();
223 std::vector<double>
cb(
static_cast<std::size_t
>(
nc), 0.0);
224 std::vector<double>
cn(
static_cast<std::size_t
>(
nc), 0.0);
225 const auto& c2p = levels_[
L]->c2p;
226 const Index nf = levels_[
L]->d.local().numLeaves();
228 Index p = c2p[
static_cast<std::size_t
>(
i)];
229 cb[
static_cast<std::size_t
>(
p)] += res[
static_cast<std::size_t
>(
i)];
230 cn[
static_cast<std::size_t
>(
p)] += 1.0;
233 if (
cn[
static_cast<std::size_t
>(
p)] > 0.0)
234 cb[
static_cast<std::size_t
>(
p)] /=
cn[
static_cast<std::size_t
>(
p)];
236 std::vector<double>
cx(
static_cast<std::size_t
>(
nc), 0.0);
240 x[
static_cast<std::size_t
>(
i)] +=
241 cx[
static_cast<std::size_t
>(c2p[
static_cast<std::size_t
>(
i)])];
249 std::vector<Index> c2p;
251 std::vector<std::unique_ptr<Level>> levels_;
Distributed geometric-multigrid V-cycle for the plain Laplacian on a uniform (lmax==0) octree,...
void build(const IVec< Dim > &g0, const AmrGeometry< Dim > &geo, const std::array< bool, Dim > &periodic, MPI_Comm comm)
Build the hierarchy from the finest global root grid g0 (lmax==0) on comm.
void vcycle(std::vector< double > &x, const std::vector< double > &b, int pre=2, int post=2, int bottom=30, std::size_t L=0)
One V-cycle of A x = b on level L (default the finest), correction scheme.
DistributedOctree< Dim, Bits > & octree(std::size_t L=0)
DistributedPoisson< Dim, Bits > & op(std::size_t L=0)
Index numLeaves(std::size_t L=0) const
std::size_t numLevels() const
std::vector< double > faceNeighborGather(const std::vector< double > &field, double sentinel=kNoNeighbor) const
For each local leaf and each of the 2*Dim faces, the neighbouring leaf's field value.
FaceGatherPlan buildFaceGatherPlan() const
void init(DistributedOctree< Dim, Bits > &d, double h0)
double residualNorm(const std::vector< double > &x, const std::vector< double > &b) const
Global L2 norm of b − L x (across ranks).
void jacobi(std::vector< double > &x, const std::vector< double > &b, int sweeps, double omega=0.8) const
sweeps damped-Jacobi relaxations of L u = b (in place), L = ∇².
double residual(const std::vector< double > &x, const std::vector< double > &b, std::vector< double > &res) const
res = b − L x (local vector); also returns its global L2 norm.
void apply(const std::vector< double > &x, std::vector< double > &y) const
y = L x with L = ∇² (negative-definite: diagonal −2*Dim/h², off +1/h²).
void init(DistributedOctree< Dim, Bits > &d, const Vec< Dim > &h0)
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,...
Static topology of the face-neighbour gather: which out-slots are filled from a local leaf (directSlo...