flow
Kokkos cut-cell IBM incompressible Navier-Stokes solver + pnm pore extraction
Loading...
Searching...
No Matches
mac_bc.hpp
Go to the documentation of this file.
1
10#ifndef PECLET_FLOW_MAC_BC_HPP
11#define PECLET_FLOW_MAC_BC_HPP
12
13#include <Kokkos_Core.hpp>
14
15namespace peclet::flow {
16
17using BExec = Kokkos::DefaultExecutionSpace;
18using BMem = BExec::memory_space;
19using BField = Kokkos::View<double*, BMem>;
20
21struct B3 {
22 int x, y, z;
23};
24
25namespace bcdetail {
26KOKKOS_INLINE_FUNCTION void axisDims(B3 ext, int (&dims)[3], long (&strides)[3]) {
27 dims[0] = ext.x;
28 dims[1] = ext.y;
29 dims[2] = ext.z;
30 strides[0] = 1;
31 strides[1] = ext.x;
32 strides[2] = static_cast<long>(ext.x) * ext.y;
33}
34} // namespace bcdetail
35
36// Fill component comp (0=u,1=v,2=w) ghosts for one domain face (axis a, side s=0 low/1 high) with a
37// scalar wall velocity. fold=1 drops the tangential wall face (ghost=0, implicit diffusion).
38inline void bcVelocityComp(BField f, B3 ext, int g, int a, int s, int comp, double wall, int fold,
39 BField prof = BField(), int prof_nc = 0) {
40 BExec space;
41 int dims[3];
42 long strides[3];
43 bcdetail::axisDims(ext, dims, strides);
44 const int b = (a + 1) % 3, c = (a + 2) % 3;
45 const long sa = strides[a], sb = strides[b], sc = strides[c];
46 const int na = dims[a];
47 const int bf = (s == 0) ? g : (na - g);
48 const bool hasProf =
49 prof.extent(0) > 0; // per-position inlet profile (resampled to the face grid)
50 using MD = Kokkos::MDRangePolicy<BExec, Kokkos::Rank<2>>;
51 Kokkos::parallel_for(
52 "peclet::flow::bc_vel", MD(space, {0, 0}, {dims[b], dims[c]}), KOKKOS_LAMBDA(int p0, int p1) {
53 const long base = static_cast<long>(p0) * sb + static_cast<long>(p1) * sc;
54 const double wc = hasProf ? prof((static_cast<long>(p0) * prof_nc + p1) * 3 + comp) : wall;
55 auto at = [&](int ia) -> double& { return f(base + static_cast<long>(ia) * sa); };
56 if (comp == a) { // normal: Dirichlet face + odd reflection
57 at(bf) = wc;
58 if (s == 0)
59 for (int ia = 0; ia < g; ++ia)
60 at(ia) = 2.0 * wc - at(2 * bf - ia);
61 else
62 for (int ia = na - g + 1; ia < na; ++ia)
63 at(ia) = 2.0 * wc - at(2 * bf - ia);
64 } else if (fold) { // tangential implicit: drop wall face
65 if (s == 0)
66 for (int ia = 0; ia < g; ++ia)
67 at(ia) = 0.0;
68 else
69 for (int ia = na - g; ia < na; ++ia)
70 at(ia) = 0.0;
71 } else { // tangential explicit: cell-centred reflection about bf-0.5
72 if (s == 0)
73 for (int ia = 0; ia < g; ++ia)
74 at(ia) = 2.0 * wc - at(2 * bf - 1 - ia);
75 else
76 for (int ia = na - g; ia < na; ++ia)
77 at(ia) = 2.0 * wc - at(2 * bf - 1 - ia);
78 }
79 });
80}
81
82// Collocated (cell-centered) velocity Dirichlet / no-slip ghost on one domain face. The wall sits
83// at the boundary FACE (between the last inner cell and the first ghost), so EVERY component is
84// reflected about it
85// -- ghost = 2*wc - mirror(interior) makes the face-interpolated value equal `wc`. No
86// normal/tangential split and no implicit fold: explicit reflection, re-imposed each smoother sweep
87// (converges to no-slip). `wc` is the scalar `wall`, or a per-position value from `prof` (the
88// resampled inlet profile, indexed (p0*prof_nc+p1)*3+comp over the face's perpendicular plane) when
89// one is supplied (e.g. the BFS step inlet).
90inline void bcVelocityColocated(BField f, B3 ext, int g, int a, int s, double wall, int comp = 0,
91 BField prof = BField(), int prof_nc = 0) {
92 BExec space;
93 int dims[3];
94 long strides[3];
95 bcdetail::axisDims(ext, dims, strides);
96 const int b = (a + 1) % 3, c = (a + 2) % 3;
97 const long sa = strides[a], sb = strides[b], sc = strides[c];
98 const int na = dims[a];
99 const bool hasProf = prof.extent(0) > 0;
100 using MD = Kokkos::MDRangePolicy<BExec, Kokkos::Rank<2>>;
101 Kokkos::parallel_for(
102 "peclet::flow::bc_vel_coloc", MD(space, {0, 0}, {dims[b], dims[c]}),
103 KOKKOS_LAMBDA(int p0, int p1) {
104 const long base = static_cast<long>(p0) * sb + static_cast<long>(p1) * sc;
105 const double wc = hasProf ? prof((static_cast<long>(p0) * prof_nc + p1) * 3 + comp) : wall;
106 auto at = [&](int ia) -> double& { return f(base + static_cast<long>(ia) * sa); };
107 if (s == 0)
108 for (int ia = 0; ia < g; ++ia)
109 at(ia) = 2.0 * wc - at(2 * g - 1 - ia); // mirror about g-1/2
110 else
111 for (int ia = na - g; ia < na; ++ia)
112 at(ia) = 2.0 * wc - at(2 * (na - g) - 1 - ia); // about na-g-1/2
113 });
114}
115
116// Zero-gradient (Neumann) ghost on one domain face: every ghost layer = the boundary-adjacent inner
117// cell (cell-centered). Used for the collocated pressure increment phi at walls so the
118// cell-centered correction carries no spurious normal acceleration (the same role pressureBcGhost
119// plays for P in the predictor).
120inline void bcNeumannGhost(BField f, B3 ext, int g, int a, int s) {
121 BExec space;
122 int dims[3];
123 long strides[3];
124 bcdetail::axisDims(ext, dims, strides);
125 const int b = (a + 1) % 3, c = (a + 2) % 3;
126 const long sa = strides[a], sb = strides[b], sc = strides[c];
127 const int na = dims[a];
128 const int bic = (s == 0) ? g : (na - g - 1);
129 const int lo = (s == 0) ? 0 : (na - g), hi = (s == 0) ? (g - 1) : (na - 1);
130 using MD = Kokkos::MDRangePolicy<BExec, Kokkos::Rank<2>>;
131 Kokkos::parallel_for(
132 "peclet::flow::bc_neumann_ghost", MD(space, {0, 0}, {dims[b], dims[c]}),
133 KOKKOS_LAMBDA(int p0, int p1) {
134 const long base = static_cast<long>(p0) * sb + static_cast<long>(p1) * sc;
135 const double pin = f(base + static_cast<long>(bic) * sa);
136 for (int ia = lo; ia <= hi; ++ia)
137 f(base + static_cast<long>(ia) * sa) = pin;
138 });
139}
140
141// Zero-gradient (Neumann) outflow velocity ghost for component comp on one face.
142inline void bcOutflowComp(BField f, B3 ext, int g, int a, int s, int comp, int fold) {
143 BExec space;
144 int dims[3];
145 long strides[3];
146 bcdetail::axisDims(ext, dims, strides);
147 const int b = (a + 1) % 3, c = (a + 2) % 3;
148 const long sa = strides[a], sb = strides[b], sc = strides[c];
149 const int na = dims[a];
150 using MD = Kokkos::MDRangePolicy<BExec, Kokkos::Rank<2>>;
151 Kokkos::parallel_for(
152 "peclet::flow::bc_outflow", MD(space, {0, 0}, {dims[b], dims[c]}),
153 KOKKOS_LAMBDA(int p0, int p1) {
154 const long base = static_cast<long>(p0) * sb + static_cast<long>(p1) * sc;
155 auto at = [&](int ia) -> double& { return f(base + static_cast<long>(ia) * sa); };
156 if (s == 0) {
157 const int src = (comp == a) ? g + 1 : g;
158 const int last = (comp == a) ? g : g - 1;
159 const double v = fold ? 0.0 : at(src);
160 for (int ia = 0; ia <= last; ++ia)
161 at(ia) = v;
162 } else {
163 const int src = na - g - 1;
164 const double v = fold ? 0.0 : at(src);
165 for (int ia = na - g; ia < na; ++ia)
166 at(ia) = v;
167 }
168 });
169}
170
171// Implicit-diffusion face-fold accumulation at the boundary-adjacent inner cell.
172inline void bcDiffusionFold(BField dcorr, BField brhs, B3 ext, int g, int a, int s, double dval,
173 double bval) {
174 BExec space;
175 int dims[3];
176 long strides[3];
177 bcdetail::axisDims(ext, dims, strides);
178 const int b = (a + 1) % 3, c = (a + 2) % 3;
179 const long sa = strides[a], sb = strides[b], sc = strides[c];
180 const int bic = (s == 0) ? g : (dims[a] - g - 1);
181 using MD = Kokkos::MDRangePolicy<BExec, Kokkos::Rank<2>>;
182 Kokkos::parallel_for(
183 "peclet::flow::bc_fold", MD(space, {0, 0}, {dims[b], dims[c]}),
184 KOKKOS_LAMBDA(int p0, int p1) {
185 const long i =
186 static_cast<long>(p0) * sb + static_cast<long>(p1) * sc + static_cast<long>(bic) * sa;
187 dcorr(i) += dval;
188 brhs(i) += bval;
189 });
190}
191
192// Hold the pressure ghost at 0 on an outflow face (Dirichlet p=0; the open face couples to it).
193inline void bcZeroPressureGhost(BField phi, B3 ext, int g, int a, int s) {
194 BExec space;
195 int dims[3];
196 long strides[3];
197 bcdetail::axisDims(ext, dims, strides);
198 const int b = (a + 1) % 3, c = (a + 2) % 3;
199 const long sa = strides[a], sb = strides[b], sc = strides[c];
200 const int na = dims[a];
201 const int lo = (s == 0) ? 0 : (na - g), hi = (s == 0) ? (g - 1) : (na - 1);
202 using MD = Kokkos::MDRangePolicy<BExec, Kokkos::Rank<2>>;
203 Kokkos::parallel_for(
204 "peclet::flow::bc_zero_p_ghost", MD(space, {0, 0}, {dims[b], dims[c]}),
205 KOKKOS_LAMBDA(int p0, int p1) {
206 const long base = (long)p0 * sb + (long)p1 * sc;
207 for (int ia = lo; ia <= hi; ++ia)
208 phi(base + (long)ia * sa) = 0.0;
209 });
210}
211
212// Projection correction of the high-side outflow normal face (index na-g) that correct_k misses:
213// f -= phi[bf] - phi[bf-sa] (with the Dirichlet ghost phi[bf]=0 -> += phi_inner).
214// (correct_outflow_k.)
215inline void bcCorrectOutflow(BField f, BField phi, B3 ext, int g, int a) {
216 BExec space;
217 int dims[3];
218 long strides[3];
219 bcdetail::axisDims(ext, dims, strides);
220 const int b = (a + 1) % 3, c = (a + 2) % 3;
221 const long sa = strides[a], sb = strides[b], sc = strides[c];
222 using MD = Kokkos::MDRangePolicy<BExec, Kokkos::Rank<2>>;
223 Kokkos::parallel_for(
224 "peclet::flow::bc_correct_outflow", MD(space, {0, 0}, {dims[b], dims[c]}),
225 KOKKOS_LAMBDA(int p0, int p1) {
226 const long bf = (long)p0 * sb + (long)p1 * sc + (long)(dims[a] - g) * sa;
227 f(bf) -= phi(bf) - phi(bf - sa);
228 });
229}
230
231// Set the a-component face openness on a domain face to `val` (Neumann wall/inflow -> 0; the
232// periodic fill would otherwise wrap the wrong value into an outflow face from the opposite
233// boundary -> set it open = 1).
234inline void bcSetOpenness(BField oa, B3 ext, int g, int a, int s, double val) {
235 BExec space;
236 int dims[3];
237 long strides[3];
238 bcdetail::axisDims(ext, dims, strides);
239 const int b = (a + 1) % 3, c = (a + 2) % 3;
240 const long sa = strides[a], sb = strides[b], sc = strides[c];
241 const int bf = (s == 0) ? g : (dims[a] - g);
242 using MD = Kokkos::MDRangePolicy<BExec, Kokkos::Rank<2>>;
243 Kokkos::parallel_for(
244 "peclet::flow::bc_setopen", MD(space, {0, 0}, {dims[b], dims[c]}),
245 KOKKOS_LAMBDA(int p0, int p1) {
246 oa(static_cast<long>(p0) * sb + static_cast<long>(p1) * sc + static_cast<long>(bf) * sa) =
247 val;
248 });
249}
250inline void bcZeroOpenness(BField oa, B3 ext, int g, int a, int s) {
251 bcSetOpenness(oa, ext, g, a, s, 0.0);
252}
253
254} // namespace peclet::flow
255
256#endif // PECLET_FLOW_MAC_BC_HPP
void axisDims(B3 ext, int(&dims)[3], long(&strides)[3])
Definition mac_bc.hpp:26
void bcSetOpenness(BField oa, B3 ext, int g, int a, int s, double val)
Definition mac_bc.hpp:234
void bcZeroPressureGhost(BField phi, B3 ext, int g, int a, int s)
Definition mac_bc.hpp:193
void bcZeroOpenness(BField oa, B3 ext, int g, int a, int s)
Definition mac_bc.hpp:250
void bcDiffusionFold(BField dcorr, BField brhs, B3 ext, int g, int a, int s, double dval, double bval)
Definition mac_bc.hpp:172
void bcVelocityColocated(BField f, B3 ext, int g, int a, int s, double wall, int comp=0, BField prof=BField(), int prof_nc=0)
Definition mac_bc.hpp:90
void bcCorrectOutflow(BField f, BField phi, B3 ext, int g, int a)
Definition mac_bc.hpp:215
void bcVelocityComp(BField f, B3 ext, int g, int a, int s, int comp, double wall, int fold, BField prof=BField(), int prof_nc=0)
Definition mac_bc.hpp:38
BExec::memory_space BMem
Definition mac_bc.hpp:18
Kokkos::View< double *, BMem > BField
Definition mac_bc.hpp:19
void bcOutflowComp(BField f, B3 ext, int g, int a, int s, int comp, int fold)
Definition mac_bc.hpp:142
Kokkos::DefaultExecutionSpace BExec
Definition mac_bc.hpp:17
void bcNeumannGhost(BField f, B3 ext, int g, int a, int s)
Definition mac_bc.hpp:120