flow
Kokkos cut-cell IBM incompressible Navier-Stokes solver + pnm pore extraction
Loading...
Searching...
No Matches
mac_pressure.hpp
Go to the documentation of this file.
1
10#ifndef PECLET_FLOW_MAC_PRESSURE_HPP
11#define PECLET_FLOW_MAC_PRESSURE_HPP
12
13#include <Kokkos_Core.hpp>
14
15#include "mac_cutcell.hpp"
16
17namespace peclet::flow {
18
19// A = -div(open grad): AC = sum of the 6 face terms (openness*gf), off-diagonal across each face =
20// -term. ox[i] is the -x face openness of cell i (== +x face of cell i-1). (mg_build_op_k port.)
21// OpV is the operator-coefficient view type (float `mreal` to match CUDA, or double).
22template <class OpV>
23inline void buildCutcellOp(OpV AC, OpV AW, OpV AE, OpV AS, OpV AN, OpV AB, OpV AT, CCConst ox,
24 CCConst oy, CCConst oz, C3 e, int g, double gfx, double gfy,
25 double gfz) {
26 CCExec space;
27 using MD = Kokkos::MDRangePolicy<CCExec, Kokkos::Rank<3>>;
28 Kokkos::parallel_for(
29 "peclet::flow::cc_build_op", MD(space, {g, g, g}, {e.x - g, e.y - g, e.z - g}),
30 KOKKOS_LAMBDA(int lx, int ly, int lz) {
31 const long sx = 1, sy = e.x, sz = (long)e.x * e.y;
32 const long i = (long)lx + (long)ly * sy + (long)lz * sz;
33 const double tw = ox(i) * gfx, te = ox(i + sx) * gfx;
34 const double ts = oy(i) * gfy, tn = oy(i + sy) * gfy;
35 const double tb = oz(i) * gfz, tt = oz(i + sz) * gfz;
36 AW(i) = -tw;
37 AE(i) = -te;
38 AS(i) = -ts;
39 AN(i) = -tn;
40 AB(i) = -tb;
41 AT(i) = -tt;
42 AC(i) = te + tw + tn + ts + tt + tb;
43 });
44}
45
46// Open-weighted flux divergence d_i = sum_f signed(o_f * face-velocity), consistent with A
47// (diverg_open_k).
48inline void divergOpen(CCConst u, CCConst v, CCConst w, CCConst ox, CCConst oy, CCConst oz,
49 CCField d, C3 e, int g) {
50 CCExec space;
51 using MD = Kokkos::MDRangePolicy<CCExec, Kokkos::Rank<3>>;
52 Kokkos::parallel_for(
53 "peclet::flow::diverg_open", MD(space, {g, g, g}, {e.x - g, e.y - g, e.z - g}),
54 KOKKOS_LAMBDA(int x, int y, int z) {
55 const long sx = 1, sy = e.x, sz = (long)e.x * e.y;
56 const long i = (long)x + (long)y * sy + (long)z * sz;
57 d(i) = (ox(i + sx) * u(i + sx) - ox(i) * u(i)) + (oy(i + sy) * v(i + sy) - oy(i) * v(i)) +
58 (oz(i + sz) * w(i + sz) - oz(i) * w(i));
59 });
60}
61
62// One red/black sweep of the variable operator: phi=(b - offdiag)/AC; AC~0 (fully solid) cells
63// decoupled. b carries the negated divergence so the system is A phi = -div(u*) (matches the
64// validated const-coeff sign).
65template <class OpV>
66inline void cutcellSmoothColor(CCField phi, CCConst b, OpV AC, OpV AW, OpV AE, OpV AS, OpV AN,
67 OpV AB, OpV AT, C3 e, C3 og, int g, int color) {
68 CCExec space;
69 using MD = Kokkos::MDRangePolicy<CCExec, Kokkos::Rank<3>>;
70 Kokkos::parallel_for(
71 "peclet::flow::cc_smooth", MD(space, {g, g, g}, {e.x - g, e.y - g, e.z - g}),
72 KOKKOS_LAMBDA(int lx, int ly, int lz) {
73 if (((og.x + lx + og.y + ly + og.z + lz) & 1) != color)
74 return;
75 const long sx = 1, sy = e.x, sz = (long)e.x * e.y;
76 const long i = (long)lx + (long)ly * sy + (long)lz * sz;
77 const double ac = AC(i);
78 if (ac < 1e-30)
79 return; // fully closed (solid) cell: decoupled, phi stays 0
80 const double s = AE(i) * phi(i + sx) + AW(i) * phi(i - sx) + AN(i) * phi(i + sy) +
81 AS(i) * phi(i - sy) + AT(i) * phi(i + sz) + AB(i) * phi(i - sz);
82 phi(i) = (b(i) - s) / ac;
83 });
84}
85
86// y = A x for the cut-cell operator over inner cells (matvec for PCG; mg_apply_var_k port).
87template <class OpV>
88inline void applyCutcellOp(CCField y, CCConst x, OpV AC, OpV AW, OpV AE, OpV AS, OpV AN, OpV AB,
89 OpV AT, C3 e, int g) {
90 CCExec space;
91 using MD = Kokkos::MDRangePolicy<CCExec, Kokkos::Rank<3>>;
92 Kokkos::parallel_for(
93 "peclet::flow::cc_apply", MD(space, {g, g, g}, {e.x - g, e.y - g, e.z - g}),
94 KOKKOS_LAMBDA(int lx, int ly, int lz) {
95 const long sx = 1, sy = e.x, sz = (long)e.x * e.y;
96 const long i = (long)lx + (long)ly * sy + (long)lz * sz;
97 y(i) = AC(i) * x(i) + AE(i) * x(i + sx) + AW(i) * x(i - sx) + AN(i) * x(i + sy) +
98 AS(i) * x(i - sy) + AT(i) * x(i + sz) + AB(i) * x(i - sz);
99 });
100}
101
102// Projection correction u -= grad(phi) on the staggered faces (correct_k port). No openness here —
103// the openness lives in the operator + divergence; closed faces carry phi~0 on both sides so stay
104// unchanged.
105inline void projectCorrect(CCField u, CCField v, CCField w, CCConst phi, C3 e, int g) {
106 CCExec space;
107 using MD = Kokkos::MDRangePolicy<CCExec, Kokkos::Rank<3>>;
108 Kokkos::parallel_for(
109 "peclet::flow::correct", MD(space, {g, g, g}, {e.x - g, e.y - g, e.z - g}),
110 KOKKOS_LAMBDA(int x, int y, int z) {
111 const long sx = 1, sy = e.x, sz = (long)e.x * e.y;
112 const long i = (long)x + (long)y * sy + (long)z * sz;
113 u(i) -= phi(i) - phi(i - sx);
114 v(i) -= phi(i) - phi(i - sy);
115 w(i) -= phi(i) - phi(i - sz);
116 });
117}
118
119// Variable-density projection correction (sibling of projectCorrect): u_f -= (rho0/rho_f) grad(phi)
120// with rho_f the arithmetic face mean — the SAME face density that scaled the Poisson coefficient
121// c_f = open_f*rho0/rho_f, so the corrected open flux telescopes to A*phi exactly (discrete
122// consistency; constant rho == rho0 reduces to projectCorrect identically, ratio 1.0 exact in FP).
124 double rho0, C3 e, int g) {
125 CCExec space;
126 using MD = Kokkos::MDRangePolicy<CCExec, Kokkos::Rank<3>>;
127 Kokkos::parallel_for(
128 "peclet::flow::correct_var", MD(space, {g, g, g}, {e.x - g, e.y - g, e.z - g}),
129 KOKKOS_LAMBDA(int x, int y, int z) {
130 const long sx = 1, sy = e.x, sz = (long)e.x * e.y;
131 const long i = (long)x + (long)y * sy + (long)z * sz;
132 u(i) -= rho0 / (0.5 * (rho(i) + rho(i - sx))) * (phi(i) - phi(i - sx));
133 v(i) -= rho0 / (0.5 * (rho(i) + rho(i - sy))) * (phi(i) - phi(i - sy));
134 w(i) -= rho0 / (0.5 * (rho(i) + rho(i - sz))) * (phi(i) - phi(i - sz));
135 });
136}
137
138// Variable-density Poisson face coefficients on the MG (g=1) block: c_f = open_f * rho0 / rho_f,
139// rho_f = arithmetic face mean. Computed over the inner cells only — CutcellMG::setOpenness runs
140// its own periodic/halo ghost fill + non-periodic boundary re-imposition on whatever level-0 fields
141// it receives, exactly as for the raw openness (the coefficient "rides the openness rails"). The
142// rho ghost ring of the g=1 block must be valid (bridged from the filled G=2 field).
143inline void buildRhoCoeff(CCField cx, CCField cy, CCField cz, CCConst ox, CCConst oy, CCConst oz,
144 CCConst rho, double rho0, C3 e, int g) {
145 CCExec space;
146 using MD = Kokkos::MDRangePolicy<CCExec, Kokkos::Rank<3>>;
147 Kokkos::parallel_for(
148 "peclet::flow::rho_coeff", MD(space, {g, g, g}, {e.x - g, e.y - g, e.z - g}),
149 KOKKOS_LAMBDA(int x, int y, int z) {
150 const long sx = 1, sy = e.x, sz = (long)e.x * e.y;
151 const long i = (long)x + (long)y * sy + (long)z * sz;
152 cx(i) = ox(i) * rho0 / (0.5 * (rho(i) + rho(i - sx)));
153 cy(i) = oy(i) * rho0 / (0.5 * (rho(i) + rho(i - sy)));
154 cz(i) = oz(i) * rho0 / (0.5 * (rho(i) + rho(i - sz)));
155 });
156}
157
158// --- Volume-averaged (porous) continuity for unresolved CFD-DEM
159// ----------------------------------- The proper continuity is d(eps)/dt + div(eps u) = 0 (eps =
160// void fraction from the particles), so the velocity is NOT solenoidal: div(eps u) = -d(eps)/dt.
161// These size the projection to that constraint.
162
163// eps-weighted open-face divergence: d = div(open * eps_f * u), eps_f = arithmetic face mean.
164// Reduces to divergOpen when eps == 1 everywhere (no particles).
166 CCConst eps, CCField d, C3 e, int g) {
167 CCExec space;
168 using MD = Kokkos::MDRangePolicy<CCExec, Kokkos::Rank<3>>;
169 Kokkos::parallel_for(
170 "peclet::flow::diverg_open_eps", MD(space, {g, g, g}, {e.x - g, e.y - g, e.z - g}),
171 KOKKOS_LAMBDA(int x, int y, int z) {
172 const long sx = 1, sy = e.x, sz = (long)e.x * e.y;
173 const long i = (long)x + (long)y * sy + (long)z * sz;
174 const double exp = 0.5 * (eps(i) + eps(i + sx)), exm = 0.5 * (eps(i) + eps(i - sx));
175 const double eyp = 0.5 * (eps(i) + eps(i + sy)), eym = 0.5 * (eps(i) + eps(i - sy));
176 const double ezp = 0.5 * (eps(i) + eps(i + sz)), ezm = 0.5 * (eps(i) + eps(i - sz));
177 d(i) = (ox(i + sx) * exp * u(i + sx) - ox(i) * exm * u(i)) +
178 (oy(i + sy) * eyp * v(i + sy) - oy(i) * eym * v(i)) +
179 (oz(i + sz) * ezp * w(i + sz) - oz(i) * ezm * w(i));
180 });
181}
182
183// Porous Poisson face coefficient c_f = open_f * eps_f (eps_f = arithmetic face mean).
184// Constant-density gas: the correction stays u -= grad(phi) (projectCorrect) so the open*eps flux
185// telescopes to A*phi. (Combining with variable rho — c_f *= rho0/rho_f, projectCorrectVar — is a
186// later composition.)
187inline void buildPorousCoeff(CCField cx, CCField cy, CCField cz, CCConst ox, CCConst oy, CCConst oz,
188 CCConst eps, C3 e, int g) {
189 CCExec space;
190 using MD = Kokkos::MDRangePolicy<CCExec, Kokkos::Rank<3>>;
191 Kokkos::parallel_for(
192 "peclet::flow::porous_coeff", MD(space, {g, g, g}, {e.x - g, e.y - g, e.z - g}),
193 KOKKOS_LAMBDA(int x, int y, int z) {
194 const long sx = 1, sy = e.x, sz = (long)e.x * e.y;
195 const long i = (long)x + (long)y * sy + (long)z * sz;
196 cx(i) = ox(i) * 0.5 * (eps(i) + eps(i - sx));
197 cy(i) = oy(i) * 0.5 * (eps(i) + eps(i - sy));
198 cz(i) = oz(i) * 0.5 * (eps(i) + eps(i - sz));
199 });
200}
201
202// Semi-implicit-drag porous coefficient: c_f = open_f * eps_f * w_f, with the face drag-relaxation
203// w_f = idt/(idt + beta_f) (idt = rho/dt, beta = the momentum-diagonal drag coefficient). This
204// makes the pressure correction CONSISTENT with the drag-loaded momentum diagonal A_P = idt + beta:
205// where the drag is stiff (dense bed) w_f -> 0 and the pressure barely moves the velocity (the drag
206// holds it) — the SIMPLE/PISO-with-implicit-drag scheme (OpenFOAM rAU, MFIX). Reduces to
207// buildPorousCoeff when beta==0 (w==1). The correction MUST use the same w_f
208// (projectCorrectPorousDrag) so the open*eps*w flux telescopes to A*phi.
210 CCConst oz, CCConst eps, CCConst beta, double idt, C3 e, int g) {
211 CCExec space;
212 using MD = Kokkos::MDRangePolicy<CCExec, Kokkos::Rank<3>>;
213 Kokkos::parallel_for(
214 "peclet::flow::porous_coeff_drag", MD(space, {g, g, g}, {e.x - g, e.y - g, e.z - g}),
215 KOKKOS_LAMBDA(int x, int y, int z) {
216 const long sx = 1, sy = e.x, sz = (long)e.x * e.y;
217 const long i = (long)x + (long)y * sy + (long)z * sz;
218 cx(i) = ox(i) * 0.5 * (eps(i) + eps(i - sx)) * idt / (idt + 0.5 * (beta(i) + beta(i - sx)));
219 cy(i) = oy(i) * 0.5 * (eps(i) + eps(i - sy)) * idt / (idt + 0.5 * (beta(i) + beta(i - sy)));
220 cz(i) = oz(i) * 0.5 * (eps(i) + eps(i - sz)) * idt / (idt + 0.5 * (beta(i) + beta(i - sz)));
221 });
222}
223
224// Drag-relaxed velocity correction (sibling of projectCorrect): u_f -= w_f * grad(phi), w_f the
225// SAME face drag relaxation as buildPorousCoeffDrag. beta==0 -> w==1 -> projectCorrect exactly.
227 double idt, C3 e, int g) {
228 CCExec space;
229 using MD = Kokkos::MDRangePolicy<CCExec, Kokkos::Rank<3>>;
230 Kokkos::parallel_for(
231 "peclet::flow::correct_porous_drag", MD(space, {g, g, g}, {e.x - g, e.y - g, e.z - g}),
232 KOKKOS_LAMBDA(int x, int y, int z) {
233 const long sx = 1, sy = e.x, sz = (long)e.x * e.y;
234 const long i = (long)x + (long)y * sy + (long)z * sz;
235 u(i) -= idt / (idt + 0.5 * (beta(i) + beta(i - sx))) * (phi(i) - phi(i - sx));
236 v(i) -= idt / (idt + 0.5 * (beta(i) + beta(i - sy))) * (phi(i) - phi(i - sy));
237 w(i) -= idt / (idt + 0.5 * (beta(i) + beta(i - sz))) * (phi(i) - phi(i - sz));
238 });
239}
240
241} // namespace peclet::flow
242
243#endif // PECLET_FLOW_MAC_PRESSURE_HPP
flow — portable (Kokkos) cut-cell pressure-operator face openness from an SDF.
void buildPorousCoeffDrag(CCField cx, CCField cy, CCField cz, CCConst ox, CCConst oy, CCConst oz, CCConst eps, CCConst beta, double idt, C3 e, int g)
void projectCorrectPorousDrag(CCField u, CCField v, CCField w, CCConst phi, CCConst beta, double idt, C3 e, int g)
void buildRhoCoeff(CCField cx, CCField cy, CCField cz, CCConst ox, CCConst oy, CCConst oz, CCConst rho, double rho0, C3 e, int g)
void divergOpen(CCConst u, CCConst v, CCConst w, CCConst ox, CCConst oy, CCConst oz, CCField d, C3 e, int g)
void applyCutcellOp(CCField y, CCConst x, OpV AC, OpV AW, OpV AE, OpV AS, OpV AN, OpV AB, OpV AT, C3 e, int g)
void projectCorrectVar(CCField u, CCField v, CCField w, CCConst phi, CCConst rho, double rho0, C3 e, int g)
void buildCutcellOp(OpV AC, OpV AW, OpV AE, OpV AS, OpV AN, OpV AB, OpV AT, CCConst ox, CCConst oy, CCConst oz, C3 e, int g, double gfx, double gfy, double gfz)
Kokkos::View< double *, CCMem > CCField
void cutcellSmoothColor(CCField phi, CCConst b, OpV AC, OpV AW, OpV AE, OpV AS, OpV AN, OpV AB, OpV AT, C3 e, C3 og, int g, int color)
void buildPorousCoeff(CCField cx, CCField cy, CCField cz, CCConst ox, CCConst oy, CCConst oz, CCConst eps, C3 e, int g)
Kokkos::DefaultExecutionSpace CCExec
void divergOpenEps(CCConst u, CCConst v, CCConst w, CCConst ox, CCConst oy, CCConst oz, CCConst eps, CCField d, C3 e, int g)
Kokkos::View< const double *, CCMem > CCConst
void projectCorrect(CCField u, CCField v, CCField w, CCConst phi, C3 e, int g)
static constexpr double AC
Kokkos::View< double *, peclet::flow::CCMem > OpV