flow 0.4.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
11#ifndef PECLET_FLOW_CUT_CELL_IBM_HPP
12#define PECLET_FLOW_CUT_CELL_IBM_HPP
13
14#include <Kokkos_Core.hpp>
15#include <Kokkos_MathematicalFunctions.hpp>
16
17namespace peclet::flow {
18
19using IMem = Kokkos::DefaultExecutionSpace::memory_space;
20
21// ---- boundary-distance polynomials (verbatim from cut_cell_ibm.cuh) ----
22KOKKOS_INLINE_FUNCTION float poly_D(float xi) {
23 return xi * (1.0f + xi);
24}
25KOKKOS_INLINE_FUNCTION float poly_N_nb(float xi) {
26 return xi * (1.0f - xi);
27}
28KOKKOS_INLINE_FUNCTION float poly_Nc(float xi) {
29 return 2.0f * (xi * xi - 1.0f);
30}
31KOKKOS_INLINE_FUNCTION float poly_Nbc(float) {
32 return 2.0f;
33}
34KOKKOS_INLINE_FUNCTION float poly_D_avg(float xi) {
35 return xi * (1.0f + xi) - 1.0f / 12.0f;
36}
37KOKKOS_INLINE_FUNCTION float poly_Nnb_avg(float xi) {
38 return xi * (1.0f - xi) + 1.0f / 12.0f;
39}
40KOKKOS_INLINE_FUNCTION float poly_Nc_avg(float xi) {
41 return 2.0f * (xi * xi - 1.0f) - 1.0f / 6.0f;
42}
43KOKKOS_INLINE_FUNCTION float poly_Nbc_avg(float) {
44 return 2.0f;
45}
46KOKKOS_INLINE_FUNCTION float poly_D_sandwich(float xi_m, float xi_p) {
47 return xi_m * xi_p;
48}
49KOKKOS_INLINE_FUNCTION float poly_N_c_sandwich(float xi_m, float xi_p) {
50 return (xi_m + 1.0f) * (xi_p - 1.0f);
51}
52KOKKOS_INLINE_FUNCTION float poly_Nbc_pp_sw(float xi_m, float xi_p) {
53 return (xi_m / (xi_m + xi_p)) * (1.0f + xi_m);
54}
55KOKKOS_INLINE_FUNCTION float poly_Nbc_mp_sw(float xi_m, float xi_p) {
56 return (xi_p / (xi_m + xi_p)) * (1.0f - xi_p);
57}
58KOKKOS_INLINE_FUNCTION float poly_D_sandwich_avg(float xi_m, float xi_p) {
59 return xi_m * xi_p - 1.0f / 12.0f;
60}
61KOKKOS_INLINE_FUNCTION float poly_N_c_sandwich_avg(float xi_m, float xi_p) {
62 return (xi_m + 1.0f) * (xi_p - 1.0f) - 1.0f / 12.0f;
63}
64KOKKOS_INLINE_FUNCTION float poly_Nbc_pp_sw_avg(float xi_m, float xi_p) {
65 return (xi_m / (xi_m + xi_p)) * (1.0f + xi_m) - 1.0f / 12.0f;
66}
67KOKKOS_INLINE_FUNCTION float poly_Nbc_mp_sw_avg(float xi_m, float xi_p) {
68 return (xi_p / (xi_m + xi_p)) * (1.0f - xi_p) + 1.0f / 12.0f;
69}
70
71// IBM overlay output (SoA Views; per-direction arrays are size 6*num_cells). Templated on the
72// memory space so the device build and a HostSpace reference share the same fill code.
73template <class Space>
75 Kokkos::View<int*, Space> cell_index;
76 Kokkos::View<int*, Space> num_boundaries;
77 Kokkos::View<float*, Space> D_rescale;
78 Kokkos::View<int*, Space> dir_code;
79 Kokkos::View<float*, Space> K_val, M_val, X_val, Nbc_val, R_val;
80};
82
83// Fill one overlay entry (list_idx) for a cut cell from its 7 SDF samples. Verbatim port of
84// ibm_fill_entry<SCHEME>. bc_type: 0 = Dirichlet, 1 = Neumann. thEx (optional, may be nullptr):
85// per-direction EXACT wall-crossing fractions theta from the cut cell toward each of the 6
86// neighbours (analytic-SDF capability, setExactCrossings) — a finite thEx[k] overrides the
87// linear-interpolated theta; non-finite entries fall back.
88template <int SCHEME, class OV>
89KOKKOS_INLINE_FUNCTION void ibmFillEntry(const OV& o, int list_idx, int c_idx, float sdf_c,
90 const float sdf_n[6], int bc_type,
91 const float* thEx) {
92 o.cell_index(list_idx) = c_idx;
93 o.num_boundaries(list_idx) = 6;
94 bool is_ghost[6];
95 float xi_vals[6], D_vals[6];
96 for (int k = 0; k < 6; ++k) {
97 if (sdf_n[k] < 0.0f) {
98 is_ghost[k] = true;
99 if (bc_type == 0) {
100 float theta = sdf_c / (sdf_c - sdf_n[k]);
101 if (thEx != nullptr && Kokkos::isfinite(thEx[k]))
102 theta = thEx[k];
103 if (theta < 1e-4f)
104 theta = 1e-4f;
105 if (theta > 1.0f)
106 theta = 1.0f;
107 xi_vals[k] = theta;
108 D_vals[k] = (SCHEME == 0) ? poly_D(theta) : poly_D_avg(theta);
109 } else {
110 xi_vals[k] = 0.5f;
111 D_vals[k] = 1.0f;
112 }
113 } else {
114 is_ghost[k] = false;
115 xi_vals[k] = 1.0f;
116 D_vals[k] = 1e9f;
117 }
118 }
119
120 if (bc_type == 0) {
121 bool is_sandwich[3] = {is_ghost[0] && is_ghost[1], is_ghost[2] && is_ghost[3],
122 is_ghost[4] && is_ghost[5]};
123 float D_sandwich[3] = {0, 0, 0};
124 for (int a = 0; a < 3; ++a)
125 if (is_sandwich[a])
126 D_sandwich[a] = (SCHEME == 0) ? poly_D_sandwich(xi_vals[2 * a + 1], xi_vals[2 * a])
127 : poly_D_sandwich_avg(xi_vals[2 * a + 1], xi_vals[2 * a]);
128 float min_D_abs = 1e30f, D_rescale = 1.0f;
129 auto update_min = [&](float val) {
130 if (Kokkos::fabs(val) < min_D_abs) {
131 min_D_abs = Kokkos::fabs(val);
132 D_rescale = val;
133 }
134 };
135 for (int axis = 0; axis < 3; ++axis) {
136 if (is_sandwich[axis])
137 update_min(D_sandwich[axis]);
138 else {
139 if (is_ghost[2 * axis])
140 update_min(D_vals[2 * axis]);
141 if (is_ghost[2 * axis + 1])
142 update_min(D_vals[2 * axis + 1]);
143 }
144 }
145 o.D_rescale(list_idx) = D_rescale;
146
147 for (int axis = 0; axis < 3; ++axis) {
148 int km = 2 * axis + 1, kp = 2 * axis;
149 bool sandwich = is_sandwich[axis], g_p = is_ghost[kp], g_m = is_ghost[km];
150 float D_axis =
151 sandwich ? D_sandwich[axis] : (g_p ? D_vals[kp] : (g_m ? D_vals[km] : D_rescale));
152 float R = D_rescale / D_axis;
153 if (Kokkos::fabs(D_axis) < 1e-9f)
154 R = 1.0f;
155 o.R_val(list_idx * 6 + kp) = R;
156 o.R_val(list_idx * 6 + km) = R;
157 if (sandwich) {
158 if (SCHEME == 0) {
159 o.K_val(list_idx * 6 + kp) = poly_N_c_sandwich(xi_vals[km], xi_vals[kp]) * R;
160 o.K_val(list_idx * 6 + km) = poly_N_c_sandwich(xi_vals[kp], xi_vals[km]) * R;
161 o.Nbc_val(list_idx * 6 + kp) = (poly_Nbc_pp_sw(xi_vals[km], xi_vals[kp]) +
162 poly_Nbc_mp_sw(xi_vals[km], xi_vals[kp])) *
163 R;
164 o.Nbc_val(list_idx * 6 + km) = (poly_Nbc_pp_sw(xi_vals[kp], xi_vals[km]) +
165 poly_Nbc_mp_sw(xi_vals[kp], xi_vals[km])) *
166 R;
167 } else {
168 o.K_val(list_idx * 6 + kp) = poly_N_c_sandwich_avg(xi_vals[km], xi_vals[kp]) * R;
169 o.K_val(list_idx * 6 + km) = poly_N_c_sandwich_avg(xi_vals[kp], xi_vals[km]) * R;
170 o.Nbc_val(list_idx * 6 + kp) = (poly_Nbc_pp_sw_avg(xi_vals[km], xi_vals[kp]) +
171 poly_Nbc_mp_sw_avg(xi_vals[km], xi_vals[kp])) *
172 R;
173 o.Nbc_val(list_idx * 6 + km) = (poly_Nbc_pp_sw_avg(xi_vals[kp], xi_vals[km]) +
174 poly_Nbc_mp_sw_avg(xi_vals[kp], xi_vals[km])) *
175 R;
176 }
177 o.M_val(list_idx * 6 + kp) = 0.0f;
178 o.X_val(list_idx * 6 + kp) = 0.0f;
179 o.M_val(list_idx * 6 + km) = 0.0f;
180 o.X_val(list_idx * 6 + km) = 0.0f;
181 } else {
182 for (int side = 0; side < 2; ++side) {
183 int kk = side == 0 ? kp : km;
184 if (is_ghost[kk]) {
185 if (SCHEME == 0) {
186 o.K_val(list_idx * 6 + kk) = poly_Nc(xi_vals[kk]) * R;
187 o.X_val(list_idx * 6 + kk) = poly_N_nb(xi_vals[kk]) * R;
188 o.Nbc_val(list_idx * 6 + kk) = poly_Nbc(xi_vals[kk]) * R;
189 } else {
190 o.K_val(list_idx * 6 + kk) = poly_Nc_avg(xi_vals[kk]) * R;
191 o.X_val(list_idx * 6 + kk) = poly_Nnb_avg(xi_vals[kk]) * R;
192 o.Nbc_val(list_idx * 6 + kk) = poly_Nbc_avg(xi_vals[kk]) * R;
193 }
194 o.M_val(list_idx * 6 + kk) = 0.0f;
195 } else {
196 o.K_val(list_idx * 6 + kk) = 0.0f;
197 o.M_val(list_idx * 6 + kk) = 1.0f;
198 o.X_val(list_idx * 6 + kk) = 0.0f;
199 o.Nbc_val(list_idx * 6 + kk) = 0.0f;
200 }
201 }
202 }
203 o.dir_code(list_idx * 6 + kp) = kp;
204 o.dir_code(list_idx * 6 + km) = km;
205 }
206 } else { // Neumann
207 o.D_rescale(list_idx) = 1.0f;
208 for (int k = 0; k < 6; ++k) {
209 o.dir_code(list_idx * 6 + k) = k;
210 o.R_val(list_idx * 6 + k) = 1.0f;
211 o.K_val(list_idx * 6 + k) = is_ghost[k] ? 1.0f : 0.0f;
212 o.M_val(list_idx * 6 + k) = is_ghost[k] ? 0.0f : 1.0f;
213 o.X_val(list_idx * 6 + k) = 0.0f;
214 o.Nbc_val(list_idx * 6 + k) = 0.0f;
215 }
216 }
217}
218
219// Sampled-theta entry point (the historical signature; all existing call sites unchanged).
220template <int SCHEME, class OV>
221KOKKOS_INLINE_FUNCTION void ibmFillEntry(const OV& o, int list_idx, int c_idx, float sdf_c,
222 const float sdf_n[6], int bc_type) {
224}
225
226// Build the backward-Euler velocity diffusion stencil over the extended block (divided convention):
227// A_C = idiag + 6*beta, off-diagonals = -beta (dx=1). idiag = 1/dt, beta = nu.
228inline void ibmBuildDiffusion(Kokkos::View<float*, IMem> AC, Kokkos::View<float*, IMem> AW,
229 Kokkos::View<float*, IMem> AE, Kokkos::View<float*, IMem> AS,
230 Kokkos::View<float*, IMem> AN, Kokkos::View<float*, IMem> AB,
231 Kokkos::View<float*, IMem> AT, int ex, int ey, int ez, double beta,
232 double idiag) {
233 Kokkos::DefaultExecutionSpace space;
234 const std::size_t n = (std::size_t)ex * ey * ez;
235 const float nb = (float)(-beta), c = (float)(idiag + 6.0 * beta);
236 Kokkos::parallel_for(
237 "peclet::flow::ibm_build_diff", Kokkos::RangePolicy<Kokkos::DefaultExecutionSpace>(0, n),
238 KOKKOS_LAMBDA(std::size_t i) {
239 AC(i) = c;
240 AW(i) = nb;
241 AE(i) = nb;
242 AS(i) = nb;
243 AN(i) = nb;
244 AB(i) = nb;
245 AT(i) = nb;
246 });
247}
248
249// Variable-viscosity backward-Euler diffusion stencil (sibling of ibmBuildDiffusion): the face
250// off-diagonal is -beta_face (per-face viscosity from FaceProps) and A_C = idiag(i) + sum of the 6
251// face betas. Built over INNER cells (neighbour mu at i+-stride must be valid — fill the mu ghosts
252// first). Face means are computed in double, cast to float once (mirroring the constant path).
253// FaceProps: UniformFaceProps reproduces the constant operator; FieldFaceProps reads a mu field.
254template <class FaceProps>
255inline void ibmBuildDiffusionVar(Kokkos::View<float*, IMem> AC, Kokkos::View<float*, IMem> AW,
256 Kokkos::View<float*, IMem> AE, Kokkos::View<float*, IMem> AS,
257 Kokkos::View<float*, IMem> AN, Kokkos::View<float*, IMem> AB,
258 Kokkos::View<float*, IMem> AT, int ex, int ey, int ez, int g,
259 FaceProps fp) {
260 Kokkos::DefaultExecutionSpace space;
261 using MD = Kokkos::MDRangePolicy<Kokkos::DefaultExecutionSpace, Kokkos::Rank<3>>;
262 Kokkos::parallel_for(
263 "peclet::flow::ibm_build_diff_var", MD(space, {g, g, g}, {ex - g, ey - g, ez - g}),
264 KOKKOS_LAMBDA(int lx, int ly, int lz) {
265 const long sx = 1, sy = ex, sz = (long)ex * ey;
266 const long i = (long)lx + (long)ly * sy + (long)lz * sz;
267 const double bw = fp.beta(i, i - sx), be = fp.beta(i, i + sx);
268 const double bs = fp.beta(i, i - sy), bn = fp.beta(i, i + sy);
269 const double bb = fp.beta(i, i - sz), bt = fp.beta(i, i + sz);
270 AW(i) = (float)(-bw);
271 AE(i) = (float)(-be);
272 AS(i) = (float)(-bs);
273 AN(i) = (float)(-bn);
274 AB(i) = (float)(-bb);
275 AT(i) = (float)(-bt);
276 AC(i) = (float)(fp.idiag(i) + bw + be + bs + bn + bb + bt);
277 });
278}
279
280// Apply the Robust-Scaled overlay to the momentum stencil at each cut cell (port of
281// ibm_modify_stencil_k): modify A_C / 6 off-diagonals + accumulate the inhomogeneous
282// (wall-velocity) term and store the row scaling. Each cut cell owns a distinct grid index c -> no
283// races.
284inline void ibmModifyStencil(Kokkos::View<float*, IMem> AC, Kokkos::View<float*, IMem> AW,
285 Kokkos::View<float*, IMem> AE, Kokkos::View<float*, IMem> AS,
286 Kokkos::View<float*, IMem> AN, Kokkos::View<float*, IMem> AB,
287 Kokkos::View<float*, IMem> AT, Kokkos::View<double*, IMem> a_inhom,
288 Kokkos::View<double*, IMem> rhs_scale, const IbmOverlay& ibm,
289 int numActive, float u_bc_val) {
290 Kokkos::DefaultExecutionSpace space;
291 const bool hasInhom = (a_inhom.extent(0) != 0), hasScale = (rhs_scale.extent(0) != 0);
292 Kokkos::parallel_for(
293 "peclet::flow::ibm_modify", Kokkos::RangePolicy<Kokkos::DefaultExecutionSpace>(0, numActive),
295 const int OPP[6] = {1, 0, 3, 2, 5, 4};
296 const int c = ibm.cell_index(list_idx);
297 const float descale = ibm.D_rescale(list_idx);
298 if (hasScale)
299 rhs_scale(c) = descale;
300 const double orig[6] = {AE(c), AW(c), AN(c), AS(c), AT(c), AB(c)};
301 double aC = (double)AC(c) * (double)descale;
302 double mod[6] = {0, 0, 0, 0, 0, 0};
303 double inhom = 0.0;
304 for (int k = 0; k < 6; ++k) {
305 const float K = ibm.K_val(list_idx * 6 + k), M = ibm.M_val(list_idx * 6 + k);
306 const float X = ibm.X_val(list_idx * 6 + k), Nbc = ibm.Nbc_val(list_idx * 6 + k);
307 const double vnb = orig[k];
308 aC += vnb * K;
309 inhom += (double)Nbc * u_bc_val * vnb;
310 mod[k] += vnb * ((double)descale * M - 1.0);
311 mod[OPP[k]] += vnb * X;
312 }
313 AC(c) = (float)aC;
314 AE(c) = (float)(orig[0] + mod[0]);
315 AW(c) = (float)(orig[1] + mod[1]);
316 AN(c) = (float)(orig[2] + mod[2]);
317 AS(c) = (float)(orig[3] + mod[3]);
318 AT(c) = (float)(orig[4] + mod[4]);
319 AB(c) = (float)(orig[5] + mod[5]);
320 if (hasInhom)
321 a_inhom(c) += inhom;
322 });
323}
324
325} // namespace peclet::flow
326
327#endif // PECLET_FLOW_CUT_CELL_IBM_HPP
float poly_D(float xi)
float poly_Nbc_mp_sw(float xi_m, float xi_p)
Kokkos::DefaultExecutionSpace::memory_space IMem
float poly_Nbc_pp_sw(float xi_m, float xi_p)
float poly_N_c_sandwich_avg(float xi_m, float xi_p)
float poly_Nbc_avg(float)
float poly_Nc_avg(float xi)
float poly_Nbc_pp_sw_avg(float xi_m, float xi_p)
float poly_D_sandwich_avg(float xi_m, float xi_p)
float poly_D_sandwich(float xi_m, float xi_p)
void ibmBuildDiffusionVar(Kokkos::View< float *, IMem > AC, Kokkos::View< float *, IMem > AW, Kokkos::View< float *, IMem > AE, Kokkos::View< float *, IMem > AS, Kokkos::View< float *, IMem > AN, Kokkos::View< float *, IMem > AB, Kokkos::View< float *, IMem > AT, int ex, int ey, int ez, int g, FaceProps fp)
float poly_Nc(float xi)
float poly_N_nb(float xi)
void ibmBuildDiffusion(Kokkos::View< float *, IMem > AC, Kokkos::View< float *, IMem > AW, Kokkos::View< float *, IMem > AE, Kokkos::View< float *, IMem > AS, Kokkos::View< float *, IMem > AN, Kokkos::View< float *, IMem > AB, Kokkos::View< float *, IMem > AT, int ex, int ey, int ez, double beta, double idiag)
float poly_Nbc_mp_sw_avg(float xi_m, float xi_p)
float poly_Nnb_avg(float xi)
void ibmFillEntry(const OV &o, int list_idx, int c_idx, float sdf_c, const float sdf_n[6], int bc_type, const float *thEx)
float poly_Nbc(float)
void ibmModifyStencil(Kokkos::View< float *, IMem > AC, Kokkos::View< float *, IMem > AW, Kokkos::View< float *, IMem > AE, Kokkos::View< float *, IMem > AS, Kokkos::View< float *, IMem > AN, Kokkos::View< float *, IMem > AB, Kokkos::View< float *, IMem > AT, Kokkos::View< double *, IMem > a_inhom, Kokkos::View< double *, IMem > rhs_scale, const IbmOverlay &ibm, int numActive, float u_bc_val)
float poly_N_c_sandwich(float xi_m, float xi_p)
float poly_D_avg(float xi)
Kokkos::View< float *, Space > X_val
Kokkos::View< int *, Space > num_boundaries
Kokkos::View< int *, Space > dir_code
Kokkos::View< float *, Space > K_val
Kokkos::View< float *, Space > Nbc_val
Kokkos::View< float *, Space > M_val
Kokkos::View< float *, Space > D_rescale
Kokkos::View< float *, Space > R_val
Kokkos::View< int *, Space > cell_index
static constexpr double AC