22#ifndef PECLET_CORE_AMR_VELOCITY_MG_HPP
23#define PECLET_CORE_AMR_VELOCITY_MG_HPP
25#ifdef PECLET_CORE_HAVE_MORTON
40template <
unsigned Bits = 21u>
59 const std::vector<double>& kappa,
const std::vector<char>& fluid,
61 hmg_ = std::make_unique<AmrMultigrid<3, Bits>>();
63 std::size_t
nl = hmg_->numLevels();
64 while (
nl > 1 && hmg_->op(
nl - 1).octree().numLeaves() <
minCoarse)
70 std::vector<std::vector<double>>
kap(
nl);
72 for (std::size_t
L = 0;
L + 1 <
nl; ++
L) {
73 const Octree&
f = hmg_->op(
L).octree();
74 const Octree&
c = hmg_->op(
L + 1).octree();
75 const Index nf =
f.numLeaves(), nc =
c.numLeaves();
76 std::vector<double>
ks(
static_cast<std::size_t
>(nc), 0.0),
77 kn(
static_cast<std::size_t
>(nc), 0.0);
79 Code par = M::from_code(
f.code(
i)).ancestor(
f.level(
i) + 1).code();
82 ks[
static_cast<std::size_t
>(p)] +=
kap[
L][
static_cast<std::size_t
>(
i)];
83 kn[
static_cast<std::size_t
>(p)] += 1.0;
86 for (
Index p = 0; p < nc; ++p)
87 ks[
static_cast<std::size_t
>(p)] /=
88 (
kn[
static_cast<std::size_t
>(p)] > 0 ?
kn[
static_cast<std::size_t
>(p)] : 1.0);
89 kap[
L + 1] = std::move(
ks);
100 std::vector<char>
excl(fluid.size());
101 for (std::size_t
i = 0;
i < fluid.size(); ++
i)
102 excl[
i] = (!fluid[
i] || cut[
i]) ? 1 : 0;
104 allocScratch(levels_[0],
static_cast<Index>(fluid.size()));
107 std::vector<Index>
hs(
static_cast<std::size_t
>(
fineOp.n) + 1),
hn(
fineOp.faceNbr.extent(0));
108 auto ms = Kokkos::create_mirror_view(
fineOp.faceStart);
109 auto mn = Kokkos::create_mirror_view(
fineOp.faceNbr);
110 Kokkos::deep_copy(
ms,
fineOp.faceStart);
111 Kokkos::deep_copy(
mn,
fineOp.faceNbr);
112 for (std::size_t
i = 0;
i <
hs.size(); ++
i)
114 for (std::size_t
i = 0;
i <
hn.size(); ++
i)
119 for (std::size_t
L = 1;
L <
nl; ++
L)
120 buildStaircase(
L, idiag, mu,
kap[
L]);
123 for (std::size_t
L = 0;
L + 1 <
nl; ++
L)
127 std::size_t
numLevels()
const {
return levels_.size(); }
142 Level&
lv = levels_[
L];
144 if (
L + 1 == levels_.size()) {
154 Level&
cl = levels_[
L + 1];
156 Kokkos::deep_copy(
cl.x, 0.0);
182 void allocScratch(Level&
lv,
Index n) {
190 void buildStaircase(std::size_t
L,
double idiag,
double mu,
const std::vector<double>&
kap) {
199 const double Ldom =
ap.h0() *
static_cast<double>(
oct.brick()[0] * (
Index(1) <<
oct.lmax()));
200 const double id = std::max(idiag, mu / (
Ldom *
Ldom));
201 std::vector<double> diag(
static_cast<std::size_t
>(n), 1.0);
202 std::vector<char> solid(
static_cast<std::size_t
>(n), 1);
203 std::vector<Index> start(
static_cast<std::size_t
>(n) + 1, 0);
204 std::vector<std::vector<std::pair<Index, double>>>
rows(
static_cast<std::size_t
>(n));
206 if (
kap[
static_cast<std::size_t
>(
i)] < 0.5)
208 solid[
static_cast<std::size_t
>(
i)] = 0;
209 const double H =
ap.cellWidth(
i);
210 const double coef = mu / (
H *
H);
212 for (
int axis = 0; axis < 3; ++axis)
213 for (
int dir = -1; dir <= 1; dir += 2) {
214 Index j =
ap.periodicNeighbor(
i, axis, dir);
220 diag[
static_cast<std::size_t
>(
i)] =
dsum;
223 start[
static_cast<std::size_t
>(
i) + 1] =
224 start[
static_cast<std::size_t
>(
i)] +
225 static_cast<Index>(
rows[
static_cast<std::size_t
>(
i)].size());
226 const Index nnz = start[
static_cast<std::size_t
>(n)];
227 std::vector<Index> nbr(
static_cast<std::size_t
>(
nnz));
228 std::vector<double> coef(
static_cast<std::size_t
>(
nnz));
230 Index k = start[
static_cast<std::size_t
>(
i)];
232 nbr[
static_cast<std::size_t
>(k)] =
e.first;
233 coef[
static_cast<std::size_t
>(k)] =
e.second;
237 Level&
lv = levels_[
L];
240 lv.op.faceStart =
toDevice(start,
"vmg_fstart");
248 void buildTransfer(std::size_t
L) {
249 const Octree&
f = hmg_->op(
L).octree();
250 const Octree&
c = hmg_->op(
L + 1).octree();
251 const Index nf =
f.numLeaves(), nc =
c.numLeaves();
252 std::vector<Index> c2p(
static_cast<std::size_t
>(
nf));
253 std::vector<Index>
cnt(
static_cast<std::size_t
>(nc), 0);
255 Code par = M::from_code(
f.code(
i)).ancestor(
f.level(
i) + 1).code();
257 c2p[
static_cast<std::size_t
>(
i)] = p;
259 ++
cnt[
static_cast<std::size_t
>(p)];
261 std::vector<Index>
cstart(
static_cast<std::size_t
>(nc) + 1, 0);
262 for (
Index p = 0; p < nc; ++p)
263 cstart[
static_cast<std::size_t
>(p) + 1] =
264 cstart[
static_cast<std::size_t
>(p)] +
cnt[
static_cast<std::size_t
>(p)];
265 std::vector<Index>
cidx(
static_cast<std::size_t
>(
nf));
268 Index p = c2p[
static_cast<std::size_t
>(
i)];
270 cidx[
static_cast<std::size_t
>(
cur[
static_cast<std::size_t
>(p)]++)] =
i;
272 levels_[
L].c2p =
toDevice(c2p,
"vmg_c2p");
277 std::unique_ptr<AmrMultigrid<3, Bits>> hmg_;
278 std::vector<Level> levels_;
morton::Morton< Dim, Bits > M
typename M::code_type Code
void setFineOp(const MomentumOp &fineOp)
Re-point level 0 at the (possibly FOU-updated) fine operator before a solve.
BlockOctree< 3, Bits > Octree
Index numLeaves(std::size_t L=0) const
View< double > b(std::size_t L=0)
void build(const Octree &finest, double h0, double idiag, double mu, const MomentumOp &fineOp, const std::vector< double > &kappa, const std::vector< char > &fluid, const std::vector< char > &cut, Index minCoarse=256)
Build the rediscretized hierarchy.
View< double > x(std::size_t L=0)
void setGaussSeidel(bool on)
Opt-in: use multicolour Gauss–Seidel as the smoother (per-level colouring) instead of Jacobi — the st...
void vcycle(int pre=2, int post=2, int bottom=30, double omega=0.7, std::size_t L=0)
One V-cycle (correction scheme) solving the fine operator.
AmrPoisson< 3, Bits > Poisson
typename Octree::Code Code
std::size_t numLevels() const
void residualMom(const MomentumOp &op, View< const double > u, View< const double > b, View< double > res)
res = b − A u.
void multicolorGSMom(const MomentumOp &op, View< double > u, View< const double > b, const Coloring &col, double omega)
One symmetric multicolour Gauss–Seidel sweep of A u = b in place (momentum operator: diag + face CSR ...
void zeroMasked(View< double > v, View< const char > excl, Index n)
Zero v at excluded cells (excl != 0).
Coloring greedyColoring(const std::vector< Index > &start, const std::vector< Index > &nbr, Index n)
Greedy colouring of the face CSR (start/nbr, host): each cell gets the smallest colour not used by an...
void jacobiMom(const MomentumOp &op, View< double > u, View< const double > b, View< double > tmp, double omega)
One weighted-Jacobi sweep of A u = b (in place).
void restrictField(View< const Index > childStart, View< const Index > childIdx, View< const double > fine, View< double > coarse, Index nCoarse)
Restrict: coarse(p) = mean over p's children (CSR fixed order ⇒ deterministic).
void prolongAddMasked(View< const Index > c2p, View< const double > coarse, View< const char > excl, View< double > fine, Index nFine)
Masked piecewise-constant prolong + correct: fine(i) += coarse(c2p(i)) only on non-excluded fine cell...
View< T > toDevice(const std::vector< T > &h, const std::string &label)
Upload a host std::vector into a freshly-sized device View (empty vector => empty view).
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).
A graph colouring of a face CSR: cells grouped by colour.
Assembled momentum operator on the device: (A u)_i = diag_i u_i + Σ coef·u[nbr], with an optional imp...