21#ifndef PECLET_CORE_AMR_DISTRIBUTED_POISSON_HPP
22#define PECLET_CORE_AMR_DISTRIBUTED_POISSON_HPP
24#ifdef PECLET_CORE_HAVE_MORTON
39template <
int Dim,
unsigned Bits = (Dim == 2 ? 32u : (Dim == 3 ? 21u : 16u))>
56 void apply(
const std::vector<double>& x, std::vector<double>&
y)
const {
59 const int F = 2 * Dim;
60 const double inv = 1.0 / (h0_ * h0_);
61 y.assign(
static_cast<std::size_t
>(n), 0.0);
64 for (
int f = 0;
f <
F; ++
f)
65 s += g[
static_cast<std::size_t
>(
i) *
F +
f] - x[
static_cast<std::size_t
>(
i)];
66 y[
static_cast<std::size_t
>(
i)] =
inv *
s;
74 void jacobi(std::vector<double>& x,
const std::vector<double>& b,
int sweeps,
75 double omega = 0.8)
const {
77 const int F = 2 * Dim;
78 const double inv = 1.0 / (h0_ * h0_), diag =
F *
inv;
79 std::vector<double>
lx;
83 x[
static_cast<std::size_t
>(
i)] +=
84 omega * (
lx[
static_cast<std::size_t
>(
i)] - b[
static_cast<std::size_t
>(
i)]) / diag;
89 double residual(
const std::vector<double>& x,
const std::vector<double>& b,
90 std::vector<double>& res)
const {
91 std::vector<double>
ax;
94 res.assign(
static_cast<std::size_t
>(n), 0.0);
97 double r = b[
static_cast<std::size_t
>(
i)] -
ax[
static_cast<std::size_t
>(
i)];
98 res[
static_cast<std::size_t
>(
i)] =
r;
107 double residualNorm(
const std::vector<double>& x,
const std::vector<double>& b)
const {
108 std::vector<double>
ax;
112 double r = b[
static_cast<std::size_t
>(
i)] -
ax[
static_cast<std::size_t
>(
i)];
141template <
int Dim,
unsigned Bits = (Dim == 2 ? 32u : (Dim == 3 ? 21u : 16u))>
148 const std::array<bool, Dim>& periodic,
MPI_Comm comm) {
155 auto lvl = std::make_unique<Level>();
158 lvl->d.init(g, 0,
lg, periodic, comm);
160 levels_.push_back(std::move(
lvl));
165 for (
int d = 0; d < Dim; ++d) {
166 if (g[d] % 2 != 0 || g[d] / 2 < 2)
177 for (std::size_t
L = 0;
L + 1 < levels_.size(); ++
L) {
178 auto&
fine = levels_[
L]->d;
179 auto&
coarse = levels_[
L + 1]->d;
181 auto& c2p = levels_[
L]->c2p;
182 c2p.assign(
static_cast<std::size_t
>(
nf), -1);
185 for (
int d = 0; d < Dim; ++d)
188 assert(p >= 0 &&
"ORB decompositions must nest (power-of-two grid+ranks)");
189 c2p[
static_cast<std::size_t
>(
i)] = p;
194 std::size_t
numLevels()
const {
return levels_.size(); }
200 void vcycle(std::vector<double>& x,
const std::vector<double>& b,
int pre = 2,
int post = 2,
201 int bottom = 30, std::size_t
L = 0) {
202 auto&
op = levels_[
L]->op;
203 if (
L + 1 == levels_.size()) {
208 std::vector<double> res;
211 const Index nc = levels_[
L + 1]->d.local().numLeaves();
212 std::vector<double>
cb(
static_cast<std::size_t
>(nc), 0.0);
213 std::vector<double>
cn(
static_cast<std::size_t
>(nc), 0.0);
214 const auto& c2p = levels_[
L]->c2p;
215 const Index nf = levels_[
L]->d.local().numLeaves();
217 Index p = c2p[
static_cast<std::size_t
>(
i)];
218 cb[
static_cast<std::size_t
>(p)] += res[
static_cast<std::size_t
>(
i)];
219 cn[
static_cast<std::size_t
>(p)] += 1.0;
221 for (
Index p = 0; p < nc; ++p)
222 if (
cn[
static_cast<std::size_t
>(p)] > 0.0)
223 cb[
static_cast<std::size_t
>(p)] /=
cn[
static_cast<std::size_t
>(p)];
225 std::vector<double>
cx(
static_cast<std::size_t
>(nc), 0.0);
229 x[
static_cast<std::size_t
>(
i)] +=
230 cx[
static_cast<std::size_t
>(c2p[
static_cast<std::size_t
>(
i)])];
238 std::vector<Index> c2p;
240 std::vector<std::unique_ptr<Level>> levels_;
Distributed geometric-multigrid V-cycle for the plain Laplacian on a uniform (lmax==0) octree,...
std::size_t numLevels() const
DistributedOctree< Dim, Bits > & octree(std::size_t L=0)
DistributedPoisson< Dim, Bits > & op(std::size_t L=0)
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.
Index numLeaves(std::size_t L=0) const
FaceGatherPlan buildFaceGatherPlan() 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.
double residualNorm(const std::vector< double > &x, const std::vector< double > &b) const
Global L2 norm of b − L x (across ranks).
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 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 = ∇².
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, double h0)
int MPI_Comm_size(MPI_Comm, int *s)
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).
Static topology of the face-neighbour gather: which out-slots are filled from a local leaf (directSlo...