flow 1.3.0
Kokkos cut-cell IBM incompressible Navier-Stokes solver + pnm pore extraction
Loading...
Searching...
No Matches
cut_cell_ibm.hpp
Go to the documentation of this file.
1
19#ifndef PECLET_FLOW_CUT_CELL_IBM_HPP
20#define PECLET_FLOW_CUT_CELL_IBM_HPP
21
22#include <Kokkos_Core.hpp>
23#include <Kokkos_MathematicalFunctions.hpp>
24
25#include "mac_cutcell.hpp" // ccFor3 (rule H)
26#include "policy.hpp"
27
28namespace peclet::flow {
29
30using IMem = Kokkos::DefaultExecutionSpace::memory_space;
31
32// ---- boundary-distance polynomials (verbatim from cut_cell_ibm.cuh, templated on Real: G.6) ----
33template <class Real>
35 return xi * (Real(1) + xi);
36}
37template <class Real>
39 return xi * (Real(1) - xi);
40}
41template <class Real>
43 return Real(2) * (xi * xi - Real(1));
44}
45template <class Real>
49template <class Real>
51 return xi * (Real(1) + xi) - Real(1) / Real(12);
52}
53template <class Real>
55 return xi * (Real(1) - xi) + Real(1) / Real(12);
56}
57template <class Real>
59 return Real(2) * (xi * xi - Real(1)) - Real(1) / Real(6);
60}
61template <class Real>
65// ---- Navier-slip generalization of the one-sided Dirichlet polynomials (WO-V6b) ----
66//
67// The shipped closure builds the quadratic p through (u_m at -1, u_c at 0, u_g at +1) and imposes
68// the DIRICHLET condition p(theta) = u_b at the wall crossing. The Navier condition on the
69// TANGENTIAL velocity is the Robin condition
70//
71// u_t(wall) - u_body = lambda * d(u_t)/dn (n = the INWARD, fluid-side normal)
72//
73// which along the axis (solid on the +x side, so n = -x) reads p(theta) + lam*p'(theta) = u_b,
74// with `lam` the slip length measured ALONG THIS AXIS (see ibmBuildOverlay: lam = s*lambda/|n_a|,
75// s = 1 - n_c^2 the tangential weight of the component this DOF carries). Solving for u_g with
76// P = theta + lam, Q = theta^2 + 2*lam*theta
77// gives (P+Q) u_g = 2 u_b - 2 u_c (1-Q) + u_m (P-Q), i.e. exactly the shipped polynomials with
78// three additive lam-terms and an UNCHANGED Nbc = 2:
79//
80// D = theta(1+theta) + lam(1+2 theta)
81// X = theta(1-theta) + lam(1-2 theta)
82// K = 2(theta^2-1) + 4 lam theta
83//
84// lam = 0 reproduces poly_D / poly_N_nb / poly_Nc identically (the call sites branch on lam > 0 so
85// that no extra rounding is introduced at all). lam -> infinity gives the free-slip (zero
86// normal-derivative) closure: at theta = 1/2, u_g -> u_c.
87//
88// NOTE the closure is stored in FLOAT by default (double under -DPECLET_FLOW_OPERATOR_DOUBLE): the
89// Robin datum is indistinguishable from no-slip once lam*(1+2 theta) drops below the storage
90// epsilon times D(theta), i.e. below lam ~ 3e-8 cells in the float build (measured floor in the
91// findings). Every physically meaningful lambda is orders of magnitude above it.
92template <class Real>
94 return poly_D(xi) + lam * (Real(1) + Real(2) * xi);
95}
96template <class Real>
98 return poly_N_nb(xi) + lam * (Real(1) - Real(2) * xi);
99}
100template <class Real>
102 return poly_Nc(xi) + Real(4) * lam * xi;
103}
104
105template <class Real>
109template <class Real>
113template <class Real>
117template <class Real>
121template <class Real>
125template <class Real>
127 return (xi_m + Real(1)) * (xi_p - Real(1)) - Real(1) / Real(12);
128}
129template <class Real>
131 return (xi_m / (xi_m + xi_p)) * (Real(1) + xi_m) - Real(1) / Real(12);
132}
133template <class Real>
135 return (xi_p / (xi_m + xi_p)) * (Real(1) - xi_p) + Real(1) / Real(12);
136}
137
138// IBM overlay output (SoA Views; per-direction arrays are size 6*num_cells). Templated on the
139// memory space AND the storage precision (G.6) so the device build and a HostSpace reference
140// share the same fill code, and a `-DPECLET_FLOW_OPERATOR_DOUBLE` build carries the overlay in
141// double end to end instead of narrowing it to float at this SoA.
142template <class Space, class Real = float>
145 Kokkos::View<int*, Space> cell_index;
146 Kokkos::View<int*, Space> num_boundaries;
147 Kokkos::View<Real*, Space> D_rescale;
148 Kokkos::View<int*, Space> dir_code;
149 Kokkos::View<Real*, Space> K_val, M_val, X_val, Nbc_val, R_val;
150};
151// Float alias kept for callers that have not opted into a typed overlay (none left in-tree after
152// G.6; mac_ibm.hpp defines the canonical `IbmOverlay = IbmOverlayT<IMem, mreal>`, mreal = MReal).
153template <class Real>
155
156// Fill one overlay entry (list_idx) for a cut cell from its 7 SDF samples. Verbatim port of
157// ibm_fill_entry<SCHEME>. bc_type: 0 = Dirichlet, 1 = Neumann. thEx (optional, may be nullptr):
158// per-direction EXACT wall-crossing fractions theta from the cut cell toward each of the 6
159// neighbours (analytic-SDF capability, setExactCrossings) — a finite thEx[k] overrides the
160// linear-interpolated theta; non-finite entries fall back. `Real` (the overlay's own storage
161// type, `OV::value_type`) is deduced from `o` — every existing call site is unchanged.
162template <int SCHEME, class OV>
164 typename OV::value_type sdf_c,
165 const typename OV::value_type sdf_n[6], int bc_type,
166 const typename OV::value_type* thEx,
167 const typename OV::value_type* lamAxis = nullptr,
168 int* sandwichSkipped = nullptr) {
169 using Real = typename OV::value_type;
170 o.cell_index(list_idx) = c_idx;
171 o.num_boundaries(list_idx) = 6;
172 bool is_ghost[6];
173 Real xi_vals[6], D_vals[6];
174 for (int k = 0; k < 6; ++k) {
175 if (sdf_n[k] < Real(0)) {
176 is_ghost[k] = true;
177 if (bc_type == 0) {
178 Real theta = sdf_c / (sdf_c - sdf_n[k]);
179 if (thEx != nullptr && Kokkos::isfinite(thEx[k]))
180 theta = thEx[k];
181 if (theta < Real(1e-4))
182 theta = Real(1e-4);
183 if (theta > Real(1))
184 theta = Real(1);
185 xi_vals[k] = theta;
186 const Real lam = (lamAxis != nullptr && SCHEME == 0) ? lamAxis[k >> 1] : Real(0);
187 D_vals[k] = lam > Real(0) ? poly_D_slip(theta, lam)
188 : ((SCHEME == 0) ? poly_D(theta) : poly_D_avg(theta));
189 } else {
190 xi_vals[k] = Real(0.5);
191 D_vals[k] = Real(1);
192 }
193 } else {
194 is_ghost[k] = false;
195 xi_vals[k] = Real(1);
196 D_vals[k] = Real(1e9);
197 }
198 }
199
200 if (bc_type == 0) {
201 bool is_sandwich[3] = {is_ghost[0] && is_ghost[1], is_ghost[2] && is_ghost[3],
202 is_ghost[4] && is_ghost[5]};
203 Real D_sandwich[3] = {Real(0), Real(0), Real(0)};
204 for (int a = 0; a < 3; ++a)
205 if (is_sandwich[a])
206 D_sandwich[a] = (SCHEME == 0) ? poly_D_sandwich(xi_vals[2 * a + 1], xi_vals[2 * a])
207 : poly_D_sandwich_avg(xi_vals[2 * a + 1], xi_vals[2 * a]);
208 Real min_D_abs = Real(1e30), D_rescale = Real(1);
209 auto update_min = [&](Real val) {
210 if (Kokkos::fabs(val) < min_D_abs) {
211 min_D_abs = Kokkos::fabs(val);
212 D_rescale = val;
213 }
214 };
215 for (int axis = 0; axis < 3; ++axis) {
216 if (is_sandwich[axis])
217 update_min(D_sandwich[axis]);
218 else {
219 if (is_ghost[2 * axis])
220 update_min(D_vals[2 * axis]);
221 if (is_ghost[2 * axis + 1])
222 update_min(D_vals[2 * axis + 1]);
223 }
224 }
225 o.D_rescale(list_idx) = D_rescale;
226
227 for (int axis = 0; axis < 3; ++axis) {
228 int km = 2 * axis + 1, kp = 2 * axis;
229 bool sandwich = is_sandwich[axis], g_p = is_ghost[kp], g_m = is_ghost[km];
230 // SANDWICHED axis (both neighbours solid = a one-cell fluid gap): the Navier closure is NOT
231 // applied. A slip length is a sub-cell wall model and a gap that a single cell spans does
232 // not resolve one; the axis keeps its validated no-slip Dirichlet closure. Counted (not
233 // silent) through sandwichSkipped so a geometry where it matters is visible.
234 if (sandwich && lamAxis != nullptr && lamAxis[axis] > Real(0) && sandwichSkipped != nullptr)
235 Kokkos::atomic_fetch_add(sandwichSkipped, 1);
236 Real D_axis =
237 sandwich ? D_sandwich[axis] : (g_p ? D_vals[kp] : (g_m ? D_vals[km] : D_rescale));
238 Real R = D_rescale / D_axis;
239 if (Kokkos::fabs(D_axis) < Real(1e-9))
240 R = Real(1);
241 o.R_val(list_idx * 6 + kp) = R;
242 o.R_val(list_idx * 6 + km) = R;
243 if (sandwich) {
244 if (SCHEME == 0) {
245 o.K_val(list_idx * 6 + kp) = poly_N_c_sandwich(xi_vals[km], xi_vals[kp]) * R;
246 o.K_val(list_idx * 6 + km) = poly_N_c_sandwich(xi_vals[kp], xi_vals[km]) * R;
247 o.Nbc_val(list_idx * 6 + kp) = (poly_Nbc_pp_sw(xi_vals[km], xi_vals[kp]) +
248 poly_Nbc_mp_sw(xi_vals[km], xi_vals[kp])) *
249 R;
250 o.Nbc_val(list_idx * 6 + km) = (poly_Nbc_pp_sw(xi_vals[kp], xi_vals[km]) +
251 poly_Nbc_mp_sw(xi_vals[kp], xi_vals[km])) *
252 R;
253 } else {
254 o.K_val(list_idx * 6 + kp) = poly_N_c_sandwich_avg(xi_vals[km], xi_vals[kp]) * R;
255 o.K_val(list_idx * 6 + km) = poly_N_c_sandwich_avg(xi_vals[kp], xi_vals[km]) * R;
256 o.Nbc_val(list_idx * 6 + kp) = (poly_Nbc_pp_sw_avg(xi_vals[km], xi_vals[kp]) +
258 R;
259 o.Nbc_val(list_idx * 6 + km) = (poly_Nbc_pp_sw_avg(xi_vals[kp], xi_vals[km]) +
261 R;
262 }
263 o.M_val(list_idx * 6 + kp) = Real(0);
264 o.X_val(list_idx * 6 + kp) = Real(0);
265 o.M_val(list_idx * 6 + km) = Real(0);
266 o.X_val(list_idx * 6 + km) = Real(0);
267 } else {
268 for (int side = 0; side < 2; ++side) {
269 int kk = side == 0 ? kp : km;
270 if (is_ghost[kk]) {
271 const Real lam = (lamAxis != nullptr && SCHEME == 0) ? lamAxis[axis] : Real(0);
272 if (lam > Real(0)) {
273 o.K_val(list_idx * 6 + kk) = poly_Nc_slip(xi_vals[kk], lam) * R;
274 o.X_val(list_idx * 6 + kk) = poly_N_nb_slip(xi_vals[kk], lam) * R;
275 o.Nbc_val(list_idx * 6 + kk) = poly_Nbc(xi_vals[kk]) * R;
276 } else if (SCHEME == 0) {
277 o.K_val(list_idx * 6 + kk) = poly_Nc(xi_vals[kk]) * R;
278 o.X_val(list_idx * 6 + kk) = poly_N_nb(xi_vals[kk]) * R;
279 o.Nbc_val(list_idx * 6 + kk) = poly_Nbc(xi_vals[kk]) * R;
280 } else {
281 o.K_val(list_idx * 6 + kk) = poly_Nc_avg(xi_vals[kk]) * R;
282 o.X_val(list_idx * 6 + kk) = poly_Nnb_avg(xi_vals[kk]) * R;
283 o.Nbc_val(list_idx * 6 + kk) = poly_Nbc_avg(xi_vals[kk]) * R;
284 }
285 o.M_val(list_idx * 6 + kk) = Real(0);
286 } else {
287 o.K_val(list_idx * 6 + kk) = Real(0);
288 o.M_val(list_idx * 6 + kk) = Real(1);
289 o.X_val(list_idx * 6 + kk) = Real(0);
290 o.Nbc_val(list_idx * 6 + kk) = Real(0);
291 }
292 }
293 }
294 o.dir_code(list_idx * 6 + kp) = kp;
295 o.dir_code(list_idx * 6 + km) = km;
296 }
297 } else { // Neumann
298 o.D_rescale(list_idx) = Real(1);
299 for (int k = 0; k < 6; ++k) {
300 o.dir_code(list_idx * 6 + k) = k;
301 o.R_val(list_idx * 6 + k) = Real(1);
302 o.K_val(list_idx * 6 + k) = is_ghost[k] ? Real(1) : Real(0);
303 o.M_val(list_idx * 6 + k) = is_ghost[k] ? Real(0) : Real(1);
304 o.X_val(list_idx * 6 + k) = Real(0);
305 o.Nbc_val(list_idx * 6 + k) = Real(0);
306 }
307 }
308}
309
310// Sampled-theta entry point (the historical signature; all existing call sites unchanged).
311template <int SCHEME, class OV>
313 typename OV::value_type sdf_c,
314 const typename OV::value_type sdf_n[6], int bc_type) {
315 ibmFillEntry<SCHEME>(o, list_idx, c_idx, sdf_c, sdf_n, bc_type, nullptr, nullptr, nullptr);
316}
317
318// Build the backward-Euler velocity diffusion stencil over the extended block (divided convention):
319// A_C = idiag + 2*((bx+by)+bz), off-diagonals = -b_a per derivative axis a. idiag = rho/dt,
320// b_a = mu' * w_a with w_a = 1/h_a'^2 the per-axis metric weight (doc/anisotropic_metric.md §2).
321//
322// ASSOCIATION ORDER IS PART OF THE CONTRACT (note trap 3). With bx == by == bz == beta:
323// (beta+beta) is exact, +beta rounds once to fl(3*beta), and the *2 is exact, so the diagonal is
324// fl(6*beta) == 6.0*beta to the last bit — the isotropic operator is BIT-IDENTICAL to the
325// pre-Phase-2 `idiag + 6.0*beta`. Any other grouping breaks that.
326template <class MV>
327inline void ibmBuildDiffusion(MV AC, MV AW, MV AE, MV AS, MV AN, MV AB, MV AT, int ex, int ey,
328 int ez, double bx, double by, double bz, double idiag) {
329 Kokkos::DefaultExecutionSpace space;
330 const std::size_t n = (std::size_t)ex * ey * ez;
331 using MVreal = typename MV::non_const_value_type;
332 const MVreal nbx = (MVreal)(-bx), nby = (MVreal)(-by), nbz = (MVreal)(-bz),
333 c = (MVreal)(idiag + 2.0 * ((bx + by) + bz));
334 Kokkos::parallel_for(
335 "peclet::flow::ibm_build_diff", Kokkos::RangePolicy<Kokkos::DefaultExecutionSpace>(0, n),
336 KOKKOS_LAMBDA(std::size_t i) {
337 AC(i) = c;
338 AW(i) = nbx;
339 AE(i) = nbx;
340 AS(i) = nby;
341 AN(i) = nby;
342 AB(i) = nbz;
343 AT(i) = nbz;
344 });
345}
346
347// Isotropic spelling (b_x = b_y = b_z = beta), kept for the kernel-level tests and any caller
348// with no metric: bit-identical to the pre-Phase-2 kernel by the association note above.
349template <class MV>
350inline void ibmBuildDiffusion(MV AC, MV AW, MV AE, MV AS, MV AN, MV AB, MV AT, int ex, int ey,
351 int ez, double beta, double idiag) {
352 ibmBuildDiffusion(AC, AW, AE, AS, AN, AB, AT, ex, ey, ez, beta, beta, beta, idiag);
353}
354
355// Variable-viscosity backward-Euler diffusion stencil (sibling of ibmBuildDiffusion): the face
356// off-diagonal is -beta_face (per-face viscosity from FaceProps) and A_C = idiag(i) + sum of the 6
357// face betas. Built over INNER cells (neighbour mu at i+-stride must be valid — fill the mu ghosts
358// first). Face means are computed in double, cast to float once (mirroring the constant path).
359// FaceProps: UniformFaceProps reproduces the constant operator; FieldFaceProps reads a mu field.
360template <class FaceProps, class MV>
361inline void ibmBuildDiffusionVar(MV AC, MV AW, MV AE, MV AS, MV AN, MV AB, MV AT, int ex, int ey,
362 int ez, int g, FaceProps fp, double wx = 1.0, double wy = 1.0,
363 double wz = 1.0) {
364 // rule H (§4.5; §14 H-3c): host pencil, device MDRange, on the same (default) execution space;
365 // the body is verbatim. A cell writes only its own seven coefficients (ccFor3's simd contract).
366 ccFor3(
367 "peclet::flow::ibm_build_diff_var", C3{g, g, g}, C3{ex - g, ey - g, ez - g},
368 KOKKOS_LAMBDA(int lx, int ly, int lz) {
369 const long sx = 1, sy = ex, sz = (long)ex * ey;
370 const long i = (long)lx + (long)ly * sy + (long)lz * sz;
371 // The face viscosity times the metric weight w_a of the face's OWN axis
372 // (doc/anisotropic_metric.md §2); w == 1.0 exactly on the isotropic path.
373 const double bw = fp.beta(i, i - sx) * wx, be = fp.beta(i, i + sx) * wx;
374 const double bs = fp.beta(i, i - sy) * wy, bn = fp.beta(i, i + sy) * wy;
375 const double bb = fp.beta(i, i - sz) * wz, bt = fp.beta(i, i + sz) * wz;
376 AW(i) = (typename MV::non_const_value_type)(-bw);
377 AE(i) = (typename MV::non_const_value_type)(-be);
378 AS(i) = (typename MV::non_const_value_type)(-bs);
379 AN(i) = (typename MV::non_const_value_type)(-bn);
380 AB(i) = (typename MV::non_const_value_type)(-bb);
381 AT(i) = (typename MV::non_const_value_type)(-bt);
382 AC(i) = (typename MV::non_const_value_type)(fp.idiag(i) + bw + be + bs + bn + bb + bt);
383 });
384}
385
386// Apply the Robust-Scaled overlay to the momentum stencil at each cut cell (port of
387// ibm_modify_stencil_k): modify A_C / 6 off-diagonals + accumulate the inhomogeneous
388// (wall-velocity) term and store the row scaling. Each cut cell owns a distinct grid index c -> no
389// races. `OV` (deduced): the overlay's own storage type is `OV::value_type` (G.6).
390// `u_bc` (optional): per-cell wall velocity of THIS component, on the extended block -- the
391// kinematic no-slip datum for MOVING geometry (Layer 3 rung 2). An EMPTY View falls back to the
392// scalar u_bc_val, which is what keeps a static solver bit-identical: the accumulated term is
393// (double)Nbc * 0.0f * vnb either way, the same three roundings in the same order.
394template <class MV, class OV>
396 MV AC, MV AW, MV AE, MV AS, MV AN, MV AB, MV AT, Kokkos::View<double*, IMem> a_inhom,
397 Kokkos::View<double*, IMem> rhs_scale, const OV& ibm, int numActive,
398 typename OV::value_type u_bc_val,
399 Kokkos::View<const double*, IMem> u_bc = Kokkos::View<const double*, IMem>()) {
400 Kokkos::DefaultExecutionSpace space;
401 const bool hasInhom = (a_inhom.extent(0) != 0), hasScale = (rhs_scale.extent(0) != 0);
402 const bool hasWallVel = (u_bc.extent(0) != 0);
403 Kokkos::parallel_for(
404 "peclet::flow::ibm_modify", Kokkos::RangePolicy<Kokkos::DefaultExecutionSpace>(0, numActive),
406 const int OPP[6] = {1, 0, 3, 2, 5, 4};
407 const int c = ibm.cell_index(list_idx);
408 const auto descale = ibm.D_rescale(list_idx);
409 if (hasScale)
410 rhs_scale(c) = descale;
411 const double ubc = hasWallVel ? u_bc(c) : (double)u_bc_val;
412 const double orig[6] = {AE(c), AW(c), AN(c), AS(c), AT(c), AB(c)};
413 double aC = (double)AC(c) * (double)descale;
414 double mod[6] = {0, 0, 0, 0, 0, 0};
415 double inhom = 0.0;
416 for (int k = 0; k < 6; ++k) {
417 const auto K = ibm.K_val(list_idx * 6 + k), M = ibm.M_val(list_idx * 6 + k);
418 const auto X = ibm.X_val(list_idx * 6 + k), Nbc = ibm.Nbc_val(list_idx * 6 + k);
419 const double vnb = orig[k];
420 aC += vnb * (double)K;
421 inhom += (double)Nbc * ubc * vnb;
422 mod[k] += vnb * ((double)descale * (double)M - 1.0);
423 mod[OPP[k]] += vnb * (double)X;
424 }
425 AC(c) = (typename MV::non_const_value_type)aC;
426 AE(c) = (typename MV::non_const_value_type)(orig[0] + mod[0]);
427 AW(c) = (typename MV::non_const_value_type)(orig[1] + mod[1]);
428 AN(c) = (typename MV::non_const_value_type)(orig[2] + mod[2]);
429 AS(c) = (typename MV::non_const_value_type)(orig[3] + mod[3]);
430 AT(c) = (typename MV::non_const_value_type)(orig[4] + mod[4]);
431 AB(c) = (typename MV::non_const_value_type)(orig[5] + mod[5]);
432 if (hasInhom)
433 a_inhom(c) += inhom;
434 });
435}
436
437} // namespace peclet::flow
438
439#endif // PECLET_FLOW_CUT_CELL_IBM_HPP
flow — portable (Kokkos) cut-cell pressure-operator face openness from an SDF.
Real poly_Nc_slip(Real xi, Real lam)
Real poly_Nbc_avg(Real)
Kokkos::DefaultExecutionSpace::memory_space IMem
Real poly_D_slip(Real xi, Real lam)
Real poly_N_c_sandwich_avg(Real xi_m, Real xi_p)
Real poly_Nbc_pp_sw_avg(Real xi_m, Real xi_p)
Real poly_Nbc_mp_sw(Real xi_m, Real xi_p)
void ibmBuildDiffusionVar(MV AC, MV AW, MV AE, MV AS, MV AN, MV AB, MV AT, int ex, int ey, int ez, int g, FaceProps fp, double wx=1.0, double wy=1.0, double wz=1.0)
void ibmModifyStencil(MV AC, MV AW, MV AE, MV AS, MV AN, MV AB, MV AT, Kokkos::View< double *, IMem > a_inhom, Kokkos::View< double *, IMem > rhs_scale, const OV &ibm, int numActive, typename OV::value_type u_bc_val, Kokkos::View< const double *, IMem > u_bc=Kokkos::View< const double *, IMem >())
Real poly_D_avg(Real xi)
Real poly_Nc(Real xi)
Real poly_D(Real xi)
Real poly_D_sandwich(Real xi_m, Real xi_p)
Real poly_Nbc(Real)
Real poly_N_c_sandwich(Real xi_m, Real xi_p)
void ccFor3(const char *name, C3 lo, C3 hi, F f)
Real poly_Nbc_pp_sw(Real xi_m, Real xi_p)
Real poly_D_sandwich_avg(Real xi_m, Real xi_p)
Real poly_N_nb_slip(Real xi, Real lam)
Real poly_Nc_avg(Real xi)
void ibmBuildDiffusion(MV AC, MV AW, MV AE, MV AS, MV AN, MV AB, MV AT, int ex, int ey, int ez, double bx, double by, double bz, double idiag)
Real poly_Nnb_avg(Real xi)
Real poly_N_nb(Real xi)
Real poly_Nbc_mp_sw_avg(Real xi_m, Real xi_p)
void ibmFillEntry(const OV &o, int list_idx, int c_idx, typename OV::value_type sdf_c, const typename OV::value_type sdf_n[6], int bc_type, const typename OV::value_type *thEx, const typename OV::value_type *lamAxis=nullptr, int *sandwichSkipped=nullptr)
flow — the multidimensional iteration policies: iteration order follows storage.
Kokkos::View< int *, Space > cell_index
Kokkos::View< Real *, Space > M_val
Kokkos::View< Real *, Space > R_val
Kokkos::View< Real *, Space > X_val
Kokkos::View< Real *, Space > Nbc_val
Kokkos::View< Real *, Space > K_val
Kokkos::View< Real *, Space > D_rescale
Kokkos::View< int *, Space > num_boundaries
Kokkos::View< int *, Space > dir_code
static constexpr double AC