core
Shared MPI block decomposition + asynchronous ghost-layer exchange (header-only C++20)
Loading...
Searching...
No Matches
momentum.hpp
Go to the documentation of this file.
1// core — device (Kokkos) momentum operator + smoother/solver for the AMR
2// collocated flow step.
3//
4// The cut-cell momentum operator A = (ρ/dt)I − μ∇² (+ implicit-FOU advection + the
5// ξ-polynomial Dirichlet overlay on cut cells) assembled by AmrCutCell::assembleOperator
6// as a per-cell diagonal + general face CSR, uploaded once and applied / solved entirely
7// in device kernels. This replaces the host-serial AmrCutCell::gaussSeidel that the drag
8// study found to be the bottleneck (the pressure Poisson was already on the device path).
9//
10// A is generally NON-symmetric (the cut-cell D_rescale row scaling and the upwind
11// advection break symmetry), so the smoother is weighted Jacobi (parallel, deterministic:
12// reads only the previous iterate) and the Krylov accelerator is BiCGStab (handles
13// non-symmetric A) rather than CG. For moderate dt the reaction diagonal (ρ/dt) makes A
14// strongly diagonally dominant ⇒ Jacobi alone converges fast; BiCGStab covers the
15// large-dt / steady regime where the operator approaches the (ill-conditioned) viscous
16// Laplacian.
17//
18// Validation is by convergence + agreement with the host operator to tolerance (the GPU
19// matvec differs from host in the last bit by FMA contraction). Requires a Kokkos build +
20// the morton checkout (PECLET_CORE_HAVE_MORTON).
21#ifndef PECLET_CORE_AMR_MOMENTUM_HPP
22#define PECLET_CORE_AMR_MOMENTUM_HPP
23
24#ifdef PECLET_CORE_HAVE_MORTON
25
26#include <cmath>
27#include <functional>
28#include <map>
29#include <vector>
30
32#include "peclet/core/amr/face_csr.hpp" // shared host+device assembled-operator row kernels
33#include "peclet/core/amr/multigrid.hpp" // restrictField / prolongAdd transfer kernels
34#include "peclet/core/amr/pcg.hpp" // dotPlain-style primitives: axpy, zpby, negate
36
37namespace peclet::core::amr {
38
56
63 v.n = op.n;
64 v.diag = op.diag;
65 v.coef = op.faceCoef;
66 v.start = op.faceStart;
67 v.nbr = op.faceNbr;
68 v.hasAdv = op.hasAdv;
69 v.advDiag = op.advDiag;
70 v.advCoef = op.advCoef;
71 v.advStart = op.advStart;
72 v.advNbr = op.advNbr;
73 return v;
74}
75
78 const auto A = momView(op);
79 Kokkos::parallel_for(
80 "amr::mom_apply", op.n, KOKKOS_LAMBDA(const Index i) { Au(i) = faceCsrApplyRow(A, i, u); });
81}
82
85 View<double> res) {
86 const auto A = momView(op);
87 Kokkos::parallel_for(
88 "amr::mom_residual", op.n,
89 KOKKOS_LAMBDA(const Index i) { res(i) = b(i) - faceCsrApplyRow(A, i, u); });
90}
91
95 double omega) {
96 const auto A = momView(op);
97 Kokkos::parallel_for(
98 "amr::mom_jacobi_compute", op.n, KOKKOS_LAMBDA(const Index i) {
99 double off, d;
100 faceCsrOffDiag(A, i, u, off, d);
101 tmp(i) = (d != 0.0) ? (b(i) - off) / d : u(i);
102 });
103 Kokkos::parallel_for(
104 "amr::mom_jacobi_update", op.n,
105 KOKKOS_LAMBDA(const Index i) { u(i) = (1.0 - omega) * u(i) + omega * tmp(i); });
106}
107
110 double s = 0.0;
111 Kokkos::parallel_reduce(
112 "amr::mom_dot", n, KOKKOS_LAMBDA(const Index i, double& acc) { acc += a(i) * b(i); }, s);
113 return s;
114}
115
119 double omega, Index n) {
120 Kokkos::parallel_for(
121 "amr::mom_pupdate", n,
122 KOKKOS_LAMBDA(const Index i) { p(i) = r(i) + beta * (p(i) - omega * v(i)); });
123}
124
125// ===========================================================================
126// Multicolour Gauss–Seidel smoother + graph colouring (the RB-GS mirror of flow's
127// ibmRbgsStencilColor on the AMR). On a 2:1-graded octree (or a Voronoi-cell mesh) the
128// face-adjacency graph needs a general greedy colouring (~6–8 colours); a colour is a set of
129// cells with no shared face, so all of one colour update in parallel reading the already-updated
130// other colours — a true GS sweep, deterministic (fixed cell order ⇒ fixed colouring). GS smooths
131// ~2× better than damped Jacobi (and is the strong fine smoother that "owns the cut band" the
132// rediscretized staircase velocity-MG excludes from its coarse grid). Mesh-agnostic: operates on a
133// face CSR + the assembled operator, no octree types.
134// ===========================================================================
135
138struct Coloring {
139 std::vector<Index> hStart;
141 int nColors = 1;
142};
143
155inline Coloring greedyColoring(const std::vector<Index>& start, const std::vector<Index>& nbr,
156 Index n) {
157 // Build the symmetric (undirected) adjacency in CSR form.
158 std::vector<Index> deg(static_cast<std::size_t>(n) + 1, 0);
159 for (Index i = 0; i < n; ++i)
160 for (Index k = start[static_cast<std::size_t>(i)]; k < start[static_cast<std::size_t>(i) + 1];
161 ++k) {
162 ++deg[static_cast<std::size_t>(i) + 1];
163 ++deg[static_cast<std::size_t>(nbr[static_cast<std::size_t>(k)]) + 1];
164 }
165 for (Index i = 0; i < n; ++i)
166 deg[static_cast<std::size_t>(i) + 1] += deg[static_cast<std::size_t>(i)];
167 std::vector<Index> aStart(deg); // copy of the offsets
168 std::vector<Index> aNbr(static_cast<std::size_t>(deg[static_cast<std::size_t>(n)]));
169 std::vector<Index> acur(deg.begin(), deg.end() - 1);
170 for (Index i = 0; i < n; ++i)
171 for (Index k = start[static_cast<std::size_t>(i)]; k < start[static_cast<std::size_t>(i) + 1];
172 ++k) {
173 const Index j = nbr[static_cast<std::size_t>(k)];
174 aNbr[static_cast<std::size_t>(acur[static_cast<std::size_t>(i)]++)] = j;
175 aNbr[static_cast<std::size_t>(acur[static_cast<std::size_t>(j)]++)] = i;
176 }
177 std::vector<int> color(static_cast<std::size_t>(n), -1);
178 std::vector<int> stamp; // stamp[c]==i ⇒ colour c forbidden for cell i (avoids per-cell clears)
179 int nColors = 1;
180 for (Index i = 0; i < n; ++i) {
181 for (Index k = aStart[static_cast<std::size_t>(i)]; k < aStart[static_cast<std::size_t>(i) + 1];
182 ++k) {
183 const int nc = color[static_cast<std::size_t>(aNbr[static_cast<std::size_t>(k)])];
184 if (nc >= 0) {
185 if (static_cast<std::size_t>(nc) >= stamp.size())
186 stamp.resize(static_cast<std::size_t>(nc) + 1, -1);
187 stamp[static_cast<std::size_t>(nc)] = static_cast<int>(i);
188 }
189 }
190 int c = 0;
191 while (c < static_cast<int>(stamp.size()) &&
192 stamp[static_cast<std::size_t>(c)] == static_cast<int>(i))
193 ++c;
194 color[static_cast<std::size_t>(i)] = c;
195 if (c + 1 > nColors)
196 nColors = c + 1;
197 }
198 Coloring col;
199 col.nColors = nColors;
200 col.hStart.assign(static_cast<std::size_t>(nColors) + 1, 0);
201 for (Index i = 0; i < n; ++i)
202 ++col.hStart[static_cast<std::size_t>(color[static_cast<std::size_t>(i)]) + 1];
203 for (int c = 0; c < nColors; ++c)
204 col.hStart[static_cast<std::size_t>(c) + 1] += col.hStart[static_cast<std::size_t>(c)];
205 std::vector<Index> idx(static_cast<std::size_t>(n));
206 std::vector<Index> cur(col.hStart.begin(), col.hStart.end() - 1);
207 for (Index i = 0; i < n; ++i) {
208 const int c = color[static_cast<std::size_t>(i)];
209 idx[static_cast<std::size_t>(cur[static_cast<std::size_t>(c)]++)] = i;
210 }
211 col.idx = toDevice(idx, "gs_coloring");
212 return col;
213}
214
226 const Coloring& col, double omega) {
227 const auto A = momView(op);
228 auto idx = col.idx;
229 auto colorPass = [&](int c) {
230 const Index a0 = col.hStart[static_cast<std::size_t>(c)];
231 const Index a1 = col.hStart[static_cast<std::size_t>(c) + 1];
232 Kokkos::parallel_for(
233 "amr::gs_mom", Kokkos::RangePolicy<ExecSpace>(a0, a1), KOKKOS_LAMBDA(const Index k) {
234 const Index i = idx(k);
235 double off, d;
236 faceCsrOffDiag(A, i, u, off, d);
237 u(i) = faceCsrPointUpdate(b(i), off, d, u(i), omega);
238 });
239 };
240 for (int c = 0; c < col.nColors; ++c)
241 colorPass(c); // forward
242 for (int c = col.nColors - 2; c >= 0; --c)
243 colorPass(c); // reverse (last colour not repeated)
244}
245
246// ===========================================================================
247// MomentumMG — Galerkin geometric multigrid for the momentum operator.
248//
249// The cut-cell momentum operator carries the ξ-polynomial Dirichlet overlay and its
250// D_rescale row scaling, so a *rediscretised* coarse operator (the openness-Helmholtz
251// attempt) mismatches it and makes a poor preconditioner. Instead the coarse operators are
252// built by **Galerkin coarsening** A_c = R·A·P of the exact assembled fine CSR: R = volume
253// average over a coarse cell's children, P = piecewise-constant injection (the same transfer
254// pair the pressure MG uses). This is consistent with the fine operator by construction — it
255// inherits the cut-cell stencil and row scaling, and a coarse cell whose children are all
256// solid (identity rows) stays an identity row (ε-solid-on-coarse emerges for free). The
257// hierarchy is the uniformly-coarsened octree; the smoother is jacobiMom, the residual
258// restriction / correction prolongation are the shared restrictField / prolongAdd.
259// Used as the momentum BiCGStab preconditioner ⇒ the iteration count stays ~flat with N.
260// ===========================================================================
261template <unsigned Bits = 21u>
263 public:
265 using M = typename Octree::M;
266 using Code = typename Octree::Code;
267
270 void build(const Octree& finest, const std::vector<double>& diag0,
271 const std::vector<Index>& start0, const std::vector<Index>& nbr0,
272 const std::vector<double>& coef0) {
273 octs_.clear();
274 octs_.push_back(finest);
275 for (;;) {
276 Octree c = octs_.back();
277 Index merged = c.coarsenIf([](Code, unsigned) { return true; });
278 if (merged == 0 || c.numLeaves() == octs_.back().numLeaves())
279 break;
280 octs_.push_back(c);
281 if (c.numLeaves() == 1)
282 break;
283 }
284 const std::size_t nl = octs_.size();
285 levels_.clear();
286 levels_.resize(nl);
287
288 std::vector<double> hdiag = diag0, hcoef = coef0;
289 std::vector<Index> hstart = start0, hnbr = nbr0;
290 uploadLevel(0, hdiag, hstart, hnbr, hcoef);
291
292 for (std::size_t L = 0; L + 1 < nl; ++L) {
293 const Octree& f = octs_[L];
294 const Octree& c = octs_[L + 1];
295 const Index nf = f.numLeaves(), nc = c.numLeaves();
296 std::vector<Index> c2p(static_cast<std::size_t>(nf));
297 std::vector<Index> cnt(static_cast<std::size_t>(nc), 0);
298 for (Index i = 0; i < nf; ++i) {
299 Code par = M::from_code(f.code(i)).ancestor(f.level(i) + 1).code();
300 Index p = c.find(par);
301 c2p[static_cast<std::size_t>(i)] = p;
302 if (p >= 0)
303 ++cnt[static_cast<std::size_t>(p)];
304 }
305 std::vector<Index> cstart(static_cast<std::size_t>(nc) + 1, 0);
306 for (Index p = 0; p < nc; ++p)
307 cstart[static_cast<std::size_t>(p) + 1] =
308 cstart[static_cast<std::size_t>(p)] + cnt[static_cast<std::size_t>(p)];
309 std::vector<Index> cidx(static_cast<std::size_t>(nf));
310 std::vector<Index> cur(cstart.begin(), cstart.end() - 1);
311 for (Index i = 0; i < nf; ++i) {
312 Index p = c2p[static_cast<std::size_t>(i)];
313 if (p >= 0)
314 cidx[static_cast<std::size_t>(cur[static_cast<std::size_t>(p)]++)] = i;
315 }
316 levels_[L].c2p = toDevice(c2p, "mmg_c2p");
317 levels_[L].childStart = toDevice(cstart, "mmg_cstart");
318 levels_[L].childIdx = toDevice(cidx, "mmg_cidx");
319
320 // Galerkin A_c[p][q] = (1/n_ch[p]) Σ_{i child of p} ( A[i] entries mapped to parents ).
321 std::vector<std::map<Index, double>> acc(static_cast<std::size_t>(nc));
322 for (Index i = 0; i < nf; ++i) {
323 Index p = c2p[static_cast<std::size_t>(i)];
324 if (p < 0)
325 continue;
326 const double w = 1.0 / static_cast<double>(cnt[static_cast<std::size_t>(p)]);
327 acc[static_cast<std::size_t>(p)][p] += w * hdiag[static_cast<std::size_t>(i)];
328 for (Index k = hstart[static_cast<std::size_t>(i)];
330 Index q = c2p[static_cast<std::size_t>(hnbr[static_cast<std::size_t>(k)])];
331 if (q < 0)
332 continue;
333 acc[static_cast<std::size_t>(p)][q] += w * hcoef[static_cast<std::size_t>(k)];
334 }
335 }
336 std::vector<double> cdiag(static_cast<std::size_t>(nc), 0.0);
337 std::vector<Index> cs(static_cast<std::size_t>(nc) + 1, 0);
338 for (Index p = 0; p < nc; ++p) {
339 int off = 0;
340 for (auto& e : acc[static_cast<std::size_t>(p)]) {
341 if (e.first == p)
342 cdiag[static_cast<std::size_t>(p)] = e.second;
343 else
344 ++off;
345 }
346 cs[static_cast<std::size_t>(p) + 1] = cs[static_cast<std::size_t>(p)] + off;
347 }
348 std::vector<Index> cn(static_cast<std::size_t>(cs[static_cast<std::size_t>(nc)]));
349 std::vector<double> ccoef(static_cast<std::size_t>(cs[static_cast<std::size_t>(nc)]));
350 for (Index p = 0; p < nc; ++p) {
351 Index k = cs[static_cast<std::size_t>(p)];
352 for (auto& e : acc[static_cast<std::size_t>(p)])
353 if (e.first != p) {
354 cn[static_cast<std::size_t>(k)] = e.first;
355 ccoef[static_cast<std::size_t>(k)] = e.second;
356 ++k;
357 }
358 }
359 uploadLevel(L + 1, cdiag, cs, cn, ccoef);
360 hdiag = cdiag;
361 hstart = cs;
362 hnbr = cn;
363 hcoef = ccoef;
364 }
365 for (auto& lv : levels_) {
366 lv.x = View<double>("mmg_x", static_cast<std::size_t>(lv.op.n));
367 lv.b = View<double>("mmg_b", static_cast<std::size_t>(lv.op.n));
368 lv.res = View<double>("mmg_res", static_cast<std::size_t>(lv.op.n));
369 lv.tmp = View<double>("mmg_tmp", static_cast<std::size_t>(lv.op.n));
370 }
371 }
372
375 void setGaussSeidel(bool on) { useGS_ = on; }
376
378 void vcycle(int pre = 2, int post = 2, int bottom = 30, double omega = 0.7, std::size_t L = 0) {
379 Level& lv = levels_[L];
381 if (L + 1 == levels_.size()) {
382 smooth(lv, bottom, omega);
383 return;
384 }
385 smooth(lv, pre, omega);
386 residualMom(lv.op, View<const double>(lv.x), bc, lv.res);
387 Level& cl = levels_[L + 1];
388 restrictField(lv.childStart, lv.childIdx, View<const double>(lv.res), cl.b, cl.op.n);
389 Kokkos::deep_copy(cl.x, 0.0);
390 vcycle(pre, post, bottom, omega, L + 1);
391 prolongAdd(lv.c2p, View<const double>(cl.x), lv.x, lv.op.n);
392 smooth(lv, post, omega);
393 }
394
395 std::size_t numLevels() const { return levels_.size(); }
396 Index numLeaves(std::size_t L = 0) const { return levels_[L].op.n; }
397 View<double> x(std::size_t L = 0) { return levels_[L].x; }
398 View<double> b(std::size_t L = 0) { return levels_[L].b; }
399 const MomentumOp& op(std::size_t L = 0) const { return levels_[L].op; }
400
401 private:
402 struct Level {
403 MomentumOp op;
404 View<double> x, b, res, tmp;
405 View<Index> c2p, childStart, childIdx;
406 Coloring col;
407 };
408 void smooth(Level& lv, int sweeps, double omega) {
410 if (useGS_)
411 // Multicolour GS is stable undamped on this diagonally-dominant operator (ρ/dt + 6μ + FOU >
412 // 0); the passed omega is Jacobi's damping limit (~0.7) and would needlessly weaken GS, so
413 // use 1.0.
414 for (int s = 0; s < sweeps; ++s)
415 multicolorGSMom(lv.op, lv.x, bc, lv.col, 1.0);
416 else
417 for (int s = 0; s < sweeps; ++s)
418 jacobiMom(lv.op, lv.x, bc, lv.tmp, omega);
419 }
420 void uploadLevel(std::size_t L, const std::vector<double>& diag, const std::vector<Index>& start,
421 const std::vector<Index>& nbr, const std::vector<double>& coef) {
422 Level& lv = levels_[L];
423 lv.op.n = static_cast<Index>(diag.size());
424 lv.op.diag = toDevice(diag, "mmg_diag");
425 lv.op.faceStart = toDevice(start, "mmg_start");
426 lv.op.faceNbr = toDevice(nbr, "mmg_nbr");
427 lv.op.faceCoef = toDevice(coef, "mmg_coef");
428 lv.col = greedyColoring(start, nbr, lv.op.n);
429 }
430 std::vector<Octree> octs_;
431 std::vector<Level> levels_;
432 bool useGS_ = false; // multicolour Gauss–Seidel smoother (opt-in; default weighted Jacobi)
433};
434
435// ---------------------------------------------------------------------------
436// Jacobi-preconditioned BiCGStab for the (non-symmetric) momentum operator. Reuses the
437// device matvec + Kokkos reductions; the preconditioner is `jacPre` damped-Jacobi sweeps
438// of A (diagonal-dominant ⇒ a cheap, effective smoother-preconditioner). Robust where
439// plain Jacobi stalls (large dt / weak reaction term).
440// ---------------------------------------------------------------------------
441template <unsigned Bits = 21u>
443 public:
444 void setJacobi(int preSweeps, double omega) {
445 jacPre_ = preSweeps;
446 omega_ = omega;
447 }
448
456 precFn_ = std::move(fn);
457 }
458
462 ensure(op.n);
463 for (int s = 0; s < sweeps; ++s)
464 jacobiMom(op, u, b, tmp_, omega_);
465 residualMom(op, View<const double>(u), b, r_);
466 return std::sqrt(dotPlain(View<const double>(r_), View<const double>(r_), op.n));
467 }
468
469 struct Result {
470 int iters = 0;
471 double res0 = 0.0;
472 double res = 0.0;
473 };
474
483 int maxIters = 200, double tol = 1e-8) {
484 const Index n = op.n;
485 ensure(n);
486 Result R;
487 residualMom(op, View<const double>(u), b, r_);
488 R.res0 = std::sqrt(dotPlain(View<const double>(r_), View<const double>(r_), n));
489 if (R.res0 == 0.0)
490 return R;
491 double rnorm = R.res0;
492 int it = 0;
493 for (; it < maxIters; ++it) {
494 applyPrec(op, r_, phat_); // phat = M⁻¹ r
495 axpy(u, 1.0, View<const double>(phat_), n); // u += phat
496 residualMom(op, View<const double>(u), b, r_);
497 rnorm = std::sqrt(dotPlain(View<const double>(r_), View<const double>(r_), n));
498 if (rnorm <= tol * R.res0) {
499 ++it;
500 break;
501 }
502 }
503 R.iters = it;
504 R.res = rnorm;
505 return R;
506 }
507
511 int maxIters = 500, double tol = 1e-10) {
512 const Index n = op.n;
513 ensure(n);
514 Result R;
515 // r = b − A u
516 residualMom(op, View<const double>(u), b, r_);
517 Kokkos::deep_copy(rhat_, r_); // shadow residual
518 R.res0 = std::sqrt(dotPlain(View<const double>(r_), View<const double>(r_), n));
519 if (R.res0 == 0.0)
520 return R;
521 double rho = 1, alpha = 1, omega = 1;
522 Kokkos::deep_copy(v_, 0.0);
523 Kokkos::deep_copy(p_, 0.0);
524 double rnorm = R.res0;
525 int it = 0;
526 for (; it < maxIters; ++it) {
527 double rhoNew = dotPlain(View<const double>(rhat_), View<const double>(r_), n);
528 if (rhoNew == 0.0)
529 break;
530 double beta = (rhoNew / rho) * (alpha / omega);
531 // p = r + beta (p − omega v)
533 applyPrec(op, p_, phat_); // phat = M^{-1} p
534 applyMom(op, View<const double>(phat_), v_);
535 double rhatV = dotPlain(View<const double>(rhat_), View<const double>(v_), n);
536 alpha = rhoNew / rhatV;
537 // s = r − alpha v
538 Kokkos::deep_copy(s_, r_);
539 axpy(s_, -alpha, View<const double>(v_), n);
540 double snorm = std::sqrt(dotPlain(View<const double>(s_), View<const double>(s_), n));
541 if (snorm <= tol * R.res0) {
542 axpy(u, alpha, View<const double>(phat_), n); // u += alpha phat
543 rnorm = snorm;
544 ++it;
545 break;
546 }
547 applyPrec(op, s_, shat_); // shat = M^{-1} s
548 applyMom(op, View<const double>(shat_), t_);
549 double tt = dotPlain(View<const double>(t_), View<const double>(t_), n);
550 omega = (tt != 0.0) ? dotPlain(View<const double>(t_), View<const double>(s_), n) / tt : 0.0;
551 // u += alpha phat + omega shat
552 axpy(u, alpha, View<const double>(phat_), n);
553 axpy(u, omega, View<const double>(shat_), n);
554 // r = s − omega t
555 Kokkos::deep_copy(r_, s_);
556 axpy(r_, -omega, View<const double>(t_), n);
557 rnorm = std::sqrt(dotPlain(View<const double>(r_), View<const double>(r_), n));
558 if (rnorm <= tol * R.res0) {
559 ++it;
560 break;
561 }
562 rho = rhoNew;
563 if (omega == 0.0)
564 break;
565 }
566 R.iters = it;
567 R.res = rnorm;
568 return R;
569 }
570
571 private:
572 // z = M^{-1} v : the generic MG preconditioner if set, else `jacPre_` damped-Jacobi sweeps of
573 // A z = v starting from z = 0.
574 void applyPrec(const MomentumOp& op, View<double> v, View<double> z) {
575 if (precFn_) {
576 precFn_(View<const double>(v), z);
577 return;
578 }
579 Kokkos::deep_copy(z, 0.0);
580 if (jacPre_ <= 0) { // no preconditioner ⇒ identity
581 Kokkos::deep_copy(z, v);
582 return;
583 }
584 for (int s = 0; s < jacPre_; ++s)
585 jacobiMom(op, z, View<const double>(v), tmp_, omega_);
586 }
587 void ensure(Index n) {
588 if (r_.extent(0) == static_cast<std::size_t>(n))
589 return;
590 auto mk = [&](const char* l) { return View<double>(l, static_cast<std::size_t>(n)); };
591 r_ = mk("mom_r");
592 rhat_ = mk("mom_rhat");
593 p_ = mk("mom_p");
594 phat_ = mk("mom_phat");
595 v_ = mk("mom_v");
596 s_ = mk("mom_s");
597 shat_ = mk("mom_shat");
598 t_ = mk("mom_t");
599 tmp_ = mk("mom_tmp");
600 }
601
602 View<double> r_, rhat_, p_, phat_, v_, s_, shat_, t_, tmp_;
603 int jacPre_ = 2;
604 double omega_ = 0.7;
605 std::function<void(View<const double>, View<double>)> precFn_; // generic z = M^{-1} r
606};
607
608} // namespace peclet::core::amr
609
610#endif // PECLET_CORE_HAVE_MORTON
611#endif // PECLET_CORE_AMR_MOMENTUM_HPP
morton::Morton< Dim, Bits > M
typename M::code_type Code
Index numLeaves(std::size_t L=0) const
Definition momentum.hpp:396
BlockOctree< 3, Bits > Octree
Definition momentum.hpp:264
void setGaussSeidel(bool on)
Opt-in: use multicolour Gauss–Seidel as the smoother (per-level colouring built at build) instead of ...
Definition momentum.hpp:375
void vcycle(int pre=2, int post=2, int bottom=30, double omega=0.7, std::size_t L=0)
One V-cycle on level L solving A u = b (correction scheme).
Definition momentum.hpp:378
View< double > x(std::size_t L=0)
Definition momentum.hpp:397
std::size_t numLevels() const
Definition momentum.hpp:395
const MomentumOp & op(std::size_t L=0) const
Definition momentum.hpp:399
View< double > b(std::size_t L=0)
Definition momentum.hpp:398
typename Octree::Code Code
Definition momentum.hpp:266
void build(const Octree &finest, const std::vector< double > &diag0, const std::vector< Index > &start0, const std::vector< Index > &nbr0, const std::vector< double > &coef0)
Build the Galerkin hierarchy from the finest octree + the assembled fine operator CSR (diag + face CS...
Definition momentum.hpp:270
double solveJacobi(const MomentumOp &op, View< double > u, View< const double > b, int sweeps)
Plain weighted-Jacobi solve (the simple parallel mirror of the host GS smoother): sweeps damped-Jacob...
Definition momentum.hpp:461
Result solveBiCGStab(const MomentumOp &op, View< double > u, View< const double > b, int maxIters=500, double tol=1e-10)
Jacobi-preconditioned BiCGStab solve of A u = b in place.
Definition momentum.hpp:510
void setPreconditioner(std::function< void(View< const double >, View< double >)> fn)
Set a generic preconditioner z = M⁻¹ r (a host callable that launches device kernels) — the multigrid...
Definition momentum.hpp:455
void setJacobi(int preSweeps, double omega)
Definition momentum.hpp:444
Result solveDefectCorrection(const MomentumOp &op, View< double > u, View< const double > b, int maxIters=200, double tol=1e-8)
MG-preconditioned defect-correction (Richardson) solve of A u = b in place: u ← u + M⁻¹(b − A u),...
Definition momentum.hpp:482
void axpy(View< double > y, double a, View< const double > x, Index n)
y += a·x
Definition pcg.hpp:94
void residualMom(const MomentumOp &op, View< const double > u, View< const double > b, View< double > res)
res = b − A u.
Definition momentum.hpp:84
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 ...
Definition momentum.hpp:225
void bicgPUpdate(View< double > p, View< const double > r, View< const double > v, double beta, double omega, Index n)
BiCGStab direction update: p = r + β(p − ω v).
Definition momentum.hpp:118
void prolongAdd(View< const Index > c2p, View< const double > coarse, View< double > fine, Index nFine)
Prolong (piecewise-constant) + correct: fine(i) += coarse(c2p(i)).
Definition multigrid.hpp:81
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...
Definition momentum.hpp:155
MORTON_HD void faceCsrOffDiag(const Op &op, Index i, const U &u, double &off, double &d)
Off-diagonal sum and the (advection-inclusive) diagonal for the point smoothers: out off = Σ coef·u[n...
Definition face_csr.hpp:78
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).
Definition momentum.hpp:94
MORTON_HD double faceCsrPointUpdate(double b_i, double off, double d, double uOld, double omega)
The damped point update used by both Jacobi and (multicolour) Gauss–Seidel: returns the new u_i given...
Definition face_csr.hpp:93
void applyMom(const MomentumOp &op, View< const double > u, View< double > Au)
Au = A u (cut-cell operator + optional implicit-FOU advection).
Definition momentum.hpp:77
MORTON_HD double faceCsrApplyRow(const Op &op, Index i, const U &u)
(A u)_i — one assembled-operator row.
Definition face_csr.hpp:62
double dotPlain(View< const double > a, View< const double > b, Index n)
Plain (unweighted) dot product.
Definition momentum.hpp:109
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).
Definition multigrid.hpp:45
FaceCsrOpT< View< const double >, View< const Index > > momView(const MomentumOp &op)
View the assembled momentum operator through the shared, backend-agnostic FaceCsrOpT,...
Definition momentum.hpp:61
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).
Definition view.hpp:44
Kokkos::View< T *, MemSpace > View
1D device array.
Definition view.hpp:26
std::int64_t Index
Signed index type for grids and particles (supersedes block_decomposer's long int IndxT).
Definition types.hpp:15
A graph colouring of a face CSR: cells grouped by colour.
Definition momentum.hpp:138
std::vector< Index > hStart
Definition momentum.hpp:139
Assembled momentum operator on the device: (A u)_i = diag_i u_i + Σ coef·u[nbr], with an optional imp...
Definition momentum.hpp:43
View< double > diag
size n
Definition momentum.hpp:44
View< Index > advStart
face-geom CSR row offsets, size n+1
Definition momentum.hpp:52
View< Index > faceNbr
neighbour leaf per off-diagonal, size nnz
Definition momentum.hpp:46
View< Index > advNbr
face-geom neighbour per face, size nFaces
Definition momentum.hpp:53
View< double > advDiag
per-cell outflow (diagonal) advection weight, size n
Definition momentum.hpp:51
View< double > faceCoef
off-diagonal coefficient, size nnz
Definition momentum.hpp:47
View< Index > faceStart
CSR row offsets, size n+1.
Definition momentum.hpp:45
View< double > advCoef
per-face inflow advection coefficient (0 on outflow/solid faces)
Definition momentum.hpp:54