flow 1.3.0
Kokkos cut-cell IBM incompressible Navier-Stokes solver + pnm pore extraction
Loading...
Searching...
No Matches
scalar_cutcell_operator.hpp
Go to the documentation of this file.
1// Cut-cell scalar operator: the stored true operator of doc/scalar_ibm_design.md §4.1 (single
2// phase, WO-3) — the 7 bands, the facet overlay coefficients, the right-hand side, the lumped
3// level-0 surrogate diagonal of §4.3 — and the per-facet flux evaluation the budget, the wall-flux
4// getter and the facet diagnostics share. Block kernels on flow's G = 2 extended block; the Solver
5// side (unit conversions, refusals, the solve, the getters) is `flow_ibm_scalars_cutcell.hpp`.
6//
7// The fluid row of unknown i, per unit FULL-cell volume V (§1.4, backward Euler; idt = 0 steady):
8//
9// kappa_i idt (c_i - c_i^n) + sum_a sum_+- Lam w_a a (c_i - c_nb) + sum_phi cw_phi u_p,phi + B_i
10// = kappa_i S_i + sum_phi rw_phi + (Dirichlet domain faces: 2 Lam w_a a_bf g_bf)
11//
12// with cw = alpha G and rw = alpha G g (Dirichlet G = Lam/s, Robin G = 1/(s/Lam + 1/k)) or cw = 0
13// and rw = alpha q (Neumann). The bands hold kappa idt, the face diffusion Lam w a and the
14// Dirichlet-domain fold 2 Lam w a_bf; the overlay adds cw times the probe interpolation. Every row
15// that is not a fluid unknown is an identity row (AC = 1, bands 0, rhs 0), so c stays 0 there.
16//
17// A face toward a cell that is not an unknown carries no band (the guard of ruling D-WO3-4). By the
18// geometry's invariant (kappa = 0 => every aperture 0; a sealed cell has every aperture 0) it is a
19// no-op on every interior face; at a non-periodic global face the ghost's unknown flag was cleared
20// by the record, which is exactly the "band toward the ghost is 0" of §1.4's domain-face table.
21#ifndef PECLET_FLOW_SCALAR_CUTCELL_OPERATOR_HPP
22#define PECLET_FLOW_SCALAR_CUTCELL_OPERATOR_HPP
23
24#include <cstdint>
25#include <Kokkos_Core.hpp>
26#include <limits>
27#include <memory>
28#include <string>
29#include <utility>
30#include <vector>
31
32#include "mac_cutcell.hpp" // CCField, CCConst, CCExec, CCMem, C3, ccFor3
33#include "peclet/core/scheme/probe_flux.hpp"
34#include "policy.hpp"
36#include "staggered_advection.hpp" // sadv::tvd / sou / fou_flux, the legacy reconstructions
37
38namespace peclet::flow {
39
40class ScalarMG; // scalar_mg.hpp (WO-4): the preconditioner, one per cut-cell scalar
41
45 int type = 0;
46 double value = 0.0;
47 double coefficient = 0.0;
48 // conjugate (§1.1, §8.1 `set_scalar_solid`): the solid's own diffusivity D_s (L^2/T), the
49 // capacity ratio C_s, the partition coefficient K (= c_s / c at equilibrium) and the contact
50 // resistance R_c (T/L, on psi). psi form: Lam_s = C_s D_s K, capacity C_s K.
51 double solidD = 0.0, solidC = 1.0, solidK = 1.0, solidRc = 0.0;
52};
53
57 // ---- settings (physical) ----
59 std::vector<std::pair<int, ScalarWallSpec>> wallInstance;
60 double rtol = 1e-10;
61 int maxit = 200;
62 double sourceConst = 0.0;
64 bool sourceIsField = false;
65 bool hasProfile[6] = {false, false, false, false, false, false};
66 std::vector<double> profile[6];
67 int profileN[6][2] = {};
70 double meanGrad[3] = {0.0, 0.0, 0.0};
71 // ---- the operator of the last advance / steady solve ----
73 Kokkos::View<double*, CCMem> cw, rw;
74 Kokkos::View<double*, CCMem> gFace[6];
75 bool dirFace[6] = {false, false, false, false, false, false};
76 double lam = 0.0;
77 double idt = 0.0;
78 double dt = 0.0;
79 double srcFactor = 0.0;
80 std::vector<int> wtType;
81 std::vector<double> wtK, wtG, wtQ;
82 long numUnknowns = 0;
83 bool steady = false, built = false, singular = false;
84 bool meanGradOn = false;
85 double gPhys[3] = {0.0, 0.0, 0.0};
86 double gInt[3] = {0.0, 0.0, 0.0};
87 double ubar[3] = {0.0, 0.0, 0.0};
88 // ---- Krylov scratch and statistics ----
90 Kokkos::View<double*, CCMem> ks;
91 Kokkos::View<double*, Kokkos::HostSpace> pk;
93 int iterations = 0;
94 double residual = 0.0;
95 bool converged = true;
96 bool warnedNoConv = false;
100 bool warnedKoren = false;
101 // persistent device tables, refilled every advance (review finding 7: no per-advance allocation):
102 // the wall table (sco::WallTable) and the conjugate material table (sco::MaterialTable)
103 Kokkos::View<int*, CCMem> wtTypeV, mtConjV;
104 Kokkos::View<double*, CCMem> wtKV, wtGV, wtQV, mtLamV, mtCKV, mtKV, mtRcV;
105 double incompatibility = 0.0;
106 // ---- ScalarMG (§5.2): level table per geometry version / block, coefficients per build ----
107 std::shared_ptr<ScalarMG> mg;
108 long mgVersion = -1;
109 std::size_t mgN = 0;
110 int mgLevels = 1;
111 // ---- advection (WO-5, §6): the face flux of the last advance and its classification ----
112 CCField
113 phi[3];
121 double maxCellPeclet = 0.0;
122 bool openFace[6] = {false, false, false, false, false, false};
123 bool openInflow[6] = {false, false, false, false, false, false};
124 bool advecting = false;
125 long numSmall = 0;
127 long numFluxFaces = 0;
130 bool warnedBackflow = false;
131 bool warnedBottom = false;
132 double bulkCourant = 0.0;
133 // ---- conjugate (WO-7, §1.1, §1.3, §2.7, §3.4, §4): the solid field psi_s = c_s / K ----------
134 // Allocated by the first set_scalar_solid; the two-field path runs only while some body is
135 // conjugate (`conj` of the last build), so a scalar without a conjugate body takes the
136 // single-phase path above, bitwise.
138 bool conj = false;
139 // the per-scalar solid record (§2.7, §3.4), rebuilt when the geometry or the conjugate set moves
140 long solidVersion = -1;
141 std::vector<char> solidKey;
144 Kokkos::View<double*, CCMem> sS;
145 Kokkos::View<int* [8], Kokkos::LayoutLeft, CCMem> offS;
146 Kokkos::View<double* [8], Kokkos::LayoutLeft, CCMem> wS;
147 Kokkos::View<std::uint8_t*, CCMem> rungS;
148 Kokkos::View<std::uint8_t*, CCMem> conjF;
150 double solidVolume = 0.0;
151 double sealedSolidVolume = 0.0;
152 long solidRungs[4] = {0, 0, 0, 0};
153 // the solid operator of the last build (§4.1: the solid phase's own 7 bands)
158 Kokkos::View<double*, CCMem> cc;
159 std::vector<double> mtLam, mtCK, mtK, mtRc;
160 double maxSolidD = 0.0;
162};
163
164namespace sco {
165
167KOKKOS_INLINE_FUNCTION double lowAp(const CCConst& sax, const CCConst& say, const CCConst& saz,
168 int a, long i) {
169 return a == 0 ? sax(i) : (a == 1 ? say(i) : saz(i));
170}
171
174inline void zeroNonUnknown(CCField c, CCConst unk, C3 e, int g) {
175 ccFor3(
176 "peclet::flow::sco_zero_nonunknown", C3{g, g, g}, C3{e.x - g, e.y - g, e.z - g},
177 KOKKOS_LAMBDA(int x, int y, int z) {
178 const long i = (long)x + (long)y * e.x + (long)z * (long)e.x * e.y;
179 if (!(unk(i) > 0.5))
180 c(i) = 0.0;
181 });
182}
183
186inline void buildBands(CCField AC, CCField AW, CCField AE, CCField AS, CCField AN, CCField AB,
187 CCField AT, CCConst kappa, CCConst unk, CCConst sax, CCConst say,
188 CCConst saz, double lam, double idt, const double w[3], C3 e, int g) {
189 const double lx = lam * w[0], ly = lam * w[1], lz = lam * w[2];
190 ccFor3(
191 "peclet::flow::sco_bands", C3{g, g, g}, C3{e.x - g, e.y - g, e.z - g},
192 KOKKOS_LAMBDA(int x, int y, int z) {
193 const long sx = 1, sy = e.x, sz = (long)e.x * e.y;
194 const long i = (long)x + (long)y * sy + (long)z * sz;
195 if (!(unk(i) > 0.5)) {
196 AC(i) = 1.0;
197 AW(i) = AE(i) = AS(i) = AN(i) = AB(i) = AT(i) = 0.0;
198 return;
199 }
200 const double tw = unk(i - sx) > 0.5 ? lx * sax(i) : 0.0;
201 const double te = unk(i + sx) > 0.5 ? lx * sax(i + sx) : 0.0;
202 const double ts = unk(i - sy) > 0.5 ? ly * say(i) : 0.0;
203 const double tn = unk(i + sy) > 0.5 ? ly * say(i + sy) : 0.0;
204 const double tb = unk(i - sz) > 0.5 ? lz * saz(i) : 0.0;
205 const double tt = unk(i + sz) > 0.5 ? lz * saz(i + sz) : 0.0;
206 AW(i) = -tw;
207 AE(i) = -te;
208 AS(i) = -ts;
209 AN(i) = -tn;
210 AB(i) = -tb;
211 AT(i) = -tt;
212 AC(i) = kappa(i) * idt + ((tw + te) + (ts + tn)) + (tb + tt);
213 });
214}
215
218inline void buildRhs(CCField b, CCConst cOld, CCConst kappa, CCConst unk, double idt,
219 double srcConst, CCConst src, bool useField, double srcFactor, C3 e, int g) {
220 ccFor3(
221 "peclet::flow::sco_rhs", C3{g, g, g}, C3{e.x - g, e.y - g, e.z - g},
222 KOKKOS_LAMBDA(int x, int y, int z) {
223 const long i = (long)x + (long)y * e.x + (long)z * (long)e.x * e.y;
224 if (!(unk(i) > 0.5)) {
225 b(i) = 0.0;
226 return;
227 }
228 const double S = (useField ? src(i) : srcConst) * srcFactor;
229 b(i) = kappa(i) * (idt * cOld(i) + S);
230 });
231}
232
235KOKKOS_INLINE_FUNCTION void faceTangents(int a, int& t1, int& t2) {
236 t1 = (a == 0) ? 1 : 0;
237 t2 = (a == 2) ? 1 : 2;
238}
239
244 Kokkos::View<const double*, CCMem> gv, double lamW, int a, int side,
245 C3 e, int g) {
246 int t1, t2;
247 faceTangents(a, t1, t2);
248 const int ext[3] = {e.x, e.y, e.z};
249 const long st[3] = {1, (long)e.x, (long)e.x * e.y};
250 const int n1 = ext[t1] - 2 * g, n2 = ext[t2] - 2 * g;
251 const long sa = st[a], s1 = st[t1], s2 = st[t2];
252 const int aInner = side == 0 ? g : ext[a] - g - 1;
254 Kokkos::parallel_for(
255 "peclet::flow::sco_dirichlet_face", MDRange2<CCExec>(space, {0, 0}, {n1, n2}),
256 KOKKOS_LAMBDA(int j1, int j2) {
257 const long i = (long)aInner * sa + (long)(j1 + g) * s1 + (long)(j2 + g) * s2;
258 if (!(unk(i) > 0.5))
259 return;
260 const double abf = side == 0 ? saA(i) : saA(i + sa);
261 const double t = 2.0 * lamW * abf;
262 AC(i) += t;
263 b(i) += t * gv((long)j1 + (long)j2 * n1);
264 });
265 space.fence();
266}
267
271 Kokkos::View<const double*, CCMem> gv, double lamW, double vol,
272 int a, int side, C3 e, int g) {
273 int t1, t2;
274 faceTangents(a, t1, t2);
275 const int ext[3] = {e.x, e.y, e.z};
276 const long st[3] = {1, (long)e.x, (long)e.x * e.y};
277 const int n1 = ext[t1] - 2 * g, n2 = ext[t2] - 2 * g;
278 const long sa = st[a], s1 = st[t1], s2 = st[t2];
279 const int aInner = side == 0 ? g : ext[a] - g - 1;
280 Kokkos::View<double*, CCMem> q("peclet::flow::sco_face_influx", (long)n1 * n2);
282 Kokkos::parallel_for(
283 "peclet::flow::sco_dirichlet_face_influx", MDRange2<CCExec>(space, {0, 0}, {n1, n2}),
284 KOKKOS_LAMBDA(int j1, int j2) {
285 const long i = (long)aInner * sa + (long)(j1 + g) * s1 + (long)(j2 + g) * s2;
286 const long k = (long)j1 + (long)j2 * n1;
287 if (!(unk(i) > 0.5)) {
288 q(k) = 0.0;
289 return;
290 }
291 const double abf = side == 0 ? saA(i) : saA(i + sa);
292 q(k) = vol * (2.0 * lamW * abf) * (gv(k) - c(i));
293 });
294 space.fence();
295 auto h = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), q);
296 double s = 0.0;
297 for (long k = 0; k < (long)h.extent(0); ++k)
298 s += h(k);
299 return s;
300}
301
309inline void residualFluxForm(CCField r, CCConst c, CCConst b, CCConst kappa, CCConst unk,
310 CCConst sax, CCConst say, CCConst saz, double lam, double idt,
311 const double w[3], C3 e, int g) {
312 const double lx = lam * w[0], ly = lam * w[1], lz = lam * w[2];
313 ccFor3(
314 "peclet::flow::sco_residual_flux", C3{g, g, g}, C3{e.x - g, e.y - g, e.z - g},
315 KOKKOS_LAMBDA(int x, int y, int z) {
316 const long sx = 1, sy = e.x, sz = (long)e.x * e.y;
317 const long i = (long)x + (long)y * sy + (long)z * sz;
318 if (!(unk(i) > 0.5)) {
319 r(i) = 0.0;
320 return;
321 }
322 const double ci = c(i);
323 const double tw = unk(i - sx) > 0.5 ? lx * sax(i) : 0.0;
324 const double te = unk(i + sx) > 0.5 ? lx * sax(i + sx) : 0.0;
325 const double ts = unk(i - sy) > 0.5 ? ly * say(i) : 0.0;
326 const double tn = unk(i + sy) > 0.5 ? ly * say(i + sy) : 0.0;
327 const double tb = unk(i - sz) > 0.5 ? lz * saz(i) : 0.0;
328 const double tt = unk(i + sz) > 0.5 ? lz * saz(i + sz) : 0.0;
329 const double flux = ((tw * (ci - c(i - sx)) + te * (ci - c(i + sx))) +
330 (ts * (ci - c(i - sy)) + tn * (ci - c(i + sy)))) +
331 (tb * (ci - c(i - sz)) + tt * (ci - c(i + sz)));
332 r(i) = b(i) - kappa(i) * idt * ci - flux;
333 });
334}
335
339 int a, int side, C3 e, int g) {
340 int t1, t2;
341 faceTangents(a, t1, t2);
342 const int ext[3] = {e.x, e.y, e.z};
343 const long st[3] = {1, (long)e.x, (long)e.x * e.y};
344 const int n1 = ext[t1] - 2 * g, n2 = ext[t2] - 2 * g;
345 const long sa = st[a], s1 = st[t1], s2 = st[t2];
346 const int aInner = side == 0 ? g : ext[a] - g - 1;
348 Kokkos::parallel_for(
349 "peclet::flow::sco_dirichlet_face_residual", MDRange2<CCExec>(space, {0, 0}, {n1, n2}),
350 KOKKOS_LAMBDA(int j1, int j2) {
351 const long i = (long)aInner * sa + (long)(j1 + g) * s1 + (long)(j2 + g) * s2;
352 if (!(unk(i) > 0.5))
353 return;
354 const double abf = side == 0 ? saA(i) : saA(i + sa);
355 r(i) -= 2.0 * lamW * abf * c(i);
356 });
357 space.fence();
358}
359
362struct WallTable {
363 Kokkos::View<int*, CCMem> type;
364 Kokkos::View<double*, CCMem> k, gval, q;
365 int nb = 0;
366};
367
370inline void facetCoefficients(Kokkos::View<double*, CCMem> cw, Kokkos::View<double*, CCMem> rw,
371 const scg::ScalarFacetOverlay& fo, CCConst unk, const WallTable& wt,
372 double lam) {
373 auto fcell = fo.cell;
374 auto falpha = fo.alpha;
375 auto fbody = fo.body;
376 auto sF = fo.sF;
377 auto type = wt.type;
378 auto kk = wt.k;
379 auto gg = wt.gval;
380 auto qq = wt.q;
381 const int nb = wt.nb;
382 const double kInf = std::numeric_limits<double>::infinity(); // Dirichlet: G = Lam / s
384 Kokkos::parallel_for(
385 "peclet::flow::sco_facet_coeffs", Kokkos::RangePolicy<CCExec>(space, 0, fo.n),
386 KOKKOS_LAMBDA(const long f) {
387 if (!(unk(fcell(f)) > 0.5)) {
388 cw(f) = 0.0;
389 rw(f) = 0.0;
390 return;
391 }
392 int bd = fbody(f);
393 if (bd < 0 || bd >= nb)
394 bd = 0;
395 const int t = type(bd);
396 const double al = falpha(f);
397 if (t == 0) {
398 cw(f) = 0.0;
399 rw(f) = al * qq(bd);
400 return;
401 }
402 if (t == 3) { // conjugate (WO-7): the interface coupling is `cc`, not a wall term
403 cw(f) = 0.0;
404 rw(f) = 0.0;
405 return;
406 }
407 const double G = peclet::core::scheme::wallConductance(sF(f), lam, t == 1 ? kInf : kk(bd));
408 cw(f) = al * G;
409 rw(f) = al * G * gg(bd);
410 });
411 space.fence();
412}
413
420 Kokkos::View<const double*, CCMem> cw, bool subtract = false) {
421 auto cutCell = fo.cutCell;
422 auto cfs = fo.cellFacetStart;
423 auto offF = fo.offF;
424 auto wF = fo.wF;
426 Kokkos::parallel_for(
427 "peclet::flow::sco_overlay_apply", Kokkos::RangePolicy<CCExec>(space, 0, fo.nCut),
428 KOKKOS_LAMBDA(const long c) {
429 const long i = cutCell(c);
430 double acc = 0.0;
431 for (int f = cfs(c); f < cfs(c + 1); ++f) {
432 double up = 0.0;
433 for (int k = 0; k < 8; ++k)
434 up += wF(f, k) * x(i + (long)offF(f, k));
435 acc += cw(f) * up;
436 }
437 if (subtract)
438 y(i) -= acc;
439 else
440 y(i) += acc;
441 });
442}
443
446 Kokkos::View<const double*, CCMem> rw) {
447 auto cutCell = fo.cutCell;
448 auto cfs = fo.cellFacetStart;
450 Kokkos::parallel_for(
451 "peclet::flow::sco_overlay_rhs", Kokkos::RangePolicy<CCExec>(space, 0, fo.nCut),
452 KOKKOS_LAMBDA(const long c) {
453 double acc = 0.0;
454 for (int f = cfs(c); f < cfs(c + 1); ++f)
455 acc += rw(f);
456 b(cutCell(c)) += acc;
457 });
458 space.fence();
459}
460
464 Kokkos::View<const double*, CCMem> cw) {
465 Kokkos::deep_copy(CCExec(), SAC, AC);
466 auto cutCell = fo.cutCell;
467 auto cfs = fo.cellFacetStart;
469 Kokkos::parallel_for(
470 "peclet::flow::sco_surrogate_diag", Kokkos::RangePolicy<CCExec>(space, 0, fo.nCut),
471 KOKKOS_LAMBDA(const long c) {
472 double acc = 0.0;
473 for (int f = cfs(c); f < cfs(c + 1); ++f)
474 acc += cw(f);
475 SAC(cutCell(c)) += acc;
476 });
477 space.fence();
478}
479
482inline void facetFlux(Kokkos::View<double*, CCMem> up, Kokkos::View<double*, CCMem> qin, CCConst x,
483 const scg::ScalarFacetOverlay& fo, Kokkos::View<const double*, CCMem> cw,
484 Kokkos::View<const double*, CCMem> rw) {
485 auto fcell = fo.cell;
486 auto offF = fo.offF;
487 auto wF = fo.wF;
489 Kokkos::parallel_for(
490 "peclet::flow::sco_facet_flux", Kokkos::RangePolicy<CCExec>(space, 0, fo.n),
491 KOKKOS_LAMBDA(const long f) {
492 const long i = fcell(f);
493 double u = 0.0;
494 for (int k = 0; k < 8; ++k)
495 u += wF(f, k) * x(i + (long)offF(f, k));
496 up(f) = u;
497 qin(f) = rw(f) - cw(f) * u;
498 });
499 space.fence();
500}
501
502// ---- inner-cell reductions (this rank; the Solver adds the MPI_Allreduce) ----------------------
503
504// Each reduction is written once, with its result as a parameter: a host double (the Local
505// form) or a device slot (the resident Krylov, scalar_krylov.hpp). Same policy, same functor, so
506// the same value either way (WO-9b).
507
509template <class R>
510inline void dotReduce(CCConst a, CCConst b, C3 e, int g, R&& result) {
511 ccReduce3(
512 "peclet::flow::sco_dot", C3{g, g, g}, C3{e.x - g, e.y - g, e.z - g},
513 KOKKOS_LAMBDA(int x, int y, int z, double& acc) {
514 const long i = (long)x + (long)y * e.x + (long)z * (long)e.x * e.y;
515 acc += a(i) * b(i);
516 },
517 std::forward<R>(result));
518}
519inline double dotLocal(CCConst a, CCConst b, C3 e, int g) {
520 double s = 0.0;
521 dotReduce(a, b, e, g, s);
522 return s;
523}
524
526template <class R1, class R2>
527inline void dot2Reduce(CCConst a, CCConst b, CCConst c, C3 e, int g, R1&& r1, R2&& r2) {
529 Kokkos::parallel_reduce(
530 "peclet::flow::sco_dot2", MDRange3<CCExec>(space, {g, g, g}, {e.x - g, e.y - g, e.z - g}),
531 KOKKOS_LAMBDA(int x, int y, int z, double& p1, double& p2) {
532 const long i = (long)x + (long)y * e.x + (long)z * (long)e.x * e.y;
533 p1 += a(i) * b(i);
534 p2 += c(i) * c(i);
535 },
536 std::forward<R1>(r1), std::forward<R2>(r2));
537}
538inline void dot2Local(CCConst a, CCConst b, CCConst c, C3 e, int g, double& ab, double& cc) {
539 double s1 = 0.0, s2 = 0.0;
540 dot2Reduce(a, b, c, e, g, s1, s2);
541 ab = s1;
542 cc = s2;
543}
544
547template <class Red>
548inline void maxabsReduce(CCConst a, C3 e, int g, Red red) {
549 ccReduce3(
550 "peclet::flow::sco_maxabs", C3{g, g, g}, C3{e.x - g, e.y - g, e.z - g},
551 KOKKOS_LAMBDA(int x, int y, int z, double& acc) {
552 const long i = (long)x + (long)y * e.x + (long)z * (long)e.x * e.y;
553 const double v = Kokkos::fabs(a(i));
554 if (v > acc)
555 acc = v;
556 },
557 red);
558}
559inline double maxabsLocal(CCConst a, C3 e, int g) {
560 double m = 0.0;
561 maxabsReduce(a, e, g, Kokkos::Max<double>(m));
562 return m;
563}
564
566inline void sumUnknownLocal(CCConst a, CCConst unk, C3 e, int g, double& sum, double& sumAbs) {
568 double s1 = 0.0, s2 = 0.0;
569 Kokkos::parallel_reduce(
570 "peclet::flow::sco_sum_unknown",
571 MDRange3<CCExec>(space, {g, g, g}, {e.x - g, e.y - g, e.z - g}),
572 KOKKOS_LAMBDA(int x, int y, int z, double& p1, double& p2) {
573 const long i = (long)x + (long)y * e.x + (long)z * (long)e.x * e.y;
574 if (unk(i) > 0.5) {
575 p1 += a(i);
576 p2 += Kokkos::fabs(a(i));
577 }
578 },
579 s1, s2);
580 sum = s1;
581 sumAbs = s2;
582}
583
585inline void addOnUnknown(CCField a, CCConst unk, double d, C3 e, int g) {
586 ccFor3(
587 "peclet::flow::sco_add_unknown", C3{g, g, g}, C3{e.x - g, e.y - g, e.z - g},
588 KOKKOS_LAMBDA(int x, int y, int z) {
589 const long i = (long)x + (long)y * e.x + (long)z * (long)e.x * e.y;
590 if (unk(i) > 0.5)
591 a(i) += d;
592 });
593}
594
596inline void kappaMomentsLocal(CCConst c, CCConst kappa, CCConst unk, C3 e, int g, double& kc,
597 double& k) {
599 double s1 = 0.0, s2 = 0.0;
600 Kokkos::parallel_reduce(
601 "peclet::flow::sco_kappa_moments",
602 MDRange3<CCExec>(space, {g, g, g}, {e.x - g, e.y - g, e.z - g}),
603 KOKKOS_LAMBDA(int x, int y, int z, double& p1, double& p2) {
604 const long i = (long)x + (long)y * e.x + (long)z * (long)e.x * e.y;
605 if (unk(i) > 0.5) {
606 p1 += kappa(i) * c(i);
607 p2 += kappa(i);
608 }
609 },
610 s1, s2);
611 kc = s1;
612 k = s2;
613}
614
616inline double sourceSumLocal(CCConst kappa, CCConst unk, double srcConst, CCConst src,
617 bool useField, double srcFactor, C3 e, int g) {
618 double s = 0.0;
619 ccReduce3(
620 "peclet::flow::sco_source_sum", C3{g, g, g}, C3{e.x - g, e.y - g, e.z - g},
621 KOKKOS_LAMBDA(int x, int y, int z, double& acc) {
622 const long i = (long)x + (long)y * e.x + (long)z * (long)e.x * e.y;
623 if (unk(i) > 0.5)
624 acc += kappa(i) * ((useField ? src(i) : srcConst) * srcFactor);
625 },
626 s);
627 return s;
628}
629
630// ---- advection and small cells (WO-5, §6) -------------------------------------------------------
631//
632// phi_a(i) is the volume flux through the LOW a-face of cell i per unit FULL-cell volume per
633// internal time, positive along +a: open * vel with the projection's own openness and face velocity
634// (Solver::scalarFaceFlux, §6.1). The velocity is an INDEX velocity (cells per tRef), so the A_a/V
635// = 1/h'_a of F/V is already inside it (doc/anisotropic_metric.md §2). The flux out of cell i
636// through its high a-face is phi_a(i + s_a), through its low a-face -phi_a(i).
637
642inline void faceFluxes(CCField phi, CCConst vel, CCConst open, CCConst unk, int a, C3 e) {
643 const long st[3] = {1, (long)e.x, (long)e.x * e.y};
644 const long s = st[a];
645 ccFor3(
646 "peclet::flow::sco_face_flux", C3{0, 0, 0}, e, KOKKOS_LAMBDA(int x, int y, int z) {
647 const long i = (long)x + (long)y * e.x + (long)z * (long)e.x * e.y;
648 const int xa = a == 0 ? x : (a == 1 ? y : z);
649 if (xa < 1 || !(unk(i) > 0.5) || !(unk(i - s) > 0.5)) {
650 phi(i) = 0.0;
651 return;
652 }
653 phi(i) = open(i) * vel(i);
654 });
655}
656
660inline void faceFluxCountsLocal(CCConst phi, CCConst vel, CCConst open, int a, C3 e, int g,
661 long& nFlux, long& nGuarded) {
662 (void)a;
664 long n1 = 0, n2 = 0;
665 Kokkos::parallel_reduce(
666 "peclet::flow::sco_flux_counts",
667 MDRange3<CCExec>(space, {g, g, g}, {e.x - g, e.y - g, e.z - g}),
668 KOKKOS_LAMBDA(int x, int y, int z, long& p1, long& p2) {
669 const long i = (long)x + (long)y * e.x + (long)z * (long)e.x * e.y;
670 if (phi(i) != 0.0)
671 ++p1;
672 else if (open(i) * vel(i) != 0.0)
673 ++p2;
674 },
675 n1, n2);
676 nFlux = n1;
677 nGuarded = n2;
678}
679
681inline double maxAbsFluxLocal(CCConst phx, CCConst phy, CCConst phz, C3 e, int g) {
682 double m = 0.0;
683 ccReduce3(
684 "peclet::flow::sco_max_flux", C3{g, g, g}, C3{e.x - g, e.y - g, e.z - g},
685 KOKKOS_LAMBDA(int x, int y, int z, double& acc) {
686 const long i = (long)x + (long)y * e.x + (long)z * (long)e.x * e.y;
687 const double v = Kokkos::fmax(Kokkos::fabs(phx(i)),
688 Kokkos::fmax(Kokkos::fabs(phy(i)), Kokkos::fabs(phz(i))));
689 if (v > acc)
690 acc = v;
691 },
692 Kokkos::Max<double>(m));
693 return m;
694}
695
699inline double maxCellPecletLocal(CCConst phx, CCConst phy, CCConst phz, CCConst unk, double lam,
700 const double w[3], C3 e, int g) {
701 const double dx = lam * w[0], dy = lam * w[1], dz = lam * w[2];
702 double m = 0.0;
703 ccReduce3(
704 "peclet::flow::sco_max_cell_peclet", C3{g, g, g}, C3{e.x - g, e.y - g, e.z - g},
705 KOKKOS_LAMBDA(int x, int y, int z, double& acc) {
706 const long sx = 1, sy = e.x, sz = (long)e.x * e.y;
707 const long i = (long)x + (long)y * sy + (long)z * sz;
708 if (!(unk(i) > 0.5))
709 return;
710 const double P[3] = {unk(i - sx) > 0.5 ? Kokkos::fabs(phx(i)) / dx : 0.0,
711 unk(i - sy) > 0.5 ? Kokkos::fabs(phy(i)) / dy : 0.0,
712 unk(i - sz) > 0.5 ? Kokkos::fabs(phz(i)) / dz : 0.0};
713 for (int k = 0; k < 3; ++k)
714 if (P[k] > acc) // (0 / 0 = NaN when lam = 0 and no flux: skipped)
715 acc = P[k];
716 },
717 Kokkos::Max<double>(m));
718 return m > 0.0 ? m : 0.0;
719}
720
722inline void clampNonNegative(CCField omega, C3 e, int g) {
723 ccFor3(
724 "peclet::flow::sco_clamp_omega", C3{g, g, g}, C3{e.x - g, e.y - g, e.z - g},
725 KOKKOS_LAMBDA(int x, int y, int z) {
726 const long i = (long)x + (long)y * e.x + (long)z * (long)e.x * e.y;
727 if (omega(i) < 0.0)
728 omega(i) = 0.0;
729 });
730}
731
734 const CCConst& phz, long i, long sy, long sz) {
735 return ((Kokkos::fmax(phx(i + 1), 0.0) + Kokkos::fmax(-phx(i), 0.0)) +
736 (Kokkos::fmax(phy(i + sy), 0.0) + Kokkos::fmax(-phy(i), 0.0))) +
737 (Kokkos::fmax(phz(i + sz), 0.0) + Kokkos::fmax(-phz(i), 0.0));
738}
739
743 CCConst sax, CCConst say, CCConst saz, double dt, C3 e, int g) {
744 double m = 0.0;
745 ccReduce3(
746 "peclet::flow::sco_bulk_courant", C3{g, g, g}, C3{e.x - g, e.y - g, e.z - g},
747 KOKKOS_LAMBDA(int x, int y, int z, double& acc) {
748 const long sy = e.x, sz = (long)e.x * e.y;
749 const long i = (long)x + (long)y * sy + (long)z * sz;
750 if (!(unk(i) > 0.5) || !(kappa(i) >= 1.0) || !(sax(i) >= 1.0) || !(sax(i + 1) >= 1.0) ||
751 !(say(i) >= 1.0) || !(say(i + sy) >= 1.0) || !(saz(i) >= 1.0) || !(saz(i + sz) >= 1.0))
752 return;
753 const double v = dt * cellOutflow(phx, phy, phz, i, sy, sz);
754 if (v > acc)
755 acc = v;
756 },
757 Kokkos::Max<double>(m));
758 return m;
759}
760
766 CCConst unk, double dt, double thr, C3 e, int g) {
767 Kokkos::deep_copy(CCExec(), small, 0.0);
768 ccFor3(
769 "peclet::flow::sco_small_cells", C3{g, g, g}, C3{e.x - g, e.y - g, e.z - g},
770 KOKKOS_LAMBDA(int x, int y, int z) {
771 const long sy = e.x, sz = (long)e.x * e.y;
772 const long i = (long)x + (long)y * sy + (long)z * sz;
773 const double k = kappa(i);
774 if (!(unk(i) > 0.5) || !(k < 1.0))
775 return;
776 small(i) = dt * cellOutflow(phx, phy, phz, i, sy, sz) > thr * k ? 1.0 : 0.0;
777 });
778}
779
781inline long countSmallLocal(CCConst small, C3 e, int g) {
782 long n = 0;
783 ccReduce3(
784 "peclet::flow::sco_count_small", C3{g, g, g}, C3{e.x - g, e.y - g, e.z - g},
785 KOKKOS_LAMBDA(int x, int y, int z, long& acc) {
786 const long i = (long)x + (long)y * e.x + (long)z * (long)e.x * e.y;
787 if (small(i) > 0.5)
788 ++acc;
789 },
790 n);
791 return n;
792}
793
797inline long countImplicitLocal(CCConst phi, CCConst small, CCConst unk, int a, bool steady, C3 e,
798 int g) {
799 const long st[3] = {1, (long)e.x, (long)e.x * e.y};
800 const long s = st[a];
801 long n = 0;
802 ccReduce3(
803 "peclet::flow::sco_count_implicit", C3{g, g, g}, C3{e.x - g, e.y - g, e.z - g},
804 KOKKOS_LAMBDA(int x, int y, int z, long& acc) {
805 const long i = (long)x + (long)y * e.x + (long)z * (long)e.x * e.y;
806 if (phi(i) != 0.0 && unk(i) > 0.5 && unk(i - s) > 0.5 &&
807 (steady || small(i) > 0.5 || small(i - s) > 0.5))
808 ++acc;
809 },
810 n);
811 return n;
812}
813
823 CCConst small, CCConst unk, bool steady, C3 e, int g) {
824 ccFor3(
825 "peclet::flow::sco_adv_bands", C3{g, g, g}, C3{e.x - g, e.y - g, e.z - g},
826 KOKKOS_LAMBDA(int x, int y, int z) {
827 const long sx = 1, sy = e.x, sz = (long)e.x * e.y;
828 const long i = (long)x + (long)y * sy + (long)z * sz;
829 if (!(unk(i) > 0.5)) {
830 outflow(i) = 0.0;
831 return;
832 }
833 const bool si = small(i) > 0.5;
834 // F_out per face: W, E, S, N, B, T; implicit flag per face
835 const double F[6] = {-phx(i), phx(i + sx), -phy(i), phy(i + sy), -phz(i), phz(i + sz)};
836 const long nb[6] = {i - sx, i + sx, i - sy, i + sy, i - sz, i + sz};
837 double d[6], o[6];
838 for (int f = 0; f < 6; ++f) {
839 const bool imp = (steady || si || small(nb[f]) > 0.5) && unk(nb[f]) > 0.5;
840 d[f] = imp ? Kokkos::fmax(F[f], 0.0) : 0.0;
841 o[f] = imp ? Kokkos::fmin(F[f], 0.0) : 0.0;
842 }
843 const double om = ((d[0] + d[1]) + (d[2] + d[3])) + (d[4] + d[5]);
844 outflow(i) = om;
845 AC(i) += om;
846 AW(i) += o[0];
847 AE(i) += o[1];
848 AS(i) += o[2];
849 AN(i) += o[3];
850 AB(i) += o[4];
851 AT(i) += o[5];
852 });
853}
854
862inline void explicitFaceFluxes(CCField Phi, CCConst phi, CCConst c, CCConst unk, CCConst small,
863 int a, int scheme, bool steady, C3 e, int g) {
864 const long st[3] = {1, (long)e.x, (long)e.x * e.y};
865 const long s = st[a];
866 const C3 hi{a == 0 ? e.x - g + 1 : e.x - g, a == 1 ? e.y - g + 1 : e.y - g,
867 a == 2 ? e.z - g + 1 : e.z - g};
868 Kokkos::deep_copy(CCExec(), Phi, 0.0);
869 ccFor3(
870 "peclet::flow::sco_explicit_flux", C3{g, g, g}, hi, KOKKOS_LAMBDA(int x, int y, int z) {
871 const long i = (long)x + (long)y * e.x + (long)z * (long)e.x * e.y;
872 const double v = phi(i);
873 if (v == 0.0 || steady || small(i) > 0.5 || small(i - s) > 0.5 || !(unk(i) > 0.5) ||
874 !(unk(i - s) > 0.5))
875 return;
876 const double cLL = c(i - 2 * s), cL = c(i - s), cR = c(i), cRR = c(i + s);
877 bool fou = scheme == 0;
878 if (!fou)
879 fou = v > 0.0 ? !(unk(i - 2 * s) > 0.5 && unk(i - s) > 0.5 && unk(i) > 0.5)
880 : !(unk(i + s) > 0.5 && unk(i) > 0.5 && unk(i - s) > 0.5);
881 Phi(i) =
882 fou ? sadv::fou_flux(cL, cR, v)
883 : (scheme == 2 ? sadv::sou(cLL, cL, cR, cRR, v) : sadv::tvd(cLL, cL, cR, cRR, v));
884 });
885}
886
889 int g) {
890 ccFor3(
891 "peclet::flow::sco_explicit_rhs", C3{g, g, g}, C3{e.x - g, e.y - g, e.z - g},
892 KOKKOS_LAMBDA(int x, int y, int z) {
893 const long sx = 1, sy = e.x, sz = (long)e.x * e.y;
894 const long i = (long)x + (long)y * sy + (long)z * sz;
895 if (!(unk(i) > 0.5))
896 return;
897 b(i) -= ((Px(i + sx) - Px(i)) + (Py(i + sy) - Py(i))) + (Pz(i + sz) - Pz(i));
898 });
899}
900
905 CCConst small, CCConst unk, bool steady, C3 e, int g) {
906 ccFor3(
907 "peclet::flow::sco_adv_residual", C3{g, g, g}, C3{e.x - g, e.y - g, e.z - g},
908 KOKKOS_LAMBDA(int x, int y, int z) {
909 const long sx = 1, sy = e.x, sz = (long)e.x * e.y;
910 const long i = (long)x + (long)y * sy + (long)z * sz;
911 if (!(unk(i) > 0.5))
912 return;
913 const bool si = small(i) > 0.5;
914 const double F[6] = {-phx(i), phx(i + sx), -phy(i), phy(i + sy), -phz(i), phz(i + sz)};
915 const long nb[6] = {i - sx, i + sx, i - sy, i + sy, i - sz, i + sz};
916 const double ci = c(i);
917 double q[6];
918 for (int f = 0; f < 6; ++f) {
919 const bool imp = (steady || si || small(nb[f]) > 0.5) && unk(nb[f]) > 0.5;
920 q[f] = imp ? (F[f] > 0.0 ? F[f] * ci : F[f] * c(nb[f])) : 0.0;
921 }
922 r(i) -= ((q[0] + q[1]) + (q[2] + q[3])) + (q[4] + q[5]);
923 });
924}
925
926// ---- open domain faces (WO-5b; §1.4 B_i, §6.5, ruling D-WO5-3) ---------------------------------
927//
928// A flow inflow / outflow global face carries the boundary-face flux the projection constrained,
929// open * vel at the boundary face (the low face of the first inner cell, or the first GHOST index
930// on the high side), captured right after the projection (Solver::scalarCaptureOpenFaceFlux) as a
931// plane over the block's FULL transverse extent, index j1 + j2 * ext[t1] (faceTangents order).
932// openFaceFluxes writes it into phi_a at the boundary face, so Out_i, C_bulk and the small flags
933// of §6.3 see it; the interior kernels above skip every face toward a non-unknown, so its row is
934// carried here alone, per §1.4's domain-face table:
935// inflow b_i += F_in g_bf (g_bf the scalar's Dirichlet value; always on the rhs);
936// outflow F_out c_i (upwind on the inner cell: the zero-gradient exit), explicit at c^n on the
937// rhs, or implicit (AC += F_out) when the inner cell is small, and always when steady.
938// F_out is the volume flux per unit V out of the inner cell through that face: -phi on the low
939// side, +phi on the high side.
940
943inline void openFaceFluxes(CCField phi, Kokkos::View<const double*, CCMem> plane, CCConst unk,
944 int a, int side, C3 e, int g) {
945 int t1, t2;
946 faceTangents(a, t1, t2);
947 const int ext[3] = {e.x, e.y, e.z};
948 const long st[3] = {1, (long)e.x, (long)e.x * e.y};
949 const long sa = st[a], s1 = st[t1], s2 = st[t2];
950 const int m1 = ext[t1], m2 = ext[t2];
951 const long bfa = side == 0 ? g : ext[a] - g;
953 Kokkos::parallel_for(
954 "peclet::flow::sco_open_face_flux", MDRange2<CCExec>(space, {0, 0}, {m1, m2}),
955 KOKKOS_LAMBDA(int j1, int j2) {
956 const long bf = bfa * sa + (long)j1 * s1 + (long)j2 * s2;
957 const long in = side == 0 ? bf : bf - sa;
958 phi(bf) = unk(in) > 0.5 ? plane((long)j1 + (long)j2 * m1) : 0.0;
959 });
960 space.fence();
961}
962
968inline void openFaceAdvection(CCField AC, CCField b, CCField outflow, CCConst cOld, CCConst phi,
969 CCConst small, CCConst unk, Kokkos::View<const double*, CCMem> gv,
970 bool inflow, int a, int side, bool steady, C3 e, int g,
971 CCField omega = CCField(), bool wOmega = false) {
972 int t1, t2;
973 faceTangents(a, t1, t2);
974 const int ext[3] = {e.x, e.y, e.z};
975 const long st[3] = {1, (long)e.x, (long)e.x * e.y};
976 const int n1 = ext[t1] - 2 * g, n2 = ext[t2] - 2 * g;
977 const long sa = st[a], s1 = st[t1], s2 = st[t2];
978 const int aInner = side == 0 ? g : ext[a] - g - 1;
980 Kokkos::parallel_for(
981 "peclet::flow::sco_open_face_adv", MDRange2<CCExec>(space, {0, 0}, {n1, n2}),
982 KOKKOS_LAMBDA(int j1, int j2) {
983 const long i = (long)aInner * sa + (long)(j1 + g) * s1 + (long)(j2 + g) * s2;
984 if (!(unk(i) > 0.5))
985 return;
986 const double Fout = side == 0 ? -phi(i) : phi(i + sa);
987 if (inflow) {
988 b(i) -= Fout * gv((long)j1 + (long)j2 * n1);
989 } else if (steady || small(i) > 0.5) {
990 AC(i) += Fout;
991 outflow(i) += Fout;
992 if (wOmega)
993 omega(i) += Fout;
994 } else {
995 b(i) -= Fout * cOld(i);
996 }
997 });
998 space.fence();
999}
1000
1003inline void openFaceResidual(CCField r, CCConst c, CCConst phi, CCConst small, CCConst unk,
1004 bool inflow, int a, int side, bool steady, C3 e, int g) {
1005 if (inflow)
1006 return;
1007 int t1, t2;
1008 faceTangents(a, t1, t2);
1009 const int ext[3] = {e.x, e.y, e.z};
1010 const long st[3] = {1, (long)e.x, (long)e.x * e.y};
1011 const int n1 = ext[t1] - 2 * g, n2 = ext[t2] - 2 * g;
1012 const long sa = st[a], s1 = st[t1], s2 = st[t2];
1013 const int aInner = side == 0 ? g : ext[a] - g - 1;
1014 CCExec space;
1015 Kokkos::parallel_for(
1016 "peclet::flow::sco_open_face_residual", MDRange2<CCExec>(space, {0, 0}, {n1, n2}),
1017 KOKKOS_LAMBDA(int j1, int j2) {
1018 const long i = (long)aInner * sa + (long)(j1 + g) * s1 + (long)(j2 + g) * s2;
1019 if (!(unk(i) > 0.5) || !(steady || small(i) > 0.5))
1020 return;
1021 const double Fout = side == 0 ? -phi(i) : phi(i + sa);
1022 r(i) -= Fout * c(i);
1023 });
1024 space.fence();
1025}
1026
1030inline double openFaceInflux(CCConst c, CCConst cOld, CCConst phi, CCConst small, CCConst unk,
1031 Kokkos::View<const double*, CCMem> gv, bool inflow, double vol, int a,
1032 int side, bool steady, C3 e, int g) {
1033 int t1, t2;
1034 faceTangents(a, t1, t2);
1035 const int ext[3] = {e.x, e.y, e.z};
1036 const long st[3] = {1, (long)e.x, (long)e.x * e.y};
1037 const int n1 = ext[t1] - 2 * g, n2 = ext[t2] - 2 * g;
1038 const long sa = st[a], s1 = st[t1], s2 = st[t2];
1039 const int aInner = side == 0 ? g : ext[a] - g - 1;
1040 Kokkos::View<double*, CCMem> q("peclet::flow::sco_open_face_influx", (long)n1 * n2);
1041 CCExec space;
1042 Kokkos::parallel_for(
1043 "peclet::flow::sco_open_face_influx", MDRange2<CCExec>(space, {0, 0}, {n1, n2}),
1044 KOKKOS_LAMBDA(int j1, int j2) {
1045 const long i = (long)aInner * sa + (long)(j1 + g) * s1 + (long)(j2 + g) * s2;
1046 const long k = (long)j1 + (long)j2 * n1;
1047 if (!(unk(i) > 0.5)) {
1048 q(k) = 0.0;
1049 return;
1050 }
1051 const double Fout = side == 0 ? -phi(i) : phi(i + sa);
1052 const double cv = inflow ? gv(k) : ((steady || small(i) > 0.5) ? c(i) : cOld(i));
1053 q(k) = -vol * (Fout * cv);
1054 });
1055 space.fence();
1056 auto h = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace(), q);
1057 double s = 0.0;
1058 for (long k = 0; k < (long)h.extent(0); ++k)
1059 s += h(k);
1060 return s;
1061}
1062
1068inline void openFaceCountsLocal(CCConst phi, CCConst small, CCConst unk, bool inflow, int a,
1069 int side, bool steady, C3 e, int g, long& nFlux, long& nImpl,
1070 long& nBack) {
1071 int t1, t2;
1072 faceTangents(a, t1, t2);
1073 const int ext[3] = {e.x, e.y, e.z};
1074 const long st[3] = {1, (long)e.x, (long)e.x * e.y};
1075 const int n1 = ext[t1] - 2 * g, n2 = ext[t2] - 2 * g;
1076 const long sa = st[a], s1 = st[t1], s2 = st[t2];
1077 const int aInner = side == 0 ? g : ext[a] - g - 1;
1078 CCExec space;
1079 long p = 0, q = 0, r = 0;
1080 Kokkos::parallel_reduce(
1081 "peclet::flow::sco_open_face_counts", MDRange2<CCExec>(space, {0, 0}, {n1, n2}),
1082 KOKKOS_LAMBDA(int j1, int j2, long& c1, long& c2, long& c3) {
1083 const long i = (long)aInner * sa + (long)(j1 + g) * s1 + (long)(j2 + g) * s2;
1084 const double v = side == 0 ? phi(i) : phi(i + sa);
1085 if (!(unk(i) > 0.5) || v == 0.0)
1086 return;
1087 if (side == 1)
1088 ++c1;
1089 if (!inflow && (steady || small(i) > 0.5))
1090 ++c2;
1091 if (!inflow && (side == 0 ? v > 0.0 : v < 0.0)) // F_out < 0
1092 ++c3;
1093 },
1094 p, q, r);
1095 nFlux = p;
1096 nImpl = q;
1097 nBack = r;
1098}
1099
1101inline double maxFacetLocal(Kokkos::View<const double*, CCMem> cw) {
1102 double m = 0.0;
1103 Kokkos::parallel_reduce(
1104 "peclet::flow::sco_max_cw", Kokkos::RangePolicy<CCExec>(0, (long)cw.extent(0)),
1105 KOKKOS_LAMBDA(const long f, double& acc) {
1106 if (cw(f) > acc)
1107 acc = cw(f);
1108 },
1109 Kokkos::Max<double>(m));
1110 return m;
1111}
1112
1113// ---- mean-gradient (closure) mode (WO-6, §1.5) --------------------------------------------------
1114//
1115// The unknown is theta, c = G.x + theta. Wherever the discretization evaluates c at a point x it
1116// uses theta + G.x and moves the G.x part to the right-hand side. Face differences use the TRUE
1117// displacement +-h'_a e_a (never a wrapped index difference); a probe stencil cell or a domain-face
1118// centre uses its global position, unwrapped relative to the cut cell (an extended-block index
1119// past a periodic face is simply its unwrapped position).
1120
1123 double G[3] = {0.0, 0.0, 0.0};
1124 double org[3] = {0.0, 0.0, 0.0};
1125 double h[3] = {1.0, 1.0, 1.0};
1126 long base[3] = {0, 0, 0};
1127 long sy = 1, sz = 1;
1128 KOKKOS_INLINE_FUNCTION double operator()(long j) const {
1129 const long z = j / sz, y = (j - z * sz) / sy, x = j - z * sz - y * sy;
1130 const double px = org[0] + ((double)(base[0] + x) + 0.5) * h[0];
1131 const double py = org[1] + ((double)(base[1] + y) + 0.5) * h[1];
1132 const double pz = org[2] + ((double)(base[2] + z) + 0.5) * h[2];
1133 return (G[0] * px + G[1] * py) + G[2] * pz;
1134 }
1135};
1136
1140inline void meanGradientFaceRhs(CCField b, CCConst unk, CCConst sax, CCConst say, CCConst saz,
1141 const double gf[3], C3 e, int g) {
1142 const double gx = gf[0], gy = gf[1], gz = gf[2];
1143 ccFor3(
1144 "peclet::flow::sco_mg_face_rhs", C3{g, g, g}, C3{e.x - g, e.y - g, e.z - g},
1145 KOKKOS_LAMBDA(int x, int y, int z) {
1146 const long sx = 1, sy = e.x, sz = (long)e.x * e.y;
1147 const long i = (long)x + (long)y * sy + (long)z * sz;
1148 if (!(unk(i) > 0.5))
1149 return;
1150 const double aw = unk(i - sx) > 0.5 ? sax(i) : 0.0;
1151 const double ae = unk(i + sx) > 0.5 ? sax(i + sx) : 0.0;
1152 const double as = unk(i - sy) > 0.5 ? say(i) : 0.0;
1153 const double an = unk(i + sy) > 0.5 ? say(i + sy) : 0.0;
1154 const double ab = unk(i - sz) > 0.5 ? saz(i) : 0.0;
1155 const double at = unk(i + sz) > 0.5 ? saz(i + sz) : 0.0;
1156 b(i) += (gx * (ae - aw) + gy * (an - as)) + gz * (at - ab);
1157 });
1158}
1159
1163inline void meanGradientProbeShift(Kokkos::View<double*, CCMem> rw,
1165 Kokkos::View<const double*, CCMem> cw,
1166 const MeanGradPosition& pos) {
1167 auto fcell = fo.cell;
1168 auto offF = fo.offF;
1169 auto wF = fo.wF;
1170 CCExec space;
1171 Kokkos::parallel_for(
1172 "peclet::flow::sco_mg_probe_shift", Kokkos::RangePolicy<CCExec>(space, 0, fo.n),
1173 KOKKOS_LAMBDA(const long f) {
1174 if (cw(f) == 0.0)
1175 return;
1176 const long i = fcell(f);
1177 double gxp = 0.0;
1178 for (int k = 0; k < 8; ++k)
1179 gxp += wF(f, k) * pos(i + (long)offF(f, k));
1180 rw(f) -= cw(f) * gxp;
1181 });
1182 space.fence();
1183}
1184
1188 long i, long sy, long sz, const double hh[3],
1189 double u[3]) {
1190 u[0] = hh[0] * (phx(i + 1) + phx(i));
1191 u[1] = hh[1] * (phy(i + sy) + phy(i));
1192 u[2] = hh[2] * (phz(i + sz) + phz(i));
1193}
1194
1198 const double hh[3], C3 e, int g, double s[3]) {
1199 CCExec space;
1200 const double h0 = hh[0], h1 = hh[1], h2 = hh[2];
1201 double a = 0.0, b = 0.0, c = 0.0;
1202 Kokkos::parallel_reduce(
1203 "peclet::flow::sco_mg_cell_velocity_sum",
1204 MDRange3<CCExec>(space, {g, g, g}, {e.x - g, e.y - g, e.z - g}),
1205 KOKKOS_LAMBDA(int x, int y, int z, double& p1, double& p2, double& p3) {
1206 const long sy = e.x, sz = (long)e.x * e.y;
1207 const long i = (long)x + (long)y * sy + (long)z * sz;
1208 if (!(unk(i) > 0.5))
1209 return;
1210 const double hq[3] = {h0, h1, h2};
1211 double u[3];
1212 cellVelocity(phx, phy, phz, i, sy, sz, hq, u);
1213 p1 += u[0];
1214 p2 += u[1];
1215 p3 += u[2];
1216 },
1217 a, b, c);
1218 s[0] = a;
1219 s[1] = b;
1220 s[2] = c;
1221}
1222
1227 CCConst kappa, CCConst unk, const double gi[3],
1228 const double hh[3], const double ub[3], C3 e, int g) {
1229 const double g0 = gi[0], g1 = gi[1], g2 = gi[2], h0 = hh[0], h1 = hh[1], h2 = hh[2], u0 = ub[0],
1230 u1 = ub[1], u2 = ub[2];
1231 ccFor3(
1232 "peclet::flow::sco_mg_adv_rhs", C3{g, g, g}, C3{e.x - g, e.y - g, e.z - g},
1233 KOKKOS_LAMBDA(int x, int y, int z) {
1234 const long sy = e.x, sz = (long)e.x * e.y;
1235 const long i = (long)x + (long)y * sy + (long)z * sz;
1236 if (!(unk(i) > 0.5))
1237 return;
1238 const double hq[3] = {h0, h1, h2};
1239 double u[3];
1240 cellVelocity(phx, phy, phz, i, sy, sz, hq, u);
1241 const double k = kappa(i);
1242 b(i) -= (g0 * (u[0] - k * u0) + g1 * (u[1] - k * u1)) + g2 * (u[2] - k * u2);
1243 });
1244}
1245
1250inline void meanFluxLocal(CCConst th, CCConst kappa, CCConst unk, CCConst sax, CCConst say,
1251 CCConst saz, CCConst phx, CCConst phy, CCConst phz, bool advecting,
1252 double lam, const double gi[3], const double hp[3], const double ub[3],
1253 C3 e, int g, double diff[3], double adv[3]) {
1254 CCExec space;
1255 const double g0 = gi[0], g1 = gi[1], g2 = gi[2], hp0 = hp[0], hp1 = hp[1], hp2 = hp[2];
1256 const double h0 = 0.5 * hp[0], h1 = 0.5 * hp[1], h2 = 0.5 * hp[2], u0 = ub[0], u1 = ub[1],
1257 u2 = ub[2];
1258 double d0 = 0.0, d1 = 0.0, d2 = 0.0;
1259 Kokkos::parallel_reduce(
1260 "peclet::flow::sco_mean_flux_diff",
1261 MDRange3<CCExec>(space, {g, g, g}, {e.x - g, e.y - g, e.z - g}),
1262 KOKKOS_LAMBDA(int x, int y, int z, double& p1, double& p2, double& p3) {
1263 const long sy = e.x, sz = (long)e.x * e.y;
1264 const long i = (long)x + (long)y * sy + (long)z * sz;
1265 if (!(unk(i) > 0.5))
1266 return;
1267 if (unk(i - 1) > 0.5)
1268 p1 += -lam * sax(i) * ((th(i) - th(i - 1)) + g0 * hp0) / hp0;
1269 if (unk(i - sy) > 0.5)
1270 p2 += -lam * say(i) * ((th(i) - th(i - sy)) + g1 * hp1) / hp1;
1271 if (unk(i - sz) > 0.5)
1272 p3 += -lam * saz(i) * ((th(i) - th(i - sz)) + g2 * hp2) / hp2;
1273 },
1274 d0, d1, d2);
1275 diff[0] = d0;
1276 diff[1] = d1;
1277 diff[2] = d2;
1278 adv[0] = adv[1] = adv[2] = 0.0;
1279 if (!advecting)
1280 return;
1281 double a0 = 0.0, a1 = 0.0, a2 = 0.0;
1282 Kokkos::parallel_reduce(
1283 "peclet::flow::sco_mean_flux_adv",
1284 MDRange3<CCExec>(space, {g, g, g}, {e.x - g, e.y - g, e.z - g}),
1285 KOKKOS_LAMBDA(int x, int y, int z, double& p1, double& p2, double& p3) {
1286 const long sy = e.x, sz = (long)e.x * e.y;
1287 const long i = (long)x + (long)y * sy + (long)z * sz;
1288 if (!(unk(i) > 0.5))
1289 return;
1290 const double hq[3] = {h0, h1, h2};
1291 double u[3];
1292 cellVelocity(phx, phy, phz, i, sy, sz, hq, u);
1293 const double k = kappa(i), t = th(i);
1294 p1 += (u[0] - k * u0) * t;
1295 p2 += (u[1] - k * u1) * t;
1296 p3 += (u[2] - k * u2) * t;
1297 },
1298 a0, a1, a2);
1299 adv[0] = a0;
1300 adv[1] = a1;
1301 adv[2] = a2;
1302}
1303
1304// ---- conjugate transport (WO-7; §1.1, §1.3, §1.4, §2.7, §3.4, §4) -------------------------------
1305//
1306// Two fields on one grid: the fluid c and the solid psi_s = c_s / K (the psi form of §1.1, in which
1307// the interface conditions are flux continuity and q = (psi_s - psi_f)/R_c). Per phase the 7 bands
1308// of §4.1; the solid's hold C_s K kappa_s idt and the face diffusion Lam_face w_a a_s, a_s = 1 -
1309// a^snap, on faces whose two cells are solid unknowns (Lam_face the harmonic mean of the two
1310// cells' Lam_s when their materials differ). The overlay adds, per conjugate facet, cc = alpha G_c
1311// times (u_pf - u_ps) to the fluid row and its negative to the solid row of the same cell: one
1312// number entering two rows with opposite signs (§1.6.1).
1313
1317 Kokkos::View<int*, CCMem> conj;
1318 Kokkos::View<double*, CCMem> lam, ck, kp, rc;
1319 int nb = 0;
1320};
1321
1323KOKKOS_INLINE_FUNCTION int bodyIndex(double m, int nb) {
1324 const int b = (int)m;
1325 return (b < 0 || b >= nb) ? 0 : b;
1326}
1327
1330KOKKOS_INLINE_FUNCTION double solidFaceLam(const Kokkos::View<double*, CCMem>& lam, int ma,
1331 int mb) {
1332 const double la = lam(ma);
1333 if (ma == mb)
1334 return la;
1335 const double lb = lam(mb);
1336 return (la + lb) > 0.0 ? 2.0 * la * lb / (la + lb) : 0.0;
1337}
1338
1348inline void buildSolidMaterial(CCField mat, CCField sunk, CCConst kappa, CCConst sax, CCConst say,
1350 Kokkos::View<const int*, CCMem> owner, bool hasOwner,
1351 const MaterialTable& mt, C3 e, int g, double vol[2]) {
1352 const int nx = e.x - 2 * g, ny = e.y - 2 * g;
1353 Kokkos::deep_copy(CCExec(), mat, -1.0);
1354 Kokkos::deep_copy(CCExec(), sunk, 0.0); // first: the conjugate-facet marker
1355 ccFor3(
1356 "peclet::flow::sco_solid_owner", C3{g, g, g}, C3{e.x - g, e.y - g, e.z - g},
1357 KOKKOS_LAMBDA(int x, int y, int z) {
1358 const long i = (long)x + (long)y * e.x + (long)z * (long)e.x * e.y;
1359 const long ii = (long)(x - g) + (long)(y - g) * nx + (long)(z - g) * (long)nx * ny;
1360 mat(i) = hasOwner ? (double)owner(ii) : 0.0;
1361 });
1362 auto cutCell = fo.cutCell;
1363 auto cfs = fo.cellFacetStart;
1364 auto alpha = fo.alpha;
1365 auto body = fo.body;
1366 auto conj = mt.conj;
1367 const int nb = mt.nb;
1368 CCExec space;
1369 Kokkos::parallel_for(
1370 "peclet::flow::sco_solid_facet_material", Kokkos::RangePolicy<CCExec>(space, 0, fo.nCut),
1371 KOKKOS_LAMBDA(const long c) {
1372 int best = -1, bestAny = -1;
1373 double aBest = -1.0, aAny = -1.0;
1374 for (int f = cfs(c); f < cfs(c + 1); ++f) {
1375 const int b = bodyIndex((double)body(f), nb);
1376 if (alpha(f) > aAny) {
1377 aAny = alpha(f);
1378 bestAny = b;
1379 }
1380 if (conj(b) != 0 && alpha(f) > aBest) {
1381 aBest = alpha(f);
1382 best = b;
1383 }
1384 }
1385 mat(cutCell(c)) = (double)(best >= 0 ? best : bestAny);
1386 sunk(cutCell(c)) = best >= 0 ? 1.0 : 0.0; // the cell has a conjugate facet
1387 });
1388 space.fence();
1389 double v0 = 0.0, v1 = 0.0;
1390 Kokkos::parallel_reduce(
1391 "peclet::flow::sco_solid_flags",
1392 MDRange3<CCExec>(space, {g, g, g}, {e.x - g, e.y - g, e.z - g}),
1393 KOKKOS_LAMBDA(int x, int y, int z, double& pv, double& ps) {
1394 const long sy = e.x, sz = (long)e.x * e.y;
1395 const long i = (long)x + (long)y * sy + (long)z * sz;
1396 const double m = mat(i);
1397 const bool cand = kappa(i) < 1.0 && m >= 0.0 && conj(bodyIndex(m, nb)) != 0;
1398 const double asSum =
1399 (((1.0 - sax(i)) + (1.0 - sax(i + 1))) + ((1.0 - say(i)) + (1.0 - say(i + sy)))) +
1400 ((1.0 - saz(i)) + (1.0 - saz(i + sz)));
1401 const bool u = cand && (asSum > 0.0 || sunk(i) > 0.5);
1402 if (cand) {
1403 pv += 1.0 - kappa(i);
1404 if (!u)
1405 ps += 1.0 - kappa(i);
1406 }
1407 sunk(i) = u ? 1.0 : 0.0;
1408 },
1409 v0, v1);
1410 vol[0] = v0;
1411 vol[1] = v1;
1412}
1413
1416inline void buildSolidProbes(Kokkos::View<double*, CCMem> sS,
1417 Kokkos::View<int* [8], Kokkos::LayoutLeft, CCMem> offS,
1418 Kokkos::View<double* [8], Kokkos::LayoutLeft, CCMem> wS,
1419 Kokkos::View<std::uint8_t*, CCMem> rungS,
1420 Kokkos::View<const std::uint8_t*, CCMem> conjF,
1421 const scg::ScalarFacetOverlay& fo, CCConst sdf, CCConst sunk, C3 e,
1422 const double hp[3]) {
1423 CCExec space;
1424 const long sy = e.x, sz = (long)e.x * e.y;
1425 const double h0 = hp[0], h1 = hp[1], h2 = hp[2];
1426 auto fcell = fo.cell;
1427 auto fnormal = fo.normal;
1428 auto fcen = fo.centroid;
1429 Kokkos::parallel_for(
1430 "peclet::flow::sco_solid_probes", Kokkos::RangePolicy<CCExec>(space, 0, fo.n),
1431 KOKKOS_LAMBDA(const long f) {
1432 if (conjF(f) == 0) {
1433 sS(f) = 0.0;
1434 rungS(f) = 0;
1435 for (int k = 0; k < 8; ++k) {
1436 offS(f, k) = 0;
1437 wS(f, k) = 0.0;
1438 }
1439 return;
1440 }
1441 const double h[3] = {h0, h1, h2};
1442 const double xw[3] = {fcen(f, 0), fcen(f, 1), fcen(f, 2)};
1443 const double n[3] = {-fnormal(f, 0), -fnormal(f, 1), -fnormal(f, 2)};
1444 const scg::BlockLookup L{sdf, sunk, fcell(f), sy, sz, -1.0};
1445 const peclet::core::scheme::ProbeResult r = peclet::core::scheme::buildProbe(xw, n, h, L);
1446 sS(f) = r.s;
1447 rungS(f) = (std::uint8_t)r.rung;
1448 for (int k = 0; k < 8; ++k) {
1449 const bool used = k < r.n;
1450 offS(f, k) =
1451 used ? (int)((long)r.off[k][0] + (long)r.off[k][1] * sy + (long)r.off[k][2] * sz) : 0;
1452 wS(f, k) = used ? r.w[k] : 0.0;
1453 }
1454 });
1455 space.fence();
1456}
1457
1461inline void solidFaceProducts(CCField spx, CCField spy, CCField spz, CCConst sunk, CCConst mat,
1462 CCConst sax, CCConst say, CCConst saz, const MaterialTable& mt,
1463 C3 e) {
1464 auto lam = mt.lam;
1465 const int nb = mt.nb;
1466 ccFor3(
1467 "peclet::flow::sco_solid_face_products", C3{0, 0, 0}, e, KOKKOS_LAMBDA(int x, int y, int z) {
1468 const long st[3] = {1, (long)e.x, (long)e.x * e.y};
1469 const long i = (long)x + (long)y * st[1] + (long)z * st[2];
1470 const int xa[3] = {x, y, z};
1471 const bool ui = sunk(i) > 0.5;
1472 double P[3];
1473 for (int a = 0; a < 3; ++a) {
1474 const long j = i - st[a];
1475 if (xa[a] < 1 || !ui || !(sunk(j) > 0.5)) {
1476 P[a] = 0.0;
1477 continue;
1478 }
1479 const double ap = a == 0 ? sax(i) : (a == 1 ? say(i) : saz(i));
1480 P[a] = solidFaceLam(lam, bodyIndex(mat(i), nb), bodyIndex(mat(j), nb)) * (1.0 - ap);
1481 }
1482 spx(i) = P[0];
1483 spy(i) = P[1];
1484 spz(i) = P[2];
1485 });
1486}
1487
1489inline void solidMass(CCField ms, CCConst kappa, CCConst sunk, CCConst mat, const MaterialTable& mt,
1490 double idt, C3 e, int g) {
1491 auto ck = mt.ck;
1492 const int nb = mt.nb;
1493 Kokkos::deep_copy(CCExec(), ms, 0.0);
1494 ccFor3(
1495 "peclet::flow::sco_solid_mass", C3{g, g, g}, C3{e.x - g, e.y - g, e.z - g},
1496 KOKKOS_LAMBDA(int x, int y, int z) {
1497 const long i = (long)x + (long)y * e.x + (long)z * (long)e.x * e.y;
1498 if (sunk(i) > 0.5)
1499 ms(i) = ck(bodyIndex(mat(i), nb)) * (1.0 - kappa(i)) * idt;
1500 });
1501}
1502
1505inline void solidBands(CCField AC, CCField AW, CCField AE, CCField AS, CCField AN, CCField AB,
1506 CCField AT, CCConst ms, CCConst sunk, CCConst spx, CCConst spy, CCConst spz,
1507 const double w[3], C3 e, int g) {
1508 const double wx = w[0], wy = w[1], wz = w[2];
1509 ccFor3(
1510 "peclet::flow::sco_solid_bands", C3{g, g, g}, C3{e.x - g, e.y - g, e.z - g},
1511 KOKKOS_LAMBDA(int x, int y, int z) {
1512 const long sx = 1, sy = e.x, sz = (long)e.x * e.y;
1513 const long i = (long)x + (long)y * sy + (long)z * sz;
1514 if (!(sunk(i) > 0.5)) {
1515 AC(i) = 1.0;
1516 AW(i) = AE(i) = AS(i) = AN(i) = AB(i) = AT(i) = 0.0;
1517 return;
1518 }
1519 const double tw = wx * spx(i), te = wx * spx(i + sx);
1520 const double ts = wy * spy(i), tn = wy * spy(i + sy);
1521 const double tb = wz * spz(i), tt = wz * spz(i + sz);
1522 AW(i) = -tw;
1523 AE(i) = -te;
1524 AS(i) = -ts;
1525 AN(i) = -tn;
1526 AB(i) = -tb;
1527 AT(i) = -tt;
1528 AC(i) = ms(i) + (((tw + te) + (ts + tn)) + (tb + tt));
1529 });
1530}
1531
1533inline void solidRhs(CCField bs, CCConst ms, CCConst sOld, C3 e, int g) {
1534 ccFor3(
1535 "peclet::flow::sco_solid_rhs", C3{g, g, g}, C3{e.x - g, e.y - g, e.z - g},
1536 KOKKOS_LAMBDA(int x, int y, int z) {
1537 const long i = (long)x + (long)y * e.x + (long)z * (long)e.x * e.y;
1538 bs(i) = ms(i) * sOld(i);
1539 });
1540}
1541
1545inline void conjugateCoefficients(Kokkos::View<double*, CCMem> cc,
1547 Kokkos::View<const std::uint8_t*, CCMem> conjF,
1548 Kokkos::View<const double*, CCMem> sS, CCConst unk, CCConst sunk,
1549 const MaterialTable& mt, double lamF) {
1550 auto fcell = fo.cell;
1551 auto falpha = fo.alpha;
1552 auto fbody = fo.body;
1553 auto sF = fo.sF;
1554 auto lam = mt.lam;
1555 auto rc = mt.rc;
1556 const int nb = mt.nb;
1557 CCExec space;
1558 Kokkos::parallel_for(
1559 "peclet::flow::sco_conj_coeffs", Kokkos::RangePolicy<CCExec>(space, 0, fo.n),
1560 KOKKOS_LAMBDA(const long f) {
1561 const long i = fcell(f);
1562 if (conjF(f) == 0 || !(unk(i) > 0.5) || !(sunk(i) > 0.5)) {
1563 cc(f) = 0.0;
1564 return;
1565 }
1566 const int b = bodyIndex((double)fbody(f), nb);
1567 cc(f) = falpha(f) *
1568 peclet::core::scheme::interfaceConductance(sF(f), lamF, rc(b), sS(f), lam(b));
1569 });
1570 space.fence();
1571}
1572
1578 Kokkos::View<const double*, CCMem> cw,
1579 Kokkos::View<const double*, CCMem> cc,
1580 Kokkos::View<int* [8], Kokkos::LayoutLeft, CCMem> offS,
1581 Kokkos::View<double* [8], Kokkos::LayoutLeft, CCMem> wS,
1582 bool subtract = false) {
1583 auto cutCell = fo.cutCell;
1584 auto cfs = fo.cellFacetStart;
1585 auto offF = fo.offF;
1586 auto wF = fo.wF;
1587 CCExec space;
1588 Kokkos::parallel_for(
1589 "peclet::flow::sco_conj_overlay_apply", Kokkos::RangePolicy<CCExec>(space, 0, fo.nCut),
1590 KOKKOS_LAMBDA(const long c) {
1591 const long i = cutCell(c);
1592 double af = 0.0, as = 0.0;
1593 for (int f = cfs(c); f < cfs(c + 1); ++f) {
1594 double upf = 0.0;
1595 for (int k = 0; k < 8; ++k)
1596 upf += wF(f, k) * xf(i + (long)offF(f, k));
1597 af += cw(f) * upf;
1598 if (cc(f) != 0.0) {
1599 double ups = 0.0;
1600 for (int k = 0; k < 8; ++k)
1601 ups += wS(f, k) * xs(i + (long)offS(f, k));
1602 const double q = cc(f) * (upf - ups);
1603 af += q;
1604 as -= q;
1605 }
1606 }
1607 if (subtract) {
1608 yf(i) -= af;
1609 ys(i) -= as;
1610 } else {
1611 yf(i) += af;
1612 ys(i) += as;
1613 }
1614 });
1615}
1616
1620 const scg::ScalarFacetOverlay& fo, Kokkos::View<const double*, CCMem> cw,
1621 Kokkos::View<const double*, CCMem> cc) {
1622 Kokkos::deep_copy(CCExec(), SACf, ACf);
1623 Kokkos::deep_copy(CCExec(), SACs, ACs);
1624 Kokkos::deep_copy(CCExec(), W0, 0.0);
1625 auto cutCell = fo.cutCell;
1626 auto cfs = fo.cellFacetStart;
1627 CCExec space;
1628 Kokkos::parallel_for(
1629 "peclet::flow::sco_conj_surrogate", Kokkos::RangePolicy<CCExec>(space, 0, fo.nCut),
1630 KOKKOS_LAMBDA(const long c) {
1631 double aw = 0.0, ac = 0.0;
1632 for (int f = cfs(c); f < cfs(c + 1); ++f) {
1633 aw += cw(f) + cc(f);
1634 ac += cc(f);
1635 }
1636 const long i = cutCell(c);
1637 SACf(i) += aw;
1638 SACs(i) += ac;
1639 W0(i) = ac;
1640 });
1641 space.fence();
1642}
1643
1648 Kokkos::View<const double*, CCMem> cc,
1649 Kokkos::View<int* [8], Kokkos::LayoutLeft, CCMem> offS,
1650 Kokkos::View<double* [8], Kokkos::LayoutLeft, CCMem> wS,
1651 const MeanGradPosition& pos) {
1652 auto cutCell = fo.cutCell;
1653 auto cfs = fo.cellFacetStart;
1654 auto offF = fo.offF;
1655 auto wF = fo.wF;
1656 CCExec space;
1657 Kokkos::parallel_for(
1658 "peclet::flow::sco_conj_mg_shift", Kokkos::RangePolicy<CCExec>(space, 0, fo.nCut),
1659 KOKKOS_LAMBDA(const long c) {
1660 const long i = cutCell(c);
1661 double d = 0.0;
1662 for (int f = cfs(c); f < cfs(c + 1); ++f) {
1663 if (cc(f) == 0.0)
1664 continue;
1665 double gf = 0.0, gs = 0.0;
1666 for (int k = 0; k < 8; ++k) {
1667 gf += wF(f, k) * pos(i + (long)offF(f, k));
1668 gs += wS(f, k) * pos(i + (long)offS(f, k));
1669 }
1670 d += cc(f) * (gs - gf);
1671 }
1672 bf(i) += d;
1673 bs(i) -= d;
1674 });
1675 space.fence();
1676}
1677
1682 CCConst spz, const double gs[3], C3 e, int g) {
1683 const double gx = gs[0], gy = gs[1], gz = gs[2];
1684 ccFor3(
1685 "peclet::flow::sco_solid_mg_face_rhs", C3{g, g, g}, C3{e.x - g, e.y - g, e.z - g},
1686 KOKKOS_LAMBDA(int x, int y, int z) {
1687 const long sx = 1, sy = e.x, sz = (long)e.x * e.y;
1688 const long i = (long)x + (long)y * sy + (long)z * sz;
1689 if (!(sunk(i) > 0.5))
1690 return;
1691 bs(i) += (gx * (spx(i + sx) - spx(i)) + gy * (spy(i + sy) - spy(i))) +
1692 gz * (spz(i + sz) - spz(i));
1693 });
1694}
1695
1699 CCConst spx, CCConst spy, CCConst spz, const double w[3], C3 e,
1700 int g) {
1701 const double wx = w[0], wy = w[1], wz = w[2];
1702 ccFor3(
1703 "peclet::flow::sco_solid_residual_flux", C3{g, g, g}, C3{e.x - g, e.y - g, e.z - g},
1704 KOKKOS_LAMBDA(int lx, int ly, int lz) {
1705 const long sx = 1, sy = e.x, sz = (long)e.x * e.y;
1706 const long i = (long)lx + (long)ly * sy + (long)lz * sz;
1707 if (!(sunk(i) > 0.5)) {
1708 r(i) = 0.0;
1709 return;
1710 }
1711 const double ci = x(i);
1712 const double flux =
1713 ((wx * spx(i) * (ci - x(i - sx)) + wx * spx(i + sx) * (ci - x(i + sx))) +
1714 (wy * spy(i) * (ci - x(i - sy)) + wy * spy(i + sy) * (ci - x(i + sy)))) +
1715 (wz * spz(i) * (ci - x(i - sz)) + wz * spz(i + sz) * (ci - x(i + sz)));
1716 r(i) = b(i) - ms(i) * ci - flux;
1717 });
1718}
1719
1722inline void solidMomentsLocal(CCConst xs, CCConst kappa, CCConst sunk, CCConst mat,
1723 const MaterialTable& mt, C3 e, int g, double& m, double& w) {
1724 auto ck = mt.ck;
1725 const int nb = mt.nb;
1726 CCExec space;
1727 double s1 = 0.0, s2 = 0.0;
1728 Kokkos::parallel_reduce(
1729 "peclet::flow::sco_solid_moments",
1730 MDRange3<CCExec>(space, {g, g, g}, {e.x - g, e.y - g, e.z - g}),
1731 KOKKOS_LAMBDA(int x, int y, int z, double& p1, double& p2) {
1732 const long i = (long)x + (long)y * e.x + (long)z * (long)e.x * e.y;
1733 if (sunk(i) > 0.5) {
1734 const double wk = ck(bodyIndex(mat(i), nb)) * (1.0 - kappa(i));
1735 p1 += wk * xs(i);
1736 p2 += wk;
1737 }
1738 },
1739 s1, s2);
1740 m = s1;
1741 w = s2;
1742}
1743
1747inline void conjFacetFlux(Kokkos::View<double*, CCMem> up, Kokkos::View<double*, CCMem> us,
1748 Kokkos::View<double*, CCMem> qin, CCConst xf, CCConst xs,
1749 const scg::ScalarFacetOverlay& fo, Kokkos::View<const double*, CCMem> cw,
1750 Kokkos::View<const double*, CCMem> rw,
1751 Kokkos::View<const double*, CCMem> cc,
1752 Kokkos::View<int* [8], Kokkos::LayoutLeft, CCMem> offS,
1753 Kokkos::View<double* [8], Kokkos::LayoutLeft, CCMem> wS,
1754 const MeanGradPosition& pos, bool mgOn) {
1755 auto fcell = fo.cell;
1756 auto offF = fo.offF;
1757 auto wF = fo.wF;
1758 CCExec space;
1759 Kokkos::parallel_for(
1760 "peclet::flow::sco_conj_facet_flux", Kokkos::RangePolicy<CCExec>(space, 0, fo.n),
1761 KOKKOS_LAMBDA(const long f) {
1762 const long i = fcell(f);
1763 double u = 0.0, v = 0.0;
1764 for (int k = 0; k < 8; ++k)
1765 u += wF(f, k) * xf(i + (long)offF(f, k));
1766 for (int k = 0; k < 8; ++k)
1767 v += wS(f, k) * xs(i + (long)offS(f, k));
1768 up(f) = u;
1769 us(f) = v;
1770 if (cc(f) != 0.0) {
1771 // in mean-gradient mode the probes hold theta: the flux is that of theta + G.x (§1.5)
1772 double d = v - u;
1773 if (mgOn) {
1774 double gf = 0.0, gs = 0.0;
1775 for (int k = 0; k < 8; ++k) {
1776 gf += wF(f, k) * pos(i + (long)offF(f, k));
1777 gs += wS(f, k) * pos(i + (long)offS(f, k));
1778 }
1779 d += gs - gf;
1780 }
1781 qin(f) = cc(f) * d;
1782 } else {
1783 qin(f) = rw(f) - cw(f) * u;
1784 }
1785 });
1786 space.fence();
1787}
1788
1790template <class R>
1791inline void dotTwoReduce(CCConst af, CCConst bf, CCConst as, CCConst bs, C3 e, int g, R&& result) {
1792 ccReduce3(
1793 "peclet::flow::sco_dot_two", C3{g, g, g}, C3{e.x - g, e.y - g, e.z - g},
1794 KOKKOS_LAMBDA(int x, int y, int z, double& acc) {
1795 const long i = (long)x + (long)y * e.x + (long)z * (long)e.x * e.y;
1796 acc += af(i) * bf(i) + as(i) * bs(i);
1797 },
1798 std::forward<R>(result));
1799}
1800inline double dotTwoLocal(CCConst af, CCConst bf, CCConst as, CCConst bs, C3 e, int g) {
1801 double s = 0.0;
1802 dotTwoReduce(af, bf, as, bs, e, g, s);
1803 return s;
1804}
1805
1807template <class R1, class R2>
1809 C3 e, int g, R1&& r1, R2&& r2) {
1810 CCExec space;
1811 Kokkos::parallel_reduce(
1812 "peclet::flow::sco_dot2_two", MDRange3<CCExec>(space, {g, g, g}, {e.x - g, e.y - g, e.z - g}),
1813 KOKKOS_LAMBDA(int x, int y, int z, double& p1, double& p2) {
1814 const long i = (long)x + (long)y * e.x + (long)z * (long)e.x * e.y;
1815 p1 += af(i) * bf(i) + as(i) * bs(i);
1816 p2 += cf(i) * cf(i) + cs(i) * cs(i);
1817 },
1818 std::forward<R1>(r1), std::forward<R2>(r2));
1819}
1821 C3 e, int g, double& ab, double& cc) {
1822 double s1 = 0.0, s2 = 0.0;
1823 dot2TwoReduce(af, bf, cf, as, bs, cs, e, g, s1, s2);
1824 ab = s1;
1825 cc = s2;
1826}
1827
1830inline void solidMeanFluxLocal(CCConst th, CCConst sunk, CCConst spx, CCConst spy, CCConst spz,
1831 const double gi[3], const double hp[3], C3 e, int g,
1832 double diff[3]) {
1833 CCExec space;
1834 const double g0 = gi[0], g1 = gi[1], g2 = gi[2], hp0 = hp[0], hp1 = hp[1], hp2 = hp[2];
1835 double d0 = 0.0, d1 = 0.0, d2 = 0.0;
1836 Kokkos::parallel_reduce(
1837 "peclet::flow::sco_solid_mean_flux",
1838 MDRange3<CCExec>(space, {g, g, g}, {e.x - g, e.y - g, e.z - g}),
1839 KOKKOS_LAMBDA(int x, int y, int z, double& p1, double& p2, double& p3) {
1840 const long sy = e.x, sz = (long)e.x * e.y;
1841 const long i = (long)x + (long)y * sy + (long)z * sz;
1842 if (!(sunk(i) > 0.5))
1843 return;
1844 if (spx(i) != 0.0)
1845 p1 += -spx(i) * ((th(i) - th(i - 1)) + g0 * hp0) / hp0;
1846 if (spy(i) != 0.0)
1847 p2 += -spy(i) * ((th(i) - th(i - sy)) + g1 * hp1) / hp1;
1848 if (spz(i) != 0.0)
1849 p3 += -spz(i) * ((th(i) - th(i - sz)) + g2 * hp2) / hp2;
1850 },
1851 d0, d1, d2);
1852 diff[0] = d0;
1853 diff[1] = d1;
1854 diff[2] = d2;
1855}
1856
1857} // namespace sco
1858} // namespace peclet::flow
1859
1860#endif // PECLET_FLOW_SCALAR_CUTCELL_OPERATOR_HPP
flow — portable (Kokkos) cut-cell pressure-operator face openness from an SDF.
Definition conj.py:1
double dotTwoLocal(CCConst af, CCConst bf, CCConst as, CCConst bs, C3 e, int g)
void conjugateCoefficients(Kokkos::View< double *, CCMem > cc, const scg::ScalarFacetOverlay &fo, Kokkos::View< const std::uint8_t *, CCMem > conjF, Kokkos::View< const double *, CCMem > sS, CCConst unk, CCConst sunk, const MaterialTable &mt, double lamF)
Per facet (§1.4, §4.1): cc = alpha G_c, G_c = 1/(s_f/Lam_f + R_c + s_s/Lam_s), on a facet of a conjug...
void explicitFaceFluxes(CCField Phi, CCConst phi, CCConst c, CCConst unk, CCConst small, int a, int scheme, bool steady, C3 e, int g)
The explicit face fluxes of §6.2 on the low a-faces of the cells g .
void faceFluxes(CCField phi, CCConst vel, CCConst open, CCConst unk, int a, C3 e)
phi = open * vel on every face whose two cells are inside the block (0 on the block's lowest layer),...
void openFaceResidual(CCField r, CCConst c, CCConst phi, CCConst small, CCConst unk, bool inflow, int a, int side, bool steady, C3 e, int g)
r(i) -= F_out c_i on the implicit outflow faces of (a, side) (the budget's flux-form residual; the in...
void meanFluxLocal(CCConst th, CCConst kappa, CCConst unk, CCConst sax, CCConst say, CCConst saz, CCConst phx, CCConst phy, CCConst phz, bool advecting, double lam, const double gi[3], const double hp[3], const double ub[3], C3 e, int g, double diff[3], double adv[3])
scalar_mean_flux (§8.1), this rank's sums (internal; the caller divides by the global cell count): di...
void cellVelocitySumLocal(CCConst phx, CCConst phy, CCConst phz, CCConst unk, const double hh[3], C3 e, int g, double s[3])
(sum U'_x/V, sum U'_y/V, sum U'_z/V) over the unknown inner cells (the moving frame's numerator; its ...
void zeroNonUnknown(CCField c, CCConst unk, C3 e, int g)
Zero c on every inner cell that is not a fluid unknown (§1.3: re-zeroed at the start of every advance...
double openFaceInflux(CCConst c, CCConst cOld, CCConst phi, CCConst small, CCConst unk, Kokkos::View< const double *, CCMem > gv, bool inflow, double vol, int a, int side, bool steady, C3 e, int g)
Sum over one open face of V times the advective flux INTO the domain through it, per internal time (b...
void conjOverlayApply(CCField yf, CCField ys, CCConst xf, CCConst xs, const scg::ScalarFacetOverlay &fo, Kokkos::View< const double *, CCMem > cw, Kokkos::View< const double *, CCMem > cc, Kokkos::View< int *[8], Kokkos::LayoutLeft, CCMem > offS, Kokkos::View< double *[8], Kokkos::LayoutLeft, CCMem > wS, bool subtract=false)
The two-field overlay (§4.1 step 3), one thread per cut cell, facets in CSR order: y_f(i) += sum cw u...
void dot2TwoReduce(CCConst af, CCConst bf, CCConst cf, CCConst as, CCConst bs, CCConst cs, C3 e, int g, R1 &&r1, R2 &&r2)
(sum a b, sum c c) over inner cells, both fields, in one pass, into (r1, r2).
double sourceSumLocal(CCConst kappa, CCConst unk, double srcConst, CCConst src, bool useField, double srcFactor, C3 e, int g)
sum over unknowns of kappa S' (the source part of the right-hand side, per unit V).
void solidFaceProducts(CCField spx, CCField spy, CCField spz, CCConst sunk, CCConst mat, CCConst sax, CCConst say, CCConst saz, const MaterialTable &mt, C3 e)
The level-0 solid face products P_a(i) = Lam_face a_s on the LOW a-face of cell i when cells i - s_a ...
long countImplicitLocal(CCConst phi, CCConst small, CCConst unk, int a, bool steady, C3 e, int g)
Faces carrying flux that are implicit, over the low faces of the inner cells (census num_implicit_fac...
void dot2Reduce(CCConst a, CCConst b, CCConst c, C3 e, int g, R1 &&r1, R2 &&r2)
(sum a b, sum c c) over inner cells in one pass, into (r1, r2).
void meanGradientFaceRhs(CCField b, CCConst unk, CCConst sax, CCConst say, CCConst saz, const double gf[3], C3 e, int g)
Faces (§1.5): b(i) += sum_a gf_a (a_a^+ - a_a^-) over the coupled faces (the bands' guard),...
void dot2Local(CCConst a, CCConst b, CCConst c, C3 e, int g, double &ab, double &cc)
void solidBands(CCField AC, CCField AW, CCField AE, CCField AS, CCField AN, CCField AB, CCField AT, CCConst ms, CCConst sunk, CCConst spx, CCConst spy, CCConst spz, const double w[3], C3 e, int g)
The solid phase's 7 bands (§4.1): AC = m_s + the six face terms w_a P, off-diagonals -w_a P; identity...
double solidFaceLam(const Kokkos::View< double *, CCMem > &lam, int ma, int mb)
Lam_face of a solid face between materials ma and mb (§1.4): Lam_s of the material when equal,...
void openFaceAdvection(CCField AC, CCField b, CCField outflow, CCConst cOld, CCConst phi, CCConst small, CCConst unk, Kokkos::View< const double *, CCMem > gv, bool inflow, int a, int side, bool steady, C3 e, int g, CCField omega=CCField(), bool wOmega=false)
The open face's row terms on the unknown inner boundary cells of (a, side) (table above): inflow b +=...
void maxabsReduce(CCConst a, C3 e, int g, Red red)
max over inner cells of |a|, through the Max reducer red (Kokkos::Max over a host double or a device ...
void conjFacetFlux(Kokkos::View< double *, CCMem > up, Kokkos::View< double *, CCMem > us, Kokkos::View< double *, CCMem > qin, CCConst xf, CCConst xs, const scg::ScalarFacetOverlay &fo, Kokkos::View< const double *, CCMem > cw, Kokkos::View< const double *, CCMem > rw, Kokkos::View< const double *, CCMem > cc, Kokkos::View< int *[8], Kokkos::LayoutLeft, CCMem > offS, Kokkos::View< double *[8], Kokkos::LayoutLeft, CCMem > wS, const MeanGradPosition &pos, bool mgOn)
Per facet: the probe values and the flux INTO the fluid per unit cell volume per internal time,...
long countSmallLocal(CCConst small, C3 e, int g)
Count of small inner cells (census num_small_cells).
double maxCellPecletLocal(CCConst phx, CCConst phy, CCConst phz, CCConst unk, double lam, const double w[3], C3 e, int g)
A2 census max_cell_peclet on this rank: the max over the interior flux faces (the low faces of the in...
void sumUnknownLocal(CCConst a, CCConst unk, C3 e, int g, double &sum, double &sumAbs)
(sum a, sum |a|) over the inner cells that are unknowns.
void solidRhs(CCField bs, CCConst ms, CCConst sOld, C3 e, int g)
The solid right-hand side b_s = m_s psi_s^n (no solid source in v1, §13 Q17); 0 elsewhere.
void openFaceFluxes(CCField phi, Kokkos::View< const double *, CCMem > plane, CCConst unk, int a, int side, C3 e, int g)
phi(bf) = plane value on the boundary face of (a, side), over the full transverse extent,...
double dotLocal(CCConst a, CCConst b, C3 e, int g)
double bulkCourantLocal(CCConst phx, CCConst phy, CCConst phz, CCConst kappa, CCConst unk, CCConst sax, CCConst say, CCConst saz, double dt, C3 e, int g)
C_bulk of §6.3 on this rank: max over the inner FULL cells (an unknown with kappa = 1 and all six sna...
void solidMomentsLocal(CCConst xs, CCConst kappa, CCConst sunk, CCConst mat, const MaterialTable &mt, C3 e, int g, double &m, double &w)
(sum C_s K kappa_s psi_s, sum C_s K kappa_s) over the solid unknowns (times V by the caller): the sol...
double dirichletFaceInflux(CCConst c, CCConst unk, CCConst saA, Kokkos::View< const double *, CCMem > gv, double lamW, double vol, int a, int side, C3 e, int g)
Sum over one Dirichlet domain face of V t (g_bf - c_i): the flux INTO the domain through it,...
void solidResidualFluxForm(CCField r, CCConst x, CCConst b, CCConst ms, CCConst sunk, CCConst spx, CCConst spy, CCConst spz, const double w[3], C3 e, int g)
The solid residual in FLUX form (the budget, §9): r = b - m_s psi - sum_faces t (psi_i - psi_nb); the...
void solidMeanGradientFaceRhs(CCField bs, CCConst sunk, CCConst spx, CCConst spy, CCConst spz, const double gs[3], C3 e, int g)
Mean-gradient mode, solid faces (§1.5): b_s(i) += sum_a gs_a (P_a(i + s_a) - P_a(i)),...
void dot2TwoLocal(CCConst af, CCConst bf, CCConst cf, CCConst as, CCConst bs, CCConst cs, C3 e, int g, double &ab, double &cc)
void clampNonNegative(CCField omega, C3 e, int g)
omega = max(omega, 0) on the inner cells (A2: implicit backflow never enters the coarse mass).
void buildSolidProbes(Kokkos::View< double *, CCMem > sS, Kokkos::View< int *[8], Kokkos::LayoutLeft, CCMem > offS, Kokkos::View< double *[8], Kokkos::LayoutLeft, CCMem > wS, Kokkos::View< std::uint8_t *, CCMem > rungS, Kokkos::View< const std::uint8_t *, CCMem > conjF, const scg::ScalarFacetOverlay &fo, CCConst sdf, CCConst sunk, C3 e, const double hp[3])
The solid probe ladder (§3.4): per facet of a conjugate body (conjF), the ladder of §3....
void solidMass(CCField ms, CCConst kappa, CCConst sunk, CCConst mat, const MaterialTable &mt, double idt, C3 e, int g)
The solid mass m_s = C_s K kappa_s idt on the solid unknowns, 0 elsewhere (inner cells).
void facetFlux(Kokkos::View< double *, CCMem > up, Kokkos::View< double *, CCMem > qin, CCConst x, const scg::ScalarFacetOverlay &fo, Kokkos::View< const double *, CCMem > cw, Kokkos::View< const double *, CCMem > rw)
Per facet: the probe value u_p of x and the wall flux INTO the fluid per unit cell volume per interna...
void cellVelocity(const CCConst &phx, const CCConst &phy, const CCConst &phz, long i, long sy, long sz, const double hh[3], double u[3])
U'_a/V of cell i (§1.5): the volume-integrated cell velocity sum_f F_out (x_f - x_i) per unit full-ce...
void overlayApply(CCField y, CCConst x, const scg::ScalarFacetOverlay &fo, Kokkos::View< const double *, CCMem > cw, bool subtract=false)
y(i) += sum over the facets of cut cell i of cw * (probe interpolation of x): the overlay pass of the...
double maxAbsFluxLocal(CCConst phx, CCConst phy, CCConst phz, C3 e, int g)
max |phi| over the low faces of the inner cells (0: no advection this step).
void solidMeanFluxLocal(CCConst th, CCConst sunk, CCConst spx, CCConst spy, CCConst spz, const double gi[3], const double hp[3], C3 e, int g, double diff[3])
The solid part of scalar_mean_flux's diffusive sum (§8.1): over the low faces of the inner cells whos...
void faceFluxCountsLocal(CCConst phi, CCConst vel, CCConst open, int a, C3 e, int g, long &nFlux, long &nGuarded)
(faces carrying flux, faces whose nonzero open * vel the guard zeroed) over the low faces of the inne...
double lowAp(const CCConst &sax, const CCConst &say, const CCConst &saz, int a, long i)
Face aperture of axis a at the low face of cell i (sa_a(i)).
void dotTwoReduce(CCConst af, CCConst bf, CCConst as, CCConst bs, C3 e, int g, R &&result)
sum over inner cells of af bf + as bs (the two-field dot).
void conjSurrogate(CCField SACf, CCField SACs, CCField W0, CCConst ACf, CCConst ACs, const scg::ScalarFacetOverlay &fo, Kokkos::View< const double *, CCMem > cw, Kokkos::View< const double *, CCMem > cc)
The two-field level-0 surrogate (§4.3): SAC_f = AC_f + sum (cw + cc), SAC_s = AC_s + sum cc,...
double cellOutflow(const CCConst &phx, const CCConst &phy, const CCConst &phz, long i, long sy, long sz)
Out_i = sum over the six faces of max(F_out, 0), per unit V (§6.3), in a fixed order.
void residualFluxForm(CCField r, CCConst c, CCConst b, CCConst kappa, CCConst unk, CCConst sax, CCConst say, CCConst saz, double lam, double idt, const double w[3], C3 e, int g)
The budget's residual r = b - A c in FLUX form (§9): r = b - kappa idt c - sum_faces t (c_i - c_nb) w...
void smallCells(CCField small, CCConst phx, CCConst phy, CCConst phz, CCConst kappa, CCConst unk, double dt, double thr, C3 e, int g)
The small-cell flags of §6.3 over the inner cells: small(i) = 1 iff i is an unknown,...
void meanGradientAdvectionRhs(CCField b, CCConst phx, CCConst phy, CCConst phz, CCConst kappa, CCConst unk, const double gi[3], const double hh[3], const double ub[3], C3 e, int g)
Advection (§1.5): b(i) -= G'.
double maxabsLocal(CCConst a, C3 e, int g)
void buildSolidMaterial(CCField mat, CCField sunk, CCConst kappa, CCConst sax, CCConst say, CCConst saz, const scg::ScalarFacetOverlay &fo, Kokkos::View< const int *, CCMem > owner, bool hasOwner, const MaterialTable &mt, C3 e, int g, double vol[2])
The material per cell and the solid-unknown flags on the inner cells (§1.3, §2.7).
void advectionBands(CCField AC, CCField AW, CCField AE, CCField AS, CCField AN, CCField AB, CCField AT, CCField outflow, CCConst phx, CCConst phy, CCConst phz, CCConst small, CCConst unk, bool steady, C3 e, int g)
The implicit FOU part of the operator (§1.4, §6.3; all faces when steady, §6.7): on every unknown inn...
void buildBands(CCField AC, CCField AW, CCField AE, CCField AS, CCField AN, CCField AB, CCField AT, CCConst kappa, CCConst unk, CCConst sax, CCConst say, CCConst saz, double lam, double idt, const double w[3], C3 e, int g)
The 7 bands (§4.1): AC = kappa idt + sum of the six face terms Lam w_a a (guarded toward a cell that ...
void dirichletFaceFold(CCField AC, CCField b, CCConst unk, CCConst saA, Kokkos::View< const double *, CCMem > gv, double lamW, int a, int side, C3 e, int g)
Dirichlet domain face (§1.4 B_i): for the inner boundary cell i of face (a, side),...
int bodyIndex(double m, int nb)
A body id clamped into the table (out of range -> 0, as facetCoefficients does).
void explicitAdvectionRhs(CCField b, CCConst Px, CCConst Py, CCConst Pz, CCConst unk, C3 e, int g)
b(i) -= the net explicit outflow sum_a (Phi_a(i + s_a) - Phi_a(i)) on the unknown inner cells.
void kappaMomentsLocal(CCConst c, CCConst kappa, CCConst unk, C3 e, int g, double &kc, double &k)
(sum kappa c, sum kappa) over the unknowns (times V by the caller).
void openFaceCountsLocal(CCConst phi, CCConst small, CCConst unk, bool inflow, int a, int side, bool steady, C3 e, int g, long &nFlux, long &nImpl, long &nBack)
The census counts of one open face over this rank's inner boundary cells: (faces carrying flux on the...
void dirichletFaceResidual(CCField r, CCConst c, CCConst unk, CCConst saA, double lamW, int a, int side, C3 e, int g)
r(i) -= 2 Lam w_a a_bf c_i on the inner boundary cells of a Dirichlet domain face (the LHS part of di...
void facetCoefficients(Kokkos::View< double *, CCMem > cw, Kokkos::View< double *, CCMem > rw, const scg::ScalarFacetOverlay &fo, CCConst unk, const WallTable &wt, double lam)
Per-facet overlay coefficients (§4.1): cw = alpha G, rw = alpha G g (Dirichlet/Robin) or cw = 0,...
double maxFacetLocal(Kokkos::View< const double *, CCMem > cw)
max over facets of cw (0 when there is no Dirichlet/Robin facet: the steady singular test).
void faceTangents(int a, int &t1, int &t2)
The tangential axes of face axis a, in x, y, z order (t1 < t2): the layout of a domain-face profile (...
void surrogateDiagonal(CCField SAC, CCConst AC, const scg::ScalarFacetOverlay &fo, Kokkos::View< const double *, CCMem > cw)
The level-0 surrogate diagonal (§4.3): SAC = AC + sum of the cut cell's cw (the probe weights lumped,...
void dotReduce(CCConst a, CCConst b, C3 e, int g, R &&result)
sum over inner cells of a(i) b(i), into result.
void buildRhs(CCField b, CCConst cOld, CCConst kappa, CCConst unk, double idt, double srcConst, CCConst src, bool useField, double srcFactor, C3 e, int g)
Base right-hand side: b = kappa (idt c^n + S'), identity rows 0.
void advectionResidual(CCField r, CCConst c, CCConst phx, CCConst phy, CCConst phz, CCConst small, CCConst unk, bool steady, C3 e, int g)
r(i) -= the implicit faces' net outflow sum F_out c_up at c (the budget's flux-form residual,...
void meanGradientProbeShift(Kokkos::View< double *, CCMem > rw, const scg::ScalarFacetOverlay &fo, Kokkos::View< const double *, CCMem > cw, const MeanGradPosition &pos)
Dirichlet/Robin probes (§1.5): rw(f) -= cw(f) sum_k w_k G.x_k, so that rw = alpha G_phi (g - sum_k w_...
void addOnUnknown(CCField a, CCConst unk, double d, C3 e, int g)
a(i) += d on the inner cells that are unknowns.
void overlayRhs(CCField b, const scg::ScalarFacetOverlay &fo, Kokkos::View< const double *, CCMem > rw)
b(i) += sum over the facets of cut cell i of rw (the wall part of the right-hand side).
void conjMeanGradientShift(CCField bf, CCField bs, const scg::ScalarFacetOverlay &fo, Kokkos::View< const double *, CCMem > cc, Kokkos::View< int *[8], Kokkos::LayoutLeft, CCMem > offS, Kokkos::View< double *[8], Kokkos::LayoutLeft, CCMem > wS, const MeanGradPosition &pos)
Mean-gradient mode, conjugate facets (§1.5): the fluid row carries + cc (u_pf - u_ps) with u = theta ...
CCExec::memory_space CCMem
void ccReduce3(const char *name, C3 lo, C3 hi, F f, R &&... reducers)
void ccFor3(const char *name, C3 lo, C3 hi, F f)
Kokkos::View< double *, CCMem > CCField
Kokkos::DefaultExecutionSpace CCExec
Kokkos::View< const double *, CCMem > CCConst
double fou_flux(double L, double R, double vel)
double tvd(double LL, double L, double R, double RR, double vel)
double sou(double LL, double L, double R, double RR, double vel)
flow — the multidimensional iteration policies: iteration order follows storage.
flow — portable (Kokkos) staggered MAC momentum advection (Koren TVD + FOU).
The per-cut-cell-scalar state (ScalarField::cut).
std::size_t mgN
the block size it was built for
Kokkos::View< double *, CCMem > ks
WO-9b: the resident Krylov's scalar slots.
ScalarWallSpec wallDefault
every body without an instance override
CCField outflow
lumped implicit outflow per cell (inside SAC; ScalarMG coarse mass, §5.2)
double residual
final TRUE residual max|b - A c| / ref, after any gauge shift
bool warnedKoren
the koren-above-bulk-Courant-1/2 warning was issued (once)
long numGuardedFaces
faces whose projection flux the guard zeroed (a no-op reading)
Kokkos::View< double *, CCMem > mtLamV
Kokkos::View< int *, CCMem > wtTypeV
double srcFactor
S' = S * srcFactor (tRef)
CCField Phi[3]
explicit face fluxes phi c*(c^n), 0 on implicit faces (§6.2)
CCField kq
singular case: the projected rhs, and the preconditioner's rhs copy
Kokkos::View< double *, CCMem > gFace[6]
Dirichlet domain-face value per face cell.
std::vector< int > wtType
the resolved per-body wall table of that operator
CCField spz
level-0 solid face products Lam_face a_s (guarded), low faces
Kokkos::View< double *, CCMem > rw
per facet (§4.1)
long numSmall
census num_small_cells
bool openInflow[6]
... and it is an inflow
std::vector< double > profile[6]
Dirichlet domain-face profile (t1 fastest)
bool warnedBackflow
the steady backflow warning was issued (once per scalar)
bool advecting
some face carried flux: else every advection kernel was skipped
double gPhys[3]
its G (physical, c/L)
Kokkos::View< int *, CCMem > mtConjV
CCField small
1 on a small cell (§6.3), inner cells + exchanged ghosts
long warnedGeometryVersion
The geometry version whose §9 resolution warnings were issued (WO-8: once per scalar and geometry; a ...
CCField ms
solid mass C_s K kappa_s idt on solid unknowns
Kokkos::View< double *, CCMem > wtGV
CCField mat
material (body id) per cell, haloed; -1: no solid
long numUnknowns
global fluid unknowns (the singular mean)
CCField W0
solid surrogate diagonal; the same-cell coupling sum cc
CCField omegaOpen
A2 (§6.7, D-WO5-3): the implicit outflow the open-face rows add to AC, summed over the cell's open fa...
double incompatibility
steady singular case: |sum b| / sum |b| before projection
Kokkos::View< double *, CCMem > mtCKV
bool openFace[6]
open face rows built (WO-5b)
double lam
internal Lam = D tRef/hRef^2 of that operator
std::vector< std::pair< int, ScalarWallSpec > > wallInstance
instance overrides, last wins
Kokkos::View< int *[8], Kokkos::LayoutLeft, CCMem > offS
bool meanGradOn
that operator carried the mean-gradient terms (§1.5)
int profileN[6][2]
its tangential extents
long solidRungs[4]
census probe_rungs['solid'] (global)
long mgVersion
the geometry version its level table was built for
double solidVolume
census solid_volume: conjugate solid, physical
long numImplicitFaces
census num_implicit_faces (faces carrying flux, implicit)
CCField solid
the registered field <name>_solid (psi_s), when allocated
CCField phi[3]
F/V through the LOW a-face of cell i (internal 1/T), guarded (sco::faceFluxes)
int maxit
BiCGStab iteration cap (§5.1)
double meanGrad[3]
Mean-gradient (closure) mode (§1.5, WO-6): the field holds theta, c = G.x + theta,...
double maxCellPeclet
census max_cell_peclet (A2): max |phi_a| / (Lam' w_a)
Kokkos::View< double *, Kokkos::HostSpace > pk
and its host packet
Kokkos::View< double *, CCMem > sS
solid probe distance per facet (conjugate facets)
CCField sunk
solid-unknown flags (haloed, 0 beyond non-periodic faces)
Kokkos::View< double *, CCMem > mtKV
long numBackflowFaces
census num_backflow_faces: outflow-face rows with F_out < 0
double bulkCourant
census bulk_courant: C_bulk of §6.3 (0 steady)
double ubar[3]
its moving-frame velocity U'bar = sum U_i / sum kappa_i V
bool sourceIsField
set_scalar_source was given an array (sourceField)
double sourceConst
volumetric source S (c/T), uniform part
double maxSolidD
max internal D_s' = Lam_s'/(C_s K) (the level rule)
CCField kqS
Krylov scratch, solid phase.
double rtol
max-norm relative residual tolerance (§5.1)
Kokkos::View< std::uint8_t *, CCMem > rungS
long solidVersion
geometry version of the record
Kokkos::View< double *[8], Kokkos::LayoutLeft, CCMem > wS
Kokkos::View< double *, CCMem > mtRcV
Kokkos::View< double *, CCMem > wtQV
double sealedSolidVolume
census sealed_solid_volume (D-WO7-1), physical
Kokkos::View< double *, CCMem > wtKV
std::vector< double > mtRc
internal per-body material (conjugate bodies)
Kokkos::View< double *, CCMem > cc
per facet: alpha G_c (conjugate facets, else 0)
Kokkos::View< double *, CCMem > cw
int mgLevels
levels the last solve's V-cycle used (census mg_levels)
long numFluxFaces
faces carrying flux (the implicit fraction's denominator)
Kokkos::View< std::uint8_t *, CCMem > conjF
1 on a facet of a conjugate body
CCField sourceField
or per cell (physical), when allocated
bool warnedBottom
the ScalarMG shallow-bottom warning was issued (once)
std::vector< double > wtQ
(internal k', g, q')
double gInt[3]
its G' = G hRef (c per internal length)
long numSolidUnknowns
census num_solid_unknowns (global)
std::vector< char > solidKey
the conjugate flag per body it was built for
CCField SAC
level-0 surrogate diagonal (bands + lumped wall)
Wall condition per body (§1.1, §8.1 set_scalar_wall), in the caller's PHYSICAL units; the conversion ...
int type
0 neumann, 1 dirichlet, 2 robin, 3 conjugate (WO-7)
double value
neumann: q (flux into the fluid, c L/T); dirichlet/robin: g
The block Lookup of cs::buildProbe for one facet's cell: unknown flags (haloed) at lattice offsets,...
The facet overlay (§3.5): SoA, facet index fastest. Offsets are LINEAR offsets on the block.
The per-body material of a conjugate scalar, internal units (§4.2): conj 1 on a conjugate body; lam =...
Kokkos::View< double *, CCMem > lam
G.x at the centre of extended-block cell j (physical; cellCentres' formula, unwrapped).
double org[3]
the physical lower corner of the global grid
double G[3]
the physical mean gradient (c/L)
long base[3]
og_a - g: global index of extended-block index 0
Device copy of the per-body wall table, resolved (instance override or default) and converted to inte...
Kokkos::View< double *, CCMem > k
Kokkos::View< double *, CCMem > gval
Kokkos::View< int *, CCMem > type
Kokkos::View< double *, CCMem > q
static constexpr int G
static constexpr double AC
static constexpr double F