flow 1.3.0
Kokkos cut-cell IBM incompressible Navier-Stokes solver + pnm pore extraction
Loading...
Searching...
No Matches
test_vof_timing.cpp
Go to the documentation of this file.
1// WO-V9 — the VoF profiling instrument is INERT.
2//
3// `set_vof_timing(True)` puts a `Kokkos::fence()` at every VoF stage boundary (the same rule
4// `IbmSolver::phaseTick` already applies to the step's three coarse phases: on a device backend a
5// phase boundary that does not fence bills queued work to whichever phase next reads the clock).
6// A fence changes WHEN work happens and never WHAT it computes — but "never" is a claim, and this
7// is the gate on it. `useWorklist` is the same kind of claim about a re-ordering: the compacted
8// reconstruction pass writes planes for exactly the cells the dense pass writes them for, and the
9// flux never reads a non-mixed cell's plane, so the two must agree BIT FOR BIT.
10//
11// T1 timers OFF vs ON, surface tension + momentum consistency, 30 steps: C, u, v, w, P bitwise.
12// T2 the same, through the CUT-CELL path (a sphere array, `set_solid(..., cutcell_pressure)`):
13// bitwise. The V5a kernels are a different branch and carry the clip/census timers.
14// T3 `set_vof_worklist(False)` vs the default, both scenes: bitwise (the compaction claim).
15// T4 the timers actually MEASURE: with them armed the stage sum is > 0, the kernel breakdown
16// sums to no more than the colour stage, and `steps` counts the armed steps.
17#include <cmath>
18#include <cstdio>
19#include <Kokkos_Core.hpp>
20#include <memory>
21#include <vector>
22
23#include "flow_ibm.hpp"
24
25namespace {
28
29int failures = 0;
30#define CHECK(cond) \
31 do { \
32 if (!(cond)) { \
33 std::fprintf(stderr, "CHECK failed: %s\n at %s:%d\n", #cond, __FILE__, __LINE__); \
34 ++failures; \
35 } \
36 } while (0)
37
38// A NaN-propagating max|a-b| (the WO-R2 sweep: std::fmax(m, NaN) == m would hide a dead field).
39double maxAbsDiff(const std::vector<double>& a, const std::vector<double>& b) {
40 if (a.size() != b.size())
41 return 1e300;
42 double m = 0.0;
43 for (std::size_t i = 0; i < a.size(); ++i) {
44 const double d = std::abs(a[i] - b[i]);
45 if (!(d <= m)) // false for NaN, which therefore becomes the answer
46 m = d;
47 }
48 return m;
49}
50
51struct State {
52 std::vector<double> C, u, v, w, P;
53};
54
55State harvest(IbmSolver& s) {
56 State st;
57 st.C = s.getField("C");
58 st.u = s.getVelocity(0);
59 st.v = s.getVelocity(1);
60 st.w = s.getVelocity(2);
61 st.P = s.getPressure();
62 return st;
63}
64
65double diff(const State& a, const State& b) {
66 double m = maxAbsDiff(a.C, b.C);
67 m = std::fmax(m, maxAbsDiff(a.u, b.u));
68 m = std::fmax(m, maxAbsDiff(a.v, b.v));
69 m = std::fmax(m, maxAbsDiff(a.w, b.w));
70 m = std::fmax(m, maxAbsDiff(a.P, b.P));
71 return m;
72}
73
74// A rising drop in a periodic box: surface tension + variable density + momentum consistency, i.e.
75// every VoF stage the instrument touches (advection, curvature, CSF, the bridges).
76std::unique_ptr<IbmSolver> makeScene(int n, bool solid, bool timing, bool worklist) {
77 auto s = std::make_unique<IbmSolver>(n, n, n);
78 s->setRho(10.0);
79 s->setMu(0.05);
80 const double R = 0.3 * n;
81 std::vector<double> sdf((std::size_t)n * n * n, 10.0);
82 if (solid) { // a 2x2x2 sphere array the interface has to move through
83 const double Rs = 0.16 * n;
84 for (int k = 0; k < n; ++k)
85 for (int j = 0; j < n; ++j)
86 for (int i = 0; i < n; ++i) {
87 double best = 1e30;
88 for (int a = 0; a < 2; ++a)
89 for (int b = 0; b < 2; ++b)
90 for (int c = 0; c < 2; ++c) {
91 const double cx = (0.25 + 0.5 * a) * n, cy = (0.25 + 0.5 * b) * n,
92 cz = (0.25 + 0.5 * c) * n;
93 const double dx = i + 0.5 - cx, dy = j + 0.5 - cy, dz = k + 0.5 - cz;
94 best = std::fmin(best, std::sqrt(dx * dx + dy * dy + dz * dz) - Rs);
95 }
96 sdf[(std::size_t)i + (std::size_t)j * n + (std::size_t)k * n * n] = best;
97 }
98 s->setSolid(sdf, true);
99 } else {
100 s->setPressureGeometry(sdf);
101 }
102 s->enableVof();
103 s->setVofWorklist(worklist);
104 s->setVofCurvatureWorklist(worklist);
105 std::vector<double> C((std::size_t)n * n * n, 0.0);
106 for (int k = 0; k < n; ++k)
107 for (int j = 0; j < n; ++j)
108 for (int i = 0; i < n; ++i) {
109 const double dx = i + 0.5 - 0.5 * n - 0.13, dy = j + 0.5 - 0.5 * n - 0.27,
110 dz = k + 0.5 - 0.5 * n - 0.11;
111 // a smooth (but not symmetric) blob: fractions between 0 and 1 in a real band
112 const double r = std::sqrt(dx * dx + dy * dy + dz * dz);
113 C[(std::size_t)i + (std::size_t)j * n + (std::size_t)k * n * n] =
114 std::fmin(1.0, std::fmax(0.0, 0.5 - (r - R)));
115 }
116 s->setField("C", C);
117 s->setPropertyModel("rho", ClosureKind::LinearMix, "C", "", {1.0, 9.0});
118 s->setPropertyModel("mu", ClosureKind::LinearMix, "C", "", {0.005, 0.045});
119 s->setSurfaceTension(2.0);
120 s->enableVofMomentum(1.0, 10.0);
121 s->setPropertyModel("force_z", ClosureKind::LinearMix, "C", "", {-0.01, -0.09});
122 s->setPressureChebyshev(true, 300, 1e-12);
123 s->setDt(0.4 * s->capillaryDt());
124 if (timing)
125 s->setVofTiming(true);
126 return s;
127}
128
129State run(int n, bool solid, bool timing, bool worklist, int steps) {
130 auto s = makeScene(n, solid, timing, worklist);
131 for (int i = 0; i < steps; ++i)
132 s->step();
133 return harvest(*s);
134}
135} // namespace
136
137int main(int argc, char** argv) {
138 Kokkos::initialize(argc, argv);
139 {
140 const int n = 24, steps = 30;
141 for (int solid = 0; solid < 2; ++solid) {
142 const char* tag = solid ? "cut-cell" : "all-fluid";
143 const State off = run(n, solid, false, true, steps);
144 const State on = run(n, solid, true, true, steps);
145 const double d1 = diff(off, on);
146 std::printf("T1/T2 %-9s timers OFF vs ON max|d| = %.3e\n", tag, d1);
147 CHECK(d1 == 0.0);
148 const State nowl = run(n, solid, false, false, steps);
149 const double d2 = diff(off, nowl);
150 std::printf("T3 %-9s worklists ON vs OFF max|d| = %.3e\n", tag, d2);
151 CHECK(d2 == 0.0);
152 }
153 // T4: the instrument reports something, and the parts do not exceed the whole.
154 auto s = makeScene(n, false, true, true);
155 for (int i = 0; i < 10; ++i)
156 s->step();
157 const auto& v = s->vofTimingReport();
158 const auto& k = s->vofKernelTiming();
159 const auto& q = s->vofCurvatureTiming();
160 std::printf(
161 "T4 steps %ld momAdvect %.4f s curvature %.4f s csf %.4f s "
162 "[recon %.4f fluxes %.4f sweep %.4f exch %.4f]\n",
163 v.steps, v.momAdvect, v.curvature, v.csf, k.reconstruct, k.fluxes, k.sweep, k.exchange);
164 CHECK(v.steps == 10);
165 CHECK(v.momAdvect > 0.0);
166 CHECK(v.curvature > 0.0);
167 CHECK(v.csf > 0.0);
168 CHECK(k.sweeps == 30); // 3 sweeps per step
169 CHECK(k.reconstruct > 0.0 && k.fluxes > 0.0 && k.sweep > 0.0);
170 // the kernel breakdown is a part of the momentum-consistent colour stage
171 CHECK(k.reconstruct + k.fluxes + k.sweep + k.clip + k.exchange + k.freeze <=
172 v.momAdvect * 1.02 + 1e-6);
173 std::printf(
174 "T4 curvature passes: compact %.4f planes %.4f height %.4f fallback %.4f "
175 "census %.4f (%ld calls)\n",
176 q.compact, q.planes, q.height, q.fallback, q.census, q.calls);
177 CHECK(q.calls == 10);
178 CHECK(q.planes > 0.0 && q.height > 0.0);
179 CHECK(q.compact + q.planes + q.height + q.fallback + q.census <= v.curvature * 1.02 + 1e-6);
180 s->setVofTiming(false);
181 const auto& z = s->vofTimingReport();
182 CHECK(z.steps == 0 && z.momAdvect == 0.0);
183 }
184 Kokkos::finalize();
185 std::printf(failures ? "FAILED (%d)\n" : "OK (%d failures)\n", failures);
186 return failures ? 1 : 0;
187}
int main()
Definition dbg_scene.cpp:13
flow — host-facing Kokkos IBM Navier-Stokes solver (drop-in flow-style API).
static double maxAbsDiff(const std::vector< double > &a, const std::vector< double > &b)
static int run(int bc)
#define CHECK(cond)