flow
Kokkos cut-cell IBM incompressible Navier-Stokes solver + pnm pore extraction
Loading...
Searching...
No Matches
test_poiseuille_ibm.cpp
Go to the documentation of this file.
1// Validation of the assembled IBM velocity solve (the verify_poiseuille_sdflow physics): plane
2// Poiseuille through an SDF-defined channel with cut-cell IBM no-slip walls, body-force driven,
3// physical units (rho/mu/dt). Solve the backward-Euler x-momentum to steady with the IBM-modified
4// stencil and compare to U_max = F*H^2/(8*mu). Exercises buildIbmOverlay + ibmBuildDiffusion +
5// ibmModifyStencil + ibmSolidMask + ibmRbgsSweep assembled together. Runs on any Kokkos backend.
6#include <cmath>
7#include <cstdio>
8#include <Kokkos_Core.hpp>
9#include <vector>
10
11#include "mac_ibm.hpp"
12
13using namespace peclet::flow;
14
15int main(int argc, char** argv) {
16 Kokkos::initialize(argc, argv);
17 int status = 0;
18 {
19 const int g = 1;
20 const int nx = 4, nz = 4, ny = 48; // periodic x (flow) & z; channel along y
21 // Half-integer wall positions so the SDF zero lies BETWEEN cells -> fluid cells with solid
22 // neighbours (cut cells) form, exercising the Robust-Scaled cut-cell no-slip (not just
23 // masking).
24 const double ylo = 6.5, yhi = ny - 6.5; // immersed walls; H = yhi - ylo
25 const double H = yhi - ylo;
26 const double rho = 1.0, mu = 0.1, dt = 50.0, F = 0.01;
27 const double idiag = rho / dt, beta = mu;
28 C3 e{nx + 2 * g, ny + 2 * g, nz + 2 * g};
29 const std::size_t n = (std::size_t)e.x * e.y * e.z;
30 const Off3 offU{-0.5f, 0.0f, 0.0f}; // u stored at the -x face
31
32 // channel SDF: sdf(iy) = min(iy - ylo, yhi - iy) (>0 in fluid, <0 in the wall) at each ext
33 // cell.
34 CCField sdf("sdf", n);
35 {
36 auto h = Kokkos::create_mirror_view(sdf);
37 for (int z = 0; z < e.z; ++z)
38 for (int y = 0; y < e.y; ++y)
39 for (int x = 0; x < e.x; ++x) {
40 int iy = y - g;
41 double s = std::min((double)iy - ylo, yhi - (double)iy);
42 h((long)x + (long)y * e.x + (long)z * (long)e.x * e.y) = s;
43 }
44 Kokkos::deep_copy(sdf, h);
45 }
46
47 // build the u-component IBM overlay
48 const int maxCut = (int)((long)nx * ny * nz);
49 IbmOverlay ov{Kokkos::View<int*, CCMem>("ci", maxCut),
50 Kokkos::View<int*, CCMem>("nb", maxCut),
51 Kokkos::View<float*, CCMem>("dr", maxCut),
52 Kokkos::View<int*, CCMem>("dc", (std::size_t)maxCut * 6),
53 Kokkos::View<float*, CCMem>("K", (std::size_t)maxCut * 6),
54 Kokkos::View<float*, CCMem>("M", (std::size_t)maxCut * 6),
55 Kokkos::View<float*, CCMem>("X", (std::size_t)maxCut * 6),
56 Kokkos::View<float*, CCMem>("Nbc", (std::size_t)maxCut * 6),
57 Kokkos::View<float*, CCMem>("R", (std::size_t)maxCut * 6)};
58 Kokkos::View<int*, CCMem> idMap("idMap", n);
59 Kokkos::View<int, CCMem> counter("counter");
60 int nCut =
61 buildIbmOverlay<1>(CCConst(sdf), e, g, offU, /*bc_type Dirichlet*/ 0, ov, idMap, counter);
62
63 // base diffusion stencil + apply overlay
64 using FV = Kokkos::View<float*, CCMem>;
65 FV AC("AC", n), AW("AW", n), AE("AE", n), AS("AS", n), AN("AN", n), AB("AB", n), AT("AT", n);
66 Kokkos::View<double*, CCMem> inhom("inhom", n), rscale("rscale", n);
67 Kokkos::deep_copy(rscale, 1.0); // non-cut cells: scale 1
68 ibmBuildDiffusion(AC, AW, AE, AS, AN, AB, AT, e.x, e.y, e.z, beta, idiag);
69 ibmModifyStencil(AC, AW, AE, AS, AN, AB, AT, inhom, rscale, ov, nCut, /*u_bc*/ 0.0f);
70
71 // solid mask for u
72 CCField mask("mask", n);
73 ibmSolidMask(mask, CCConst(sdf), e, offU);
74
75 CCField u("u", n), b("b", n);
76 Kokkos::deep_copy(u, 0.0);
77 CCExec space;
78
79 // periodic x,z ghost fill (y handled by the immersed walls / mask)
80 auto fillXZ = [&]() {
81 CCField uu = u;
82 const int Nx = nx, Nz = nz;
83 Kokkos::parallel_for(
84 "fx", Kokkos::MDRangePolicy<CCExec, Kokkos::Rank<2>>(space, {0, 0}, {e.y, e.z}),
85 KOKKOS_LAMBDA(int y, int z) {
86 long base = (long)y * e.x + (long)z * (long)e.x * e.y;
87 for (int gl = 0; gl < g; ++gl) {
88 uu(base + gl) = uu(base + gl + Nx);
89 uu(base + g + Nx + gl) = uu(base + g + gl);
90 }
91 });
92 Kokkos::parallel_for(
93 "fz", Kokkos::MDRangePolicy<CCExec, Kokkos::Rank<2>>(space, {0, 0}, {e.x, e.y}),
94 KOKKOS_LAMBDA(int x, int y) {
95 long base = (long)x + (long)y * e.x;
96 long sz = (long)e.x * e.y;
97 for (int gl = 0; gl < g; ++gl) {
98 uu(base + (long)gl * sz) = uu(base + (long)(gl + Nz) * sz);
99 uu(base + (long)(g + Nz + gl) * sz) = uu(base + (long)(g + gl) * sz);
100 }
101 });
102 space.fence();
103 };
104
105 // time-step the IBM momentum solve to steady state (slow approach: ~mu*k^2*dt per step)
106 for (int step = 0; step < 600; ++step) {
107 // RHS b' = rscale * (idiag*u + F) (Robust-Scaled; inhom=0 for no-slip walls)
108 {
109 CCField uu = u, bb = b;
110 auto rs = rscale;
111 double id = idiag, ff = F;
112 Kokkos::parallel_for(
113 "rhs", Kokkos::RangePolicy<CCExec>(space, 0, n),
114 KOKKOS_LAMBDA(std::size_t i) { bb(i) = rs(i) * (id * uu(i) + ff); });
115 space.fence();
116 }
117 for (int it = 0; it < 200; ++it) {
118 fillXZ();
119 ibmRbgsStencilColor(u, CCConst(b), MConst(AC), MConst(AW), MConst(AE), MConst(AS),
120 MConst(AN), MConst(AB), MConst(AT), CCConst(mask), e, C3{0, 0, 0}, g,
121 0);
122 fillXZ();
123 ibmRbgsStencilColor(u, CCConst(b), MConst(AC), MConst(AW), MConst(AE), MConst(AS),
124 MConst(AN), MConst(AB), MConst(AT), CCConst(mask), e, C3{0, 0, 0}, g,
125 1);
126 }
127 }
128
129 // extract centerline profile u(y) at x=g,z=g and compare to the parabola
130 auto hu = Kokkos::create_mirror_view(u);
131 Kokkos::deep_copy(hu, u);
132 const double Uana = F * H * H / (8.0 * mu);
133 double umax = 0, l2num = 0, l2den = 0;
134 for (int iy = 0; iy < ny; ++iy) {
135 long i = (long)g + (long)(iy + g) * e.x + (long)g * (long)e.x * e.y;
136 double un = hu(i);
137 umax = std::fmax(umax, un);
138 if (iy > ylo && iy < yhi) { // fluid interior
139 double yy = iy;
140 double ue = (F / (2.0 * mu)) * (yy - ylo) * (yhi - yy);
141 l2num += (un - ue) * (un - ue);
142 l2den += ue * ue;
143 }
144 }
145 const double l2err = std::sqrt(l2num / l2den);
146 const double umaxErr = std::fabs(umax - Uana) / Uana;
147 std::printf(
148 "[poiseuille_ibm] cut cells=%d; U_max=%.5f analytic=%.5f (err %.2e); profile L2 err=%.2e\n",
149 nCut, umax, Uana, umaxErr, l2err);
150 if (nCut <= 0) {
151 std::fprintf(stderr, "FAIL: no cut cells found\n");
152 status = 1;
153 }
154 if (umaxErr > 3e-2) {
155 std::fprintf(stderr, "FAIL: U_max off analytic\n");
156 status = 1;
157 }
158 if (l2err > 3e-2) {
159 std::fprintf(stderr, "FAIL: profile not parabolic\n");
160 status = 1;
161 }
162 if (!status)
163 std::printf("[poiseuille_ibm] PASS: IBM cut-cell channel reproduces Poiseuille (exec %s)\n",
164 Kokkos::DefaultExecutionSpace::name());
165 }
166 Kokkos::finalize();
167 return status;
168}
flow — portable (Kokkos) IBM geometric fields + variable-coefficient RB-GS smoother.
void ibmSolidMask(CCField mask, CCConst sdf, C3 ext, Off3 off)
Definition mac_ibm.hpp:87
void ibmRbgsStencilColor(CCField x, CCConst b, MConst AC, MConst AW, MConst AE, MConst AS, MConst AN, MConst AB, MConst AT, CCConst solidmask, C3 ext, C3 og, int g, int color)
Definition mac_ibm.hpp:120
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)
Kokkos::View< double *, CCMem > CCField
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)
Kokkos::DefaultExecutionSpace CCExec
Kokkos::View< const float *, CCMem > MConst
Kokkos::View< const double *, CCMem > CCConst
static constexpr double AC
static constexpr double F
Kokkos::View< float *, IMem > FV
int main(int argc, char **argv)