flow
Kokkos cut-cell IBM incompressible Navier-Stokes solver + pnm pore extraction
Loading...
Searching...
No Matches
test_sdflow_tg.cpp
Go to the documentation of this file.
1// Validation of the assembled Kokkos sdflow step against the Taylor-Green vortex (an exact solution
2// of incompressible Navier-Stokes). On a periodic staggered grid in grid units (dx=1, domain N
3// cells, wavenumber k=2pi/N):
4// STOKES (advection off): the div-free TG velocity decays at the exact discrete backward-Euler
5// rate 1/(1+dt*nu*Lambda) per step, Lambda = 4(1-cos k) (discrete -Laplacian eigenvalue, 2D
6// z-uniform mode). We check the amplitude ratio, that div(u) stays ~0 (projection), and that
7// the spatial profile is preserved (uniform decay, no mode corruption).
8// NS (advection on): TG advection is balanced by the pressure gradient, so the decay stays close.
9#include <cmath>
10#include <cstdio>
11#include <Kokkos_Core.hpp>
12#include <vector>
13
14#include "flow_reference.hpp"
15
17
18// Initialise the staggered Taylor-Green field (grid units): u at the -x face, v at the -y face.
19static void initTG(FlowReference& s, std::vector<double>& u0, std::vector<double>& v0) {
20 const int N = s.N(), G = FlowReference::G;
21 auto e = s.ext();
22 const double k = 2.0 * M_PI / N;
23 auto hu = Kokkos::create_mirror_view(s.u());
24 auto hv = Kokkos::create_mirror_view(s.v());
25 auto hw = Kokkos::create_mirror_view(s.w());
26 Kokkos::deep_copy(hu, s.u());
27 Kokkos::deep_copy(hv, s.v());
28 Kokkos::deep_copy(hw, s.w());
29 u0.assign((size_t)e.x * e.y * e.z, 0.0);
30 v0.assign(u0.size(), 0.0);
31 for (int cz = 0; cz < N; ++cz)
32 for (int cy = 0; cy < N; ++cy)
33 for (int cx = 0; cx < N; ++cx) {
34 long i = (long)(cx + G) + (long)(cy + G) * e.x + (long)(cz + G) * (long)e.x * e.y;
35 double ux = cx, uy = cy + 0.5; // -x face: x=cx, y=cy+0.5
36 double vx = cx + 0.5, vy = cy; // -y face: x=cx+0.5, y=cy
37 hu(i) = std::cos(k * ux) * std::sin(k * uy);
38 hv(i) = -std::sin(k * vx) * std::cos(k * vy);
39 hw(i) = 0.0;
40 u0[i] = hu(i);
41 v0[i] = hv(i);
42 }
43 Kokkos::deep_copy(s.u(), hu);
44 Kokkos::deep_copy(s.v(), hv);
45 Kokkos::deep_copy(s.w(), hw);
46}
47
48int main(int argc, char** argv) {
49 Kokkos::initialize(argc, argv);
50 int status = 0;
51 {
52 const int N = 24;
53 const double nu = 0.1, dt = 0.05;
54 const int nsteps = 10;
55 const double k = 2.0 * M_PI / N;
56 const double Lambda = 4.0 * (1.0 - std::cos(k)); // discrete -Laplacian eigenvalue (2D)
57 const double fStep = 1.0 / (1.0 + dt * nu * Lambda); // backward-Euler amplitude factor/step
58 const double fExpect = std::pow(fStep, nsteps);
59
60 // --- STOKES (advection off): tight check vs the discrete decay ---
61 {
62 FlowReference s(N, nu, dt);
63 s.setAdvection(false);
64 s.setIterations(/*nDiff*/ 300, /*nPois*/ 80);
65 s.setPressureMultigrid(true, 6);
66 std::vector<double> u0, v0;
67 initTG(s, u0, v0);
68 const double a0 = s.l2(s.u());
69 for (int it = 0; it < nsteps; ++it)
70 s.step();
71 const double a1 = s.l2(s.u());
72 const double ratio = a1 / a0;
73 const double divmax = s.maxDivU();
74
75 // profile preservation: u_final / ratio should match u_init
76 auto hu = Kokkos::create_mirror_view(s.u());
77 Kokkos::deep_copy(hu, s.u());
78 auto e = s.ext();
79 double num = 0, den = 0;
80 for (int cz = 0; cz < N; ++cz)
81 for (int cy = 0; cy < N; ++cy)
82 for (int cx = 0; cx < N; ++cx) {
83 long i = (long)(cx + 2) + (long)(cy + 2) * e.x + (long)(cz + 2) * (long)e.x * e.y;
84 double pred = u0[i] * ratio;
85 num += (hu(i) - pred) * (hu(i) - pred);
86 den += pred * pred;
87 }
88 double profErr = std::sqrt(num / den);
89
90 const double rateErr = std::fabs(ratio - fExpect) / fExpect;
91 std::printf(
92 "[sdflow_tg] STOKES: ratio=%.6f expected=%.6f (rel err %.2e); max|div|=%.2e; profile "
93 "err=%.2e\n",
94 ratio, fExpect, rateErr, divmax, profErr);
95 if (rateErr > 5e-3) {
96 std::fprintf(stderr, "FAIL: decay rate off\n");
97 status = 1;
98 }
99 if (divmax > 1e-9) {
100 std::fprintf(stderr, "FAIL: divergence not zero\n");
101 status = 1;
102 }
103 if (profErr > 5e-3) {
104 std::fprintf(stderr, "FAIL: profile not preserved\n");
105 status = 1;
106 }
107 }
108
109 // --- NS (advection on): decay should stay close to the same diffusion rate ---
110 // Post-projection divergence = the pressure-Poisson residual. Compare the wired multigrid
111 // pressure solve (a few V-cycles) against the old plain RB-GS (80 sweeps): MG should drive div
112 // far lower for much less work.
113 {
114 FlowReference sMg(N, nu, dt), sRb(N, nu, dt);
115 sMg.setAdvection(true);
116 sMg.setPressureMultigrid(true, 6);
117 sRb.setAdvection(true);
118 sRb.setPressureMultigrid(false, 0);
119 sRb.setIterations(300, 80);
120 std::vector<double> u0, v0;
121 initTG(sMg, u0, v0);
122 initTG(sRb, u0, v0);
123 const double a0 = sMg.l2(sMg.u());
124 for (int it = 0; it < nsteps; ++it) {
125 sMg.step();
126 sRb.step();
127 }
128 const double ratio = sMg.l2(sMg.u()) / a0;
129 const double divMg = sMg.maxDivU(), divRb = sRb.maxDivU();
130 const double rateErr = std::fabs(ratio - fExpect) / fExpect;
131 std::printf(
132 "[sdflow_tg] NS: ratio=%.6f expected=%.6f (rel err %.2e); max|div| MG(6 V-cyc)=%.2e "
133 "vs RB-GS(80 sweeps)=%.2e\n",
134 ratio, fExpect, rateErr, divMg, divRb);
135 if (rateErr > 5e-2) {
136 std::fprintf(stderr, "FAIL: NS decay too far from TG\n");
137 status = 1;
138 }
139 if (divMg > 1e-7) {
140 std::fprintf(stderr, "FAIL: MG divergence too large\n");
141 status = 1;
142 }
143 if (!(divMg < divRb)) {
144 std::fprintf(stderr, "FAIL: MG did not beat RB-GS\n");
145 status = 1;
146 }
147 }
148
149 if (!status)
150 std::printf("[sdflow_tg] PASS: assembled Kokkos NS step reproduces Taylor-Green (exec: %s)\n",
151 Kokkos::DefaultExecutionSpace::name());
152 }
153 Kokkos::finalize();
154 return status;
155}
void setPressureMultigrid(bool useMg, int nVcycles)
void setIterations(int nDiff, int nPois)
flow — assembled single-GPU periodic Navier-Stokes step on Kokkos fields.
static constexpr int G
static constexpr int N
int main(int argc, char **argv)
static void initTG(FlowReference &s, std::vector< double > &u0, std::vector< double > &v0)