flow
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.
85template <int SCHEME, class OV>
86KOKKOS_INLINE_FUNCTION void ibmFillEntry(const OV& o, int list_idx, int c_idx, float sdf_c,
87 const float sdf_n[6], int bc_type) {
88 o.cell_index(list_idx) = c_idx;
89 o.num_boundaries(list_idx) = 6;
90 bool is_ghost[6];
91 float xi_vals[6], D_vals[6];
92 for (int k = 0; k < 6; ++k) {
93 if (sdf_n[k] < 0.0f) {
94 is_ghost[k] = true;
95 if (bc_type == 0) {
96 float theta = sdf_c / (sdf_c - sdf_n[k]);
97 if (theta < 1e-4f)
98 theta = 1e-4f;
99 if (theta > 1.0f)
100 theta = 1.0f;
101 xi_vals[k] = theta;
102 D_vals[k] = (SCHEME == 0) ? poly_D(theta) : poly_D_avg(theta);
103 } else {
104 xi_vals[k] = 0.5f;
105 D_vals[k] = 1.0f;
106 }
107 } else {
108 is_ghost[k] = false;
109 xi_vals[k] = 1.0f;
110 D_vals[k] = 1e9f;
111 }
112 }
113
114 if (bc_type == 0) {
115 bool is_sandwich[3] = {is_ghost[0] && is_ghost[1], is_ghost[2] && is_ghost[3],
116 is_ghost[4] && is_ghost[5]};
117 float D_sandwich[3] = {0, 0, 0};
118 for (int a = 0; a < 3; ++a)
119 if (is_sandwich[a])
120 D_sandwich[a] = (SCHEME == 0) ? poly_D_sandwich(xi_vals[2 * a + 1], xi_vals[2 * a])
121 : poly_D_sandwich_avg(xi_vals[2 * a + 1], xi_vals[2 * a]);
122 float min_D_abs = 1e30f, D_rescale = 1.0f;
123 auto update_min = [&](float val) {
124 if (Kokkos::fabs(val) < min_D_abs) {
125 min_D_abs = Kokkos::fabs(val);
126 D_rescale = val;
127 }
128 };
129 for (int axis = 0; axis < 3; ++axis) {
130 if (is_sandwich[axis])
131 update_min(D_sandwich[axis]);
132 else {
133 if (is_ghost[2 * axis])
134 update_min(D_vals[2 * axis]);
135 if (is_ghost[2 * axis + 1])
136 update_min(D_vals[2 * axis + 1]);
137 }
138 }
139 o.D_rescale(list_idx) = D_rescale;
140
141 for (int axis = 0; axis < 3; ++axis) {
142 int km = 2 * axis + 1, kp = 2 * axis;
143 bool sandwich = is_sandwich[axis], g_p = is_ghost[kp], g_m = is_ghost[km];
144 float D_axis =
145 sandwich ? D_sandwich[axis] : (g_p ? D_vals[kp] : (g_m ? D_vals[km] : D_rescale));
146 float R = D_rescale / D_axis;
147 if (Kokkos::fabs(D_axis) < 1e-9f)
148 R = 1.0f;
149 o.R_val(list_idx * 6 + kp) = R;
150 o.R_val(list_idx * 6 + km) = R;
151 if (sandwich) {
152 if (SCHEME == 0) {
153 o.K_val(list_idx * 6 + kp) = poly_N_c_sandwich(xi_vals[km], xi_vals[kp]) * R;
154 o.K_val(list_idx * 6 + km) = poly_N_c_sandwich(xi_vals[kp], xi_vals[km]) * R;
155 o.Nbc_val(list_idx * 6 + kp) = (poly_Nbc_pp_sw(xi_vals[km], xi_vals[kp]) +
156 poly_Nbc_mp_sw(xi_vals[km], xi_vals[kp])) *
157 R;
158 o.Nbc_val(list_idx * 6 + km) = (poly_Nbc_pp_sw(xi_vals[kp], xi_vals[km]) +
159 poly_Nbc_mp_sw(xi_vals[kp], xi_vals[km])) *
160 R;
161 } else {
162 o.K_val(list_idx * 6 + kp) = poly_N_c_sandwich_avg(xi_vals[km], xi_vals[kp]) * R;
163 o.K_val(list_idx * 6 + km) = poly_N_c_sandwich_avg(xi_vals[kp], xi_vals[km]) * R;
164 o.Nbc_val(list_idx * 6 + kp) = (poly_Nbc_pp_sw_avg(xi_vals[km], xi_vals[kp]) +
165 poly_Nbc_mp_sw_avg(xi_vals[km], xi_vals[kp])) *
166 R;
167 o.Nbc_val(list_idx * 6 + km) = (poly_Nbc_pp_sw_avg(xi_vals[kp], xi_vals[km]) +
168 poly_Nbc_mp_sw_avg(xi_vals[kp], xi_vals[km])) *
169 R;
170 }
171 o.M_val(list_idx * 6 + kp) = 0.0f;
172 o.X_val(list_idx * 6 + kp) = 0.0f;
173 o.M_val(list_idx * 6 + km) = 0.0f;
174 o.X_val(list_idx * 6 + km) = 0.0f;
175 } else {
176 for (int side = 0; side < 2; ++side) {
177 int kk = side == 0 ? kp : km;
178 if (is_ghost[kk]) {
179 if (SCHEME == 0) {
180 o.K_val(list_idx * 6 + kk) = poly_Nc(xi_vals[kk]) * R;
181 o.X_val(list_idx * 6 + kk) = poly_N_nb(xi_vals[kk]) * R;
182 o.Nbc_val(list_idx * 6 + kk) = poly_Nbc(xi_vals[kk]) * R;
183 } else {
184 o.K_val(list_idx * 6 + kk) = poly_Nc_avg(xi_vals[kk]) * R;
185 o.X_val(list_idx * 6 + kk) = poly_Nnb_avg(xi_vals[kk]) * R;
186 o.Nbc_val(list_idx * 6 + kk) = poly_Nbc_avg(xi_vals[kk]) * R;
187 }
188 o.M_val(list_idx * 6 + kk) = 0.0f;
189 } else {
190 o.K_val(list_idx * 6 + kk) = 0.0f;
191 o.M_val(list_idx * 6 + kk) = 1.0f;
192 o.X_val(list_idx * 6 + kk) = 0.0f;
193 o.Nbc_val(list_idx * 6 + kk) = 0.0f;
194 }
195 }
196 }
197 o.dir_code(list_idx * 6 + kp) = kp;
198 o.dir_code(list_idx * 6 + km) = km;
199 }
200 } else { // Neumann
201 o.D_rescale(list_idx) = 1.0f;
202 for (int k = 0; k < 6; ++k) {
203 o.dir_code(list_idx * 6 + k) = k;
204 o.R_val(list_idx * 6 + k) = 1.0f;
205 o.K_val(list_idx * 6 + k) = is_ghost[k] ? 1.0f : 0.0f;
206 o.M_val(list_idx * 6 + k) = is_ghost[k] ? 0.0f : 1.0f;
207 o.X_val(list_idx * 6 + k) = 0.0f;
208 o.Nbc_val(list_idx * 6 + k) = 0.0f;
209 }
210 }
211}
212
213// Build the backward-Euler velocity diffusion stencil over the extended block (divided convention):
214// A_C = idiag + 6*beta, off-diagonals = -beta (dx=1). idiag = 1/dt, beta = nu.
215inline void ibmBuildDiffusion(Kokkos::View<float*, IMem> AC, Kokkos::View<float*, IMem> AW,
216 Kokkos::View<float*, IMem> AE, Kokkos::View<float*, IMem> AS,
217 Kokkos::View<float*, IMem> AN, Kokkos::View<float*, IMem> AB,
218 Kokkos::View<float*, IMem> AT, int ex, int ey, int ez, double beta,
219 double idiag) {
220 Kokkos::DefaultExecutionSpace space;
221 const std::size_t n = (std::size_t)ex * ey * ez;
222 const float nb = (float)(-beta), c = (float)(idiag + 6.0 * beta);
223 Kokkos::parallel_for(
224 "peclet::flow::ibm_build_diff", Kokkos::RangePolicy<Kokkos::DefaultExecutionSpace>(0, n),
225 KOKKOS_LAMBDA(std::size_t i) {
226 AC(i) = c;
227 AW(i) = nb;
228 AE(i) = nb;
229 AS(i) = nb;
230 AN(i) = nb;
231 AB(i) = nb;
232 AT(i) = nb;
233 });
234}
235
236// Variable-viscosity backward-Euler diffusion stencil (sibling of ibmBuildDiffusion): the face
237// off-diagonal is -beta_face (per-face viscosity from FaceProps) and A_C = idiag(i) + sum of the 6
238// face betas. Built over INNER cells (neighbour mu at i+-stride must be valid — fill the mu ghosts
239// first). Face means are computed in double, cast to float once (mirroring the constant path).
240// FaceProps: UniformFaceProps reproduces the constant operator; FieldFaceProps reads a mu field.
241template <class FaceProps>
242inline void ibmBuildDiffusionVar(Kokkos::View<float*, IMem> AC, Kokkos::View<float*, IMem> AW,
243 Kokkos::View<float*, IMem> AE, Kokkos::View<float*, IMem> AS,
244 Kokkos::View<float*, IMem> AN, Kokkos::View<float*, IMem> AB,
245 Kokkos::View<float*, IMem> AT, int ex, int ey, int ez, int g,
246 FaceProps fp) {
247 Kokkos::DefaultExecutionSpace space;
248 using MD = Kokkos::MDRangePolicy<Kokkos::DefaultExecutionSpace, Kokkos::Rank<3>>;
249 Kokkos::parallel_for(
250 "peclet::flow::ibm_build_diff_var", MD(space, {g, g, g}, {ex - g, ey - g, ez - g}),
251 KOKKOS_LAMBDA(int lx, int ly, int lz) {
252 const long sx = 1, sy = ex, sz = (long)ex * ey;
253 const long i = (long)lx + (long)ly * sy + (long)lz * sz;
254 const double bw = fp.beta(i, i - sx), be = fp.beta(i, i + sx);
255 const double bs = fp.beta(i, i - sy), bn = fp.beta(i, i + sy);
256 const double bb = fp.beta(i, i - sz), bt = fp.beta(i, i + sz);
257 AW(i) = (float)(-bw);
258 AE(i) = (float)(-be);
259 AS(i) = (float)(-bs);
260 AN(i) = (float)(-bn);
261 AB(i) = (float)(-bb);
262 AT(i) = (float)(-bt);
263 AC(i) = (float)(fp.idiag(i) + bw + be + bs + bn + bb + bt);
264 });
265}
266
267// Apply the Robust-Scaled overlay to the momentum stencil at each cut cell (port of
268// ibm_modify_stencil_k): modify A_C / 6 off-diagonals + accumulate the inhomogeneous
269// (wall-velocity) term and store the row scaling. Each cut cell owns a distinct grid index c -> no
270// races.
271inline void ibmModifyStencil(Kokkos::View<float*, IMem> AC, Kokkos::View<float*, IMem> AW,
272 Kokkos::View<float*, IMem> AE, Kokkos::View<float*, IMem> AS,
273 Kokkos::View<float*, IMem> AN, Kokkos::View<float*, IMem> AB,
274 Kokkos::View<float*, IMem> AT, Kokkos::View<double*, IMem> a_inhom,
275 Kokkos::View<double*, IMem> rhs_scale, const IbmOverlay& ibm,
276 int numActive, float u_bc_val) {
277 Kokkos::DefaultExecutionSpace space;
278 const bool hasInhom = (a_inhom.extent(0) != 0), hasScale = (rhs_scale.extent(0) != 0);
279 Kokkos::parallel_for(
280 "peclet::flow::ibm_modify", Kokkos::RangePolicy<Kokkos::DefaultExecutionSpace>(0, numActive),
281 KOKKOS_LAMBDA(int list_idx) {
282 const int OPP[6] = {1, 0, 3, 2, 5, 4};
283 const int c = ibm.cell_index(list_idx);
284 const float descale = ibm.D_rescale(list_idx);
285 if (hasScale)
286 rhs_scale(c) = descale;
287 const double orig[6] = {AE(c), AW(c), AN(c), AS(c), AT(c), AB(c)};
288 double aC = (double)AC(c) * (double)descale;
289 double mod[6] = {0, 0, 0, 0, 0, 0};
290 double inhom = 0.0;
291 for (int k = 0; k < 6; ++k) {
292 const float K = ibm.K_val(list_idx * 6 + k), M = ibm.M_val(list_idx * 6 + k);
293 const float X = ibm.X_val(list_idx * 6 + k), Nbc = ibm.Nbc_val(list_idx * 6 + k);
294 const double vnb = orig[k];
295 aC += vnb * K;
296 inhom += (double)Nbc * u_bc_val * vnb;
297 mod[k] += vnb * ((double)descale * M - 1.0);
298 mod[OPP[k]] += vnb * X;
299 }
300 AC(c) = (float)aC;
301 AE(c) = (float)(orig[0] + mod[0]);
302 AW(c) = (float)(orig[1] + mod[1]);
303 AN(c) = (float)(orig[2] + mod[2]);
304 AS(c) = (float)(orig[3] + mod[3]);
305 AT(c) = (float)(orig[4] + mod[4]);
306 AB(c) = (float)(orig[5] + mod[5]);
307 if (hasInhom)
308 a_inhom(c) += inhom;
309 });
310}
311
312} // namespace peclet::flow
313
314#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)
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)
void ibmFillEntry(const OV &o, int list_idx, int c_idx, float sdf_c, const float sdf_n[6], int bc_type)
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