core 0.5.0
Shared MPI block decomposition + asynchronous ghost-layer exchange (header-only C++20)
Loading...
Searching...
No Matches
cf_scheme.hpp
Go to the documentation of this file.
1// core — pluggable coarse/fine (2:1) interface schemes for the collocated AMR flow solver.
2//
3// The graded-accuracy limiter measured in docs/amr_collocated_projection.md (addendum): the
4// standard two-point C/F flux is 1st-order at level boundaries, and BOTH the aperture and the
5// ghost projection inherit it identically (+9% graded drag offset on the dilute sphere,
6// scheme-independent). This header makes the C/F treatment a pluggable SCHEME and provides the
7// Martin–Cartwright/Martin–Colella tangential-quadratic closure (CfScheme::quadratic — the same
8// coarseStar substitution AmrPoisson::applyLaplacianQuad uses) for every operator the STEADY
9// solution feels:
10//
11// D — the ½/½ face-average divergence constraint (buildCfDivDelta, vector → scalar)
12// G — the ABC cell gradient (−∇pⁿ predictor + cell correction) (buildCfGradDelta, scalar → 3)
13// ∇² — the momentum (velocity) diffusion at regular fluid rows (buildCfLapDelta, scalar → scalar)
14//
15// Everything is the LINEAR substitution "coarse-side value → coarse*", where coarse* is the
16// scheme's interpolation of the coarse cell at the fine cell's tangential position. So each
17// operator's (quad − standard) difference is a precomputed CSR overlay over the level-boundary
18// rows: built ONCE on the host (shared verbatim by the host oracle and the device — parity by
19// construction), applied as one extra SpMV.
20//
21// STEADY-STATE PLACEMENT (the (1,2)-mixed philosophy, flow-validated): at the projection's fixed
22// point φ→0, so the pressure MATRIX C/F order does not move the steady solution — the matrix (and
23// the whole MG hierarchy / PCG / ghost BiCGStab) stays on the standard consistent operator, and
24// the scheme enters through (a) the RHS divergence, (b) the pressure gradients, (c) the momentum
25// operator (as a lagged deferred-correction RHS term, the same pattern as the implicit-FOU/SOU
26// split and Multigrid::solveQuad). The per-step operator mismatch converges through the
27// pseudo-transient stepping; the steady equations carry the 2nd-order C/F closure exactly.
28//
29// EXTENDING: add a value to CfScheme and a branch in cfAppendStencil — every operator delta picks
30// it up. A scheme is fully described by the linear stencil it substitutes for the coarse-side
31// value of one directed C/F sub-face.
32//
33// Robustness gating (per tangential axis, falling back to the raw coarse value): the tangential
34// coarse neighbours must exist, be same-level, be FLUID (the caller's predicate — never lean on a
35// decoupled/held solid value), and the tangential faces must be sufficiently open (openness ≥ 0.5,
36// matching applyLaplacianQuad's gate). With the finest-band contract, level boundaries sit in
37// smooth flow and the gates are inert there.
38#ifndef PECLET_CORE_AMR_CF_SCHEME_HPP
39#define PECLET_CORE_AMR_CF_SCHEME_HPP
40
41#ifdef PECLET_CORE_HAVE_MORTON
42
43#include <array>
44#include <cstdint>
45#include <utility>
46#include <vector>
47
51
52namespace peclet::core::amr {
53
55enum class CfScheme : int {
56 standard = 0,
57 quadratic = 1,
58};
59
61struct CfCsr {
62 std::vector<Index> start;
63 std::vector<Index> slot;
64 std::vector<double> coef;
65};
66
68struct CfCompCsr {
69 std::vector<Index> start;
70 std::vector<Index> slot;
71 std::vector<double> coef;
72 std::vector<int8_t> comp;
73};
74
75namespace detail {
76
82template <unsigned Bits, class Ent, class FluidFn>
84 std::vector<Ent>& out, Index coarse, Index fine, int axis,
85 double scale, FluidFn&& fluidOk, CfScheme scheme,
86 const Ent& proto = Ent{}) {
87 if (scheme != CfScheme::quadratic)
88 return;
89 auto bc = t.bounds(coarse);
90 auto bf = t.bounds(fine);
91 const double H = ap.cellWidth(coarse);
92 const double sc = static_cast<double>(Index(1) << t.level(coarse));
93 const double sf = static_cast<double>(Index(1) << t.level(fine));
94 auto push = [&](Index cell, double w) {
95 Ent e = proto;
96 e.cell = cell;
97 e.w = w;
98 out.push_back(e);
99 };
100 for (int tt = 0; tt < 3; ++tt) {
101 if (tt == axis)
102 continue;
103 const double dt = ((static_cast<double>(bf[0][tt]) + 0.5 * sf) -
104 (static_cast<double>(bc[0][tt]) + 0.5 * sc)) *
105 ap.h0();
106 // Tangential samples at the coarse cell's ± neighbours. Same-level neighbours contribute
107 // directly. A FINER neighbour (an island corner/edge: the region across the tangential face
108 // is refined — by 2:1 exactly one level finer) is sampled by the volume average of the 2^Dim
109 // leaves covering the coarse-size region: a 2nd-order sample of the region-center value, so
110 // the corner rows keep the quadratic closure instead of falling back to the raw coarse value
111 // (the old P5b-style skip, which left them locally 1st-order). A COARSER neighbour (or any
112 // non-fluid / irregular cover) still falls back to the skip (robust).
113 struct Samp {
114 Index cell[1 << 3];
115 double w[1 << 3];
116 int n = 0;
117 bool ok = false;
118 };
119 auto sampleTangential = [&](int dir) {
120 Samp sm;
121 const Index nb = ap.periodicNeighbor(coarse, tt, dir);
122 if (nb < 0)
123 return sm;
124 if (t.level(nb) == t.level(coarse)) {
125 if (!fluidOk(nb))
126 return sm;
127 sm.cell[0] = nb;
128 sm.w[0] = 1.0;
129 sm.n = 1;
130 sm.ok = true;
131 return sm;
132 }
133 if (t.level(nb) + 1 != t.level(coarse))
134 return sm; // coarser neighbour: keep the fallback
135 // Finer neighbour: enumerate the 2^Dim children covering the coarse-size region.
136 auto bc2 = t.bounds(coarse);
137 const auto sc2 = typename BlockOctree<3, Bits>::Coord(
138 typename BlockOctree<3, Bits>::Coord(1) << t.level(coarse));
139 const auto sh = typename BlockOctree<3, Bits>::Coord(sc2 >> 1);
140 long ext[3];
141 for (int d = 0; d < 3; ++d)
142 ext[d] = static_cast<long>(t.brick()[d]) * (1L << t.lmax());
143 std::array<typename BlockOctree<3, Bits>::Coord, 3> lo = bc2[0];
144 const long shifted = static_cast<long>(lo[tt]) + (dir > 0 ? static_cast<long>(sc2) : -static_cast<long>(sc2));
145 lo[tt] = static_cast<typename BlockOctree<3, Bits>::Coord>(((shifted % ext[tt]) + ext[tt]) %
146 ext[tt]);
147 for (int oct2 = 0; oct2 < (1 << 3); ++oct2) {
148 std::array<typename BlockOctree<3, Bits>::Coord, 3> q = lo;
149 for (int d = 0; d < 3; ++d)
150 if ((oct2 >> d) & 1)
151 q[d] = static_cast<typename BlockOctree<3, Bits>::Coord>(
152 (static_cast<long>(q[d]) + static_cast<long>(sh)) % ext[d]);
153 const Index ch = t.find(q);
154 if (ch < 0 || t.level(ch) + 1 != t.level(coarse) || !fluidOk(ch))
155 return Samp{}; // irregular cover or solid child: fall back
156 sm.cell[sm.n] = ch;
157 sm.w[sm.n] = 1.0 / static_cast<double>(1 << 3);
158 ++sm.n;
159 }
160 sm.ok = true;
161 return sm;
162 };
163 const Samp sp = sampleTangential(+1);
164 const Samp smi = sampleTangential(-1);
165 if (!sp.ok || !smi.ok)
166 continue;
167 if (ap.faceOpenness(coarse, tt, +1) < 0.5 || ap.faceOpenness(coarse, tt, -1) < 0.5)
168 continue;
169 const double cUp = dt / (2.0 * H) + 0.5 * dt * dt / (H * H);
170 const double cUm = -dt / (2.0 * H) + 0.5 * dt * dt / (H * H);
171 const double cUc = -dt * dt / (H * H);
172 push(coarse, scale * cUc);
173 for (int k2 = 0; k2 < sp.n; ++k2)
174 push(sp.cell[k2], scale * cUp * sp.w[k2]);
175 for (int k2 = 0; k2 < smi.n; ++k2)
176 push(smi.cell[k2], scale * cUm * smi.w[k2]);
177 }
178}
179
180struct ScalarEnt {
182 double w = 0.0;
183};
184struct CompEnt {
186 double w = 0.0;
188};
189
190template <class Ent, class Csr>
191inline void compactCsr(const std::vector<std::vector<Ent>>& per, Csr& out, Index n) {
192 out.start.assign(static_cast<std::size_t>(n) + 1, 0);
193 for (Index i = 0; i < n; ++i)
194 out.start[static_cast<std::size_t>(i) + 1] =
195 out.start[static_cast<std::size_t>(i)] +
196 static_cast<Index>(per[static_cast<std::size_t>(i)].size());
197 const Index nz = out.start[static_cast<std::size_t>(n)];
198 out.slot.resize(static_cast<std::size_t>(nz));
199 out.coef.resize(static_cast<std::size_t>(nz));
200 Index k = 0;
201 for (Index i = 0; i < n; ++i)
202 for (const auto& e : per[static_cast<std::size_t>(i)]) {
203 out.slot[static_cast<std::size_t>(k)] = e.cell;
204 out.coef[static_cast<std::size_t>(k)] = e.w;
205 ++k;
206 }
207}
208
209} // namespace detail
210
216template <unsigned Bits, class RowFn, class FluidFn>
218 double factor, RowFn&& rowOk, FluidFn&& fluidOk, CfScheme scheme) {
219 const Index n = t.numLeaves();
220 std::vector<std::vector<detail::ScalarEnt>> per(static_cast<std::size_t>(n));
221 for (Index i = 0; i < n; ++i) {
222 if (!rowOk(i))
223 continue;
224 const unsigned Li = t.level(i);
225 const double invV = 1.0 / ap.cellVolume(i);
226 ap.forEachFaceNeighbor(i, [&](Index j, Real c, int axis, double a) {
227 const unsigned Lj = t.level(j);
228 if (Lj == Li)
229 return;
230 const Index coarse = (Lj > Li) ? j : i;
231 const Index fine = (Lj > Li) ? i : j;
232 const double scale = factor * invV * (a * c) * ((Lj > Li) ? 1.0 : -1.0);
233 detail::cfAppendStencil(ap, t, per[static_cast<std::size_t>(i)], coarse, fine, axis, scale,
234 fluidOk, scheme);
235 });
236 }
237 CfCsr csr;
239 return csr;
240}
241
252template <unsigned Bits, class RowFn, class FluidFn>
254 RowFn&& rowOk, FluidFn&& fluidOk, CfScheme scheme) {
255 const Index n = t.numLeaves();
256 std::vector<std::vector<detail::CompEnt>> per(static_cast<std::size_t>(n));
257 if (scheme != CfScheme::standard) {
258 for (Index i = 0; i < n; ++i) {
259 if (!rowOk(i))
260 continue;
261 const unsigned Li = t.level(i);
262 const double invV = 1.0 / ap.cellVolume(i);
263 ap.forEachFaceFull(i, [&](Index j, int axis, int dir, double area, double, double alpha) {
264 const unsigned Lj = t.level(j);
265 if (Lj == Li)
266 return;
267 const Index coarse = (Lj > Li) ? j : i;
268 const Index fine = (Lj > Li) ? i : j;
269 const double H = ap.cellWidth(coarse), h = ap.cellWidth(fine);
270 const double d = 0.5 * (H + h);
271 const double wF = (0.5 * H) / d, wC = (0.5 * h) / d;
272 const double scale = invV * alpha * area * static_cast<double>(dir);
274 proto.comp = static_cast<int8_t>(axis);
275 auto& row = per[static_cast<std::size_t>(i)];
277 eF.cell = fine;
278 eF.w = scale * (wF - 0.5);
279 eC.cell = coarse;
280 eC.w = scale * (wC - 0.5);
281 row.push_back(eF);
282 row.push_back(eC);
283 detail::cfAppendStencil(ap, t, row, coarse, fine, axis, scale * wC, fluidOk, scheme,
284 proto);
285 });
286 }
287 }
290 csr.comp.resize(csr.slot.size());
291 Index k = 0;
292 for (Index i = 0; i < n; ++i)
293 for (const auto& e : per[static_cast<std::size_t>(i)])
294 csr.comp[static_cast<std::size_t>(k++)] = e.comp;
295 return csr;
296}
297
308template <unsigned Bits, class RowFn, class FluidFn>
309inline std::array<CfCsr, 3> buildCfGradDelta(const AmrPoisson<3, Bits>& ap,
311 FluidFn&& fluidOk, CfScheme scheme) {
312 const Index n = t.numLeaves();
313 std::array<std::vector<std::vector<detail::ScalarEnt>>, 3> per;
314 for (int a = 0; a < 3; ++a)
315 per[static_cast<std::size_t>(a)].resize(static_cast<std::size_t>(n));
316 if (scheme != CfScheme::standard) {
317 for (Index i = 0; i < n; ++i) {
318 if (!rowOk(i))
319 continue;
320 const unsigned Li = t.level(i);
321 // Per axis/side: open-face count, the side's (uniform) center-to-center distance, and
322 // whether the side has a level mismatch. slot [axis][side], side 0 = +, 1 = −.
323 int cnt[3][2] = {};
324 double sdist[3][2] = {};
325 bool cf[3][2] = {};
326 ap.forEachFaceFull(i, [&](Index j, int axis, int dir, double, double dist, double alpha) {
327 if (alpha <= 1e-12)
328 return;
329 const int s = (dir > 0) ? 0 : 1;
330 ++cnt[axis][s];
331 sdist[axis][s] = dist;
332 if (t.level(j) != Li)
333 cf[axis][s] = true;
334 });
335 for (int a = 0; a < 3; ++a) {
336 if (!cf[a][0] && !cf[a][1])
337 continue; // no level mismatch on this axis: standard ½/½ is centered
338 if (cnt[a][0] == 0 || cnt[a][1] == 0)
339 continue; // one-sided (closed) axis: leave the standard treatment
340 const double dp = sdist[a][0], dm = sdist[a][1];
341 const double wp = dm / (dp + dm), wm = dp / (dp + dm);
342 auto& row = per[static_cast<std::size_t>(a)][static_cast<std::size_t>(i)];
343 ap.forEachFaceFull(i, [&](Index j, int axis, int dir, double, double dist, double alpha) {
344 if (axis != a || alpha <= 1e-12)
345 return;
346 const int s = (dir > 0) ? 0 : 1;
347 const double w = (s == 0) ? wp : wm;
348 const double inv = 1.0 / static_cast<double>(cnt[a][s]);
349 const double gsgn = (dir > 0) ? 1.0 : -1.0; // coefficient of f_j in g
350 // (a) side reweighting of the STANDARD face gradient: (w − ½)·g/cnt.
351 const double rw = (w - 0.5) * inv * gsgn / dist;
352 if (rw != 0.0) {
353 row.push_back({j, rw});
354 row.push_back({i, -rw});
355 }
356 // (b) coarse* substitution inside the C/F face gradients, at the NEW side weight.
357 const unsigned Lj = t.level(j);
358 if (Lj == Li)
359 return;
360 const Index coarse = (Lj > Li) ? j : i;
361 const Index fine = (Lj > Li) ? i : j;
362 const double ssgn = (Lj > Li) ? gsgn : -gsgn; // sign of the substituted value in g
363 detail::cfAppendStencil(ap, t, row, coarse, fine, a, w * inv * ssgn / dist, fluidOk,
364 scheme);
365 });
366 }
367 }
368 }
369 std::array<CfCsr, 3> out;
370 for (int a = 0; a < 3; ++a)
371 detail::compactCsr(per[static_cast<std::size_t>(a)], out[static_cast<std::size_t>(a)], n);
372 return out;
373}
374
390
391template <unsigned Bits, class FluidFn>
393 FluidFn&& fluidOk, CfScheme scheme) {
394 const Index n = t.numLeaves();
395 Index nSlots = 0;
396 for (Index i = 0; i < n; ++i)
397 ap.forEachFaceFull(i, [&](Index, int, int, double, double, double) { ++nSlots; });
398 CfUfDelta d;
399 d.vel.start.assign(static_cast<std::size_t>(nSlots) + 1, 0);
400 d.phi.start.assign(static_cast<std::size_t>(nSlots) + 1, 0);
401 if (scheme == CfScheme::standard)
402 return d;
403 Index slot = 0;
404 for (Index i = 0; i < n; ++i) {
405 const unsigned Li = t.level(i);
406 ap.forEachFaceFull(i, [&](Index j, int axis, int dir, double, double dist, double) {
407 d.vel.start[static_cast<std::size_t>(slot) + 1] = d.vel.start[static_cast<std::size_t>(slot)];
408 d.phi.start[static_cast<std::size_t>(slot) + 1] = d.phi.start[static_cast<std::size_t>(slot)];
409 const unsigned Lj = t.level(j);
410 if (Lj != Li && fluidOk(i) && fluidOk(j)) {
411 const Index coarse = (Lj > Li) ? j : i;
412 const Index fine = (Lj > Li) ? i : j;
413 const double H = ap.cellWidth(coarse), h = ap.cellWidth(fine);
414 const double wF = (0.5 * H) / dist, wC = (0.5 * h) / dist;
415 std::vector<detail::CompEnt> ve;
417 proto.comp = static_cast<int8_t>(axis);
419 eF.cell = fine;
420 eF.w = wF - 0.5;
421 eC.cell = coarse;
422 eC.w = wC - 0.5;
423 ve.push_back(eF);
424 ve.push_back(eC);
425 detail::cfAppendStencil(ap, t, ve, coarse, fine, axis, wC, fluidOk, scheme, proto);
426 for (const auto& e : ve) {
427 d.vel.slot.push_back(e.cell);
428 d.vel.coef.push_back(e.w);
429 d.vel.comp.push_back(e.comp);
430 ++d.vel.start[static_cast<std::size_t>(slot) + 1];
431 }
432 // φ part: uf −= (φ₊−φ₋)/d; the coarse cell's φ is substituted with coarse*.
433 const double sideSign = ((dir > 0) == (coarse == j)) ? 1.0 : -1.0;
434 std::vector<detail::ScalarEnt> pe;
435 detail::cfAppendStencil(ap, t, pe, coarse, fine, axis, -sideSign / dist, fluidOk, scheme);
436 for (const auto& e : pe) {
437 d.phi.slot.push_back(e.cell);
438 d.phi.coef.push_back(e.w);
439 ++d.phi.start[static_cast<std::size_t>(slot) + 1];
440 }
441 }
442 ++slot;
443 });
444 }
445 return d;
446}
447
448// ---- host applies (the oracle path; the device uses the same CSRs uploaded + SpMV kernels) ----
449
451inline void cfApplyHost(const CfCsr& c, const std::vector<double>& f, std::vector<double>& out) {
452 const Index n = static_cast<Index>(c.start.size()) - 1;
453 for (Index i = 0; i < n; ++i) {
454 double acc = 0.0;
455 for (Index k = c.start[static_cast<std::size_t>(i)]; k < c.start[static_cast<std::size_t>(i) + 1];
456 ++k)
457 acc += c.coef[static_cast<std::size_t>(k)] * f[static_cast<std::size_t>(c.slot[static_cast<std::size_t>(k)])];
458 out[static_cast<std::size_t>(i)] += acc;
459 }
460}
461
463inline void cfApplyCompHost(const CfCompCsr& c, const std::array<std::vector<double>, 3>& u,
464 std::vector<double>& out) {
465 const Index n = static_cast<Index>(c.start.size()) - 1;
466 for (Index i = 0; i < n; ++i) {
467 double acc = 0.0;
468 for (Index k = c.start[static_cast<std::size_t>(i)]; k < c.start[static_cast<std::size_t>(i) + 1];
469 ++k)
470 acc += c.coef[static_cast<std::size_t>(k)] *
471 u[static_cast<std::size_t>(c.comp[static_cast<std::size_t>(k)])]
472 [static_cast<std::size_t>(c.slot[static_cast<std::size_t>(k)])];
473 out[static_cast<std::size_t>(i)] += acc;
474 }
475}
476
477// ---- device mirrors (Kokkos TUs only; include after a Kokkos-carrying header) ------------------
478#ifdef KOKKOS_INLINE_FUNCTION
479
492
493inline CfCsrDev uploadCfCsr(const CfCsr& h, const char* name) {
494 CfCsrDev d;
495 d.n = static_cast<Index>(h.start.size()) - 1;
496 d.nz = static_cast<Index>(h.slot.size());
497 if (d.nz == 0) {
498 d.n = 0;
499 return d;
500 }
501 d.start = toDevice(h.start, name);
502 d.slot = toDevice(h.slot, name);
503 d.coef = toDevice(h.coef, name);
504 return d;
505}
506inline CfCompCsrDev uploadCfCompCsr(const CfCompCsr& h, const char* name) {
507 CfCompCsrDev d;
508 d.n = static_cast<Index>(h.start.size()) - 1;
509 d.nz = static_cast<Index>(h.slot.size());
510 if (d.nz == 0) {
511 d.n = 0;
512 return d;
513 }
514 d.start = toDevice(h.start, name);
515 d.slot = toDevice(h.slot, name);
516 d.coef = toDevice(h.coef, name);
517 d.comp = toDevice(h.comp, name);
518 return d;
519}
520
523 if (c.n == 0)
524 return;
525 auto st = c.start;
526 auto sl = c.slot;
527 auto w = c.coef;
528 Kokkos::parallel_for(
529 "amr::cf_apply", c.n, KOKKOS_LAMBDA(const Index i) {
530 double acc = 0.0;
531 for (Index k = st(i); k < st(i + 1); ++k)
532 acc += w(k) * f(sl(k));
533 out(i) += acc;
534 });
535}
536
540 if (c.n == 0)
541 return;
542 auto st = c.start;
543 auto sl = c.slot;
544 auto w = c.coef;
545 auto cp = c.comp;
546 Kokkos::parallel_for(
547 "amr::cf_apply_comp", c.n, KOKKOS_LAMBDA(const Index i) {
548 double acc = 0.0;
549 for (Index k = st(i); k < st(i + 1); ++k) {
550 const int a = cp(k);
551 const double v = (a == 0) ? u0(sl(k)) : (a == 1) ? u1(sl(k)) : u2(sl(k));
552 acc += w(k) * v;
553 }
554 out(i) += acc;
555 });
556}
557
558#endif // KOKKOS_INLINE_FUNCTION
559
560} // namespace peclet::core::amr
561
562#endif // PECLET_CORE_HAVE_MORTON
563#endif // PECLET_CORE_AMR_CF_SCHEME_HPP
Cell-centered FV Poisson operator on one (periodic) block octree.
Definition poisson.hpp:44
void forEachFaceNeighbor(Index i, Fn &&fn) const
Visit each face neighbour of leaf i: fn(neighbourSlot, coeff, axis, alpha) where coeff = A_f / d_f (p...
Definition poisson.hpp:258
Real cellWidth(Index i) const
Definition poisson.hpp:245
double faceOpenness(Index i, int axis, int dir) const
Openness of leaf i's face on (axis,dir); 1 if no openness has been set.
Definition poisson.hpp:147
Index periodicNeighbor(Index i, int axis, int dir) const
Periodic face neighbour leaf (covering the cell just across the face).
Definition poisson.hpp:358
void forEachFaceFull(Index i, Fn &&fn) const
Like forEachFaceNeighbor but exposes geometry for a consistent FV divergence/gradient: fn(neighbour,...
Definition poisson.hpp:313
Real cellVolume(Index i) const
Definition poisson.hpp:246
Per-block adaptive octree over block-local Morton codes.
typename M::coord_type Coord
void compactCsr(const std::vector< std::vector< Ent > > &per, Csr &out, Index n)
void cfAppendStencil(const AmrPoisson< 3, Bits > &ap, const BlockOctree< 3, Bits > &t, std::vector< Ent > &out, Index coarse, Index fine, int axis, double scale, FluidFn &&fluidOk, CfScheme scheme, const Ent &proto=Ent{})
Append the scheme's (coarse* − coarse_raw) substitution stencil for ONE directed C/F sub-face to out,...
Definition cf_scheme.hpp:83
void cfApplyCompHost(const CfCompCsr &c, const std::array< std::vector< double >, 3 > &u, std::vector< double > &out)
out(i) += Σ coef·u[comp](slot) (the divergence overlay).
CfUfDelta buildCfUfDelta(const AmrPoisson< 3, Bits > &ap, const BlockOctree< 3, Bits > &t, FluidFn &&fluidOk, CfScheme scheme)
void cfApply(const CfCsrDev &c, View< const double > f, View< double > out)
out(i) += Σ coef·f(slot).
CfScheme
Coarse/fine interface scheme for the collocated flow operators.
Definition cf_scheme.hpp:55
@ standard
raw coarse value (two-point flux; 1st-order at 2:1 faces)
@ quadratic
Martin–Cartwright tangential quadratic (2nd-order at 2:1 faces)
CfCompCsrDev uploadCfCompCsr(const CfCompCsr &h, const char *name)
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,...
Definition adapt.hpp:53
std::array< CfCsr, 3 > buildCfGradDelta(const AmrPoisson< 3, Bits > &ap, const BlockOctree< 3, Bits > &t, RowFn &&rowOk, FluidFn &&fluidOk, CfScheme scheme)
(G_scheme − G_std) for the ABC cell gradient gradOf/grad3.
void cfApplyHost(const CfCsr &c, const std::vector< double > &f, std::vector< double > &out)
out(i) += Σ coef·f(slot) (the scalar overlay: momentum ∇² delta, gradient delta per axis).
CfCsr buildCfLapDelta(const AmrPoisson< 3, Bits > &ap, const BlockOctree< 3, Bits > &t, double factor, RowFn &&rowOk, FluidFn &&fluidOk, CfScheme scheme)
(∇²_scheme − ∇²_std) as a scalar CSR, ×factor (pass μ for the momentum deferred-correction RHS,...
CfCompCsr buildCfDivDelta(const AmrPoisson< 3, Bits > &ap, const BlockOctree< 3, Bits > &t, RowFn &&rowOk, FluidFn &&fluidOk, CfScheme scheme)
(D_scheme − D_std) for the face-average divergence div_i = invV·Σ α·A·dir·(face value).
void cfApplyComp(const CfCompCsrDev &c, View< const double > u0, View< const double > u1, View< const double > u2, View< double > out)
out(i) += Σ coef·u[comp](slot).
CfCsrDev uploadCfCsr(const CfCsr &h, const char *name)
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
double Real
Default host floating type. Device kernels may use float; conversions happen at the boundary.
Definition types.hpp:18
std::int64_t Index
Signed index type for grids and particles (supersedes block_decomposer's long int IndxT).
Definition types.hpp:15
Component-tagged CSR overlay (the divergence delta): out(i) += Σ_k coef·u[comp](slot).
Definition cf_scheme.hpp:68
std::vector< double > coef
Definition cf_scheme.hpp:71
std::vector< Index > slot
Definition cf_scheme.hpp:70
std::vector< Index > start
Definition cf_scheme.hpp:69
std::vector< int8_t > comp
Definition cf_scheme.hpp:72
Device mirror of a CfCsr (empty when the scheme is standard / no C/F rows).
Scalar-input CSR overlay: out(i) += Σ_k coef·f(slot) over rows with C/F faces.
Definition cf_scheme.hpp:61
std::vector< Index > start
size n+1
Definition cf_scheme.hpp:62
std::vector< double > coef
Definition cf_scheme.hpp:64
std::vector< Index > slot
Definition cf_scheme.hpp:63
(uf_scheme − uf_std) for the div-free FACE field uf_k = ½(u_i+u_j) − (φ₊−φ₋)/d, one delta row per for...
CfCsr phi
reads the projection potential φ, rows = face slots
CfCompCsr vel
reads the velocity components, rows = face slots
An assembled face-CSR: row offsets (size n+1), neighbour index + coefficient per face (size nFaces).
Definition csr.hpp:72