flow 1.3.0
Kokkos cut-cell IBM incompressible Navier-Stokes solver + pnm pore extraction
Loading...
Searching...
No Matches
marching_cubes_field.hpp
Go to the documentation of this file.
1
21#ifndef PECLET_FLOW_VOF_MARCHING_CUBES_FIELD_HPP
22#define PECLET_FLOW_VOF_MARCHING_CUBES_FIELD_HPP
23
24#include <Kokkos_Core.hpp>
25#include <stdexcept>
26
27#include "mac_stencils.hpp"
28#include "policy.hpp"
29#include "vof/advect_wy.hpp"
30#include "vof/curvature_field.hpp" // vofIsInterface
31#include "vof/interface_area.hpp" // InterfaceAreaMode
33#include "vof/phase_change.hpp" // pcCentreDistance, pcUnitNormal
34#include "vof/plic.hpp"
35
36namespace peclet::flow::vof {
37
40class VofMcArea {
41 public:
42 struct Stats {
43 long cells = 0;
44 long orphanCells = 0;
45 double area = 0.0;
46 double orphanArea = 0.0;
47 };
48
49 void init(int nx, int ny, int nz, int ghost) {
50 if (ghost < 2)
51 throw std::invalid_argument("peclet::flow::vof::VofMcArea: ghost width must be >= 2");
52 if (nx < 1 || ny < 1 || nz < 1)
53 throw std::invalid_argument("peclet::flow::vof::VofMcArea: empty block");
54 n_ = I3{nx, ny, nz};
55 g_ = ghost;
56 e_ = I3{nx + 2 * ghost, ny + 2 * ghost, nz + 2 * ghost};
57 len_ = static_cast<long>(e_.x) * e_.y * e_.z;
58 dist_ = SField("vof::mc::d", len_);
59 nx_ = SField("vof::mc::nx", len_);
60 ny_ = SField("vof::mc::ny", len_);
61 nz_ = SField("vof::mc::nz", len_);
62 area_ = SField("vof::mc::A", len_);
63 }
64
65 bool ready() const { return area_.extent(0) != 0; }
66 SField area() const { return area_; }
67
72 VofMetric metric;
73 double interfaceEps = 0.0;
74
76 Stats compute(SField c, int mode) {
77 if (!ready())
78 throw std::runtime_error("peclet::flow::vof::VofMcArea::compute: init() not called");
80 throw std::runtime_error("peclet::flow::vof::VofMcArea::compute: mode out of range");
81 const int src = (mode >= kAreaMcPlic) ? kMcSrcPlic : kMcSrcColour;
82 const int dep = ((mode - kAreaMcColour) & 1) ? kMcDepositSplit : kMcDepositCentroid;
83 planePass(c);
84 areaPass(c, src, dep);
85 return census(c);
86 }
87
88 // public for the same nvcc reason `VofCurvature`'s passes are.
89
92 void planePass(SField c) {
93 const I3 e = e_, n = n_;
94 const int g = g_;
95 const long sy = e_.x, sz = static_cast<long>(e_.x) * e_.y;
96 SField dd = dist_, mx = nx_, my = ny_, mz = nz_;
97 const double ieps = interfaceEps;
98 Kokkos::parallel_for(
99 "vof::mc::planes",
100 MDRange3<SExec>(SExec(), {g - 1, g - 1, g - 1}, {g + n.x + 1, g + n.y + 1, g + n.z + 1}),
101 KOKKOS_LAMBDA(int x, int y, int z) {
102 const long i = L3(x, y, z, e);
103 dd(i) = 0.0;
104 mx(i) = 0.0;
105 my(i) = 0.0;
106 mz(i) = 0.0;
107 if (!vofIsInterface(c(i), ieps))
108 return;
109 double st[27];
110 for (int kk = -1; kk <= 1; ++kk)
111 for (int jj = -1; jj <= 1; ++jj)
112 for (int ii = -1; ii <= 1; ++ii)
113 st[plicSt(ii + 1, jj + 1, kk + 1)] = c(i + ii + jj * sy + kk * sz);
114 double m[3];
115 mycNormal(st, m);
116 double un[3] = {0.0, 0.0, 0.0};
117 if (!(pcUnitNormal(m[0], m[1], m[2], un) > 0.0))
118 return;
119 const double al = plicAlpha(m[0], m[1], m[2], c(i));
120 dd(i) = pcCentreDistance(m[0], m[1], m[2], al);
121 mx(i) = un[0];
122 my(i) = un[1];
123 mz(i) = un[2];
124 });
125 Kokkos::fence();
126 }
127
129 void areaPass(SField c, int src, int dep) {
130 const I3 e = e_, n = n_;
131 const int g = g_;
132 SField dd = dist_, mx = nx_, my = ny_, mz = nz_, ar = area_;
133 const double ieps = interfaceEps;
134 const VofMetric gm = metric; // `g` is the ghost width in this scope
135 Kokkos::parallel_for(
136 "vof::mc::area", MDRange3<SExec>(SExec(), {g, g, g}, {g + n.x, g + n.y, g + n.z}),
137 KOKKOS_LAMBDA(int x, int y, int z) {
138 const long i0 = L3(x, y, z, e);
139 ar(i0) = 0.0;
140 // Early-out: every dual cube this cell is a corner of has its corners inside the 3^3
141 // neighbourhood, so with no sign change of (C - 1/2) there nothing can cross.
142 bool anyLo = false, anyHi = false;
143 for (int kk = -1; kk <= 1; ++kk)
144 for (int jj = -1; jj <= 1; ++jj)
145 for (int ii = -1; ii <= 1; ++ii) {
146 // the SAME predicate the tets use (`psi = 1/2 - C < 0` is liquid), so a cell at
147 // exactly C = 1/2 is classified identically in the early-out and in the kernel.
148 if (c(L3(x + ii, y + jj, z + kk, e)) > 0.5)
149 anyHi = true;
150 else
151 anyLo = true;
152 }
153 if (!(anyLo && anyHi))
154 return;
155 double acc = 0.0;
156 for (int oz = -1; oz <= 0; ++oz)
157 for (int oy = -1; oy <= 0; ++oy)
158 for (int ox = -1; ox <= 0; ++ox) {
159 McVertex v[8];
160 for (int k = 0; k < 8; ++k) {
161 const int bx = k & 1, by = (k >> 1) & 1, bz = (k >> 2) & 1;
162 const long j = L3(x + ox + bx, y + oy + by, z + oz + bz, e);
163 const double cv = c(j);
164 v[k].psi = 0.5 - cv;
165 v[k].has =
166 vofIsInterface(cv, ieps) && (mx(j) != 0.0 || my(j) != 0.0 || mz(j) != 0.0);
167 v[k].d = dd(j);
168 v[k].n[0] = mx(j);
169 v[k].n[1] = my(j);
170 v[k].n[2] = mz(j);
171 }
172 const int lc = (-ox) | ((-oy) << 1) | ((-oz) << 2);
173 acc += mcCubeCornerArea(v, lc, src, dep, gm);
174 }
175 ar(i0) = acc;
176 });
177 Kokkos::fence();
178 }
179
180 Stats census(SField c) const {
181 const I3 e = e_, n = n_;
182 const int g = g_;
183 SField ar = area_;
184 const double ieps = interfaceEps;
185 Stats s;
186 Kokkos::parallel_reduce(
187 "vof::mc::census", MDRange3<SExec>(SExec(), {g, g, g}, {g + n.x, g + n.y, g + n.z}),
188 KOKKOS_LAMBDA(int x, int y, int z, long& nc, long& no, double& acc, double& oacc) {
189 const long i = L3(x, y, z, e);
190 const double a = ar(i);
191 if (!(a > 0.0))
192 return;
193 ++nc;
194 acc += a;
195 if (!vofIsInterface(c(i), ieps)) {
196 ++no;
197 oacc += a;
198 }
199 },
200 s.cells, s.orphanCells, s.area, s.orphanArea);
201 Kokkos::fence();
202 return s;
203 }
204
205 private:
206 I3 n_{0, 0, 0}, e_{0, 0, 0};
207 int g_ = 0;
208 long len_ = 0;
209 SField dist_, nx_, ny_, nz_, area_;
210};
211
212} // namespace peclet::flow::vof
213
214#endif // PECLET_FLOW_VOF_MARCHING_CUBES_FIELD_HPP
flow — VoF rung V1: Weymouth & Yue (2010) directionally-split, exactly volume-conserving geometric ad...
Interfacial area per cell from the marching-tetrahedra surface of the C = 1/2 level set (or of the PL...
VofMetric metric
The interfacial predicate's wisp threshold — the phase-change driver passes ITS OWN (pcEffInterfaceEp...
void planePass(SField c)
The gas-positive centre distance and unit normal of every cell's own PLIC plane, one layer beyond the...
void init(int nx, int ny, int nz, int ghost)
Stats compute(SField c, int mode)
mode is an InterfaceAreaMode in [kAreaMcColour, kAreaMcPlicSplit].
void areaPass(SField c, int src, int dep)
The gather: each inner cell walks the 8 dual cubes it is a corner of and keeps its share.
flow — VoF rung V3 (WO-O): the view-level driver for the curvature cascade.
flow — WO-P3c: the interfacial AREA taken from the CURVATURE cascade's geometry.
flow — portable (Kokkos) MAC stencil operators: Red-Black Gauss-Seidel smoothers + divergence.
flow — WO-P3d: a JOINED interfacial area, from marching tetrahedra on the cell-centre lattice.
double pcCentreDistance(double mx, double my, double mz, double alpha)
Signed distance (in cells) from the CELL CENTRE to the PLIC plane, measured along n and POSITIVE ON T...
bool vofIsInterface(double c, double eps)
wyIsMixed with a wisp threshold: the cell carries an interface only while eps < C < 1 - eps.
@ kMcDepositSplit
clipped to each cell's cube, each cell gets its own piece
@ kMcDepositCentroid
the whole triangle to the cell holding its centroid
@ kAreaMcPlicSplit
the PLIC signed-distance level set, clipped deposit
@ kAreaMcPlic
the PLIC signed-distance level set, centroid deposit
@ kAreaMcColour
the C = 1/2 level set, whole triangles to the centroid's cell
@ kMcSrcColour
the C = 1/2 level set (the WO's design)
@ kMcSrcPlic
the zero of the PLIC-reconstructed signed distance (the smooth variant)
double pcUnitNormal(double mx, double my, double mz, double n[3])
Unit (L2) interface normal from a PLIC normal of any scale.
double mcCubeCornerArea(const McVertex v[8], int lc, int src, int deposit, const VofMetric &g)
The interfacial area inside one dual cube that belongs to its local corner lc.
long L3(int x, int y, int z, I3 e)
Kokkos::DefaultExecutionSpace SExec
Kokkos::View< double *, SMem > SField
flow — VoF phase change (Part II, rungs P0/P1): the container-free kernel set.
flow — thin shim: the container-free VoF PLIC geometric toolbox (rung V0) now live in core.
flow — the multidimensional iteration policies: iteration order follows storage.
One dual-cube corner = one cell centre.
double orphanArea
... the part of it the phase-change consumer would drop
long orphanCells
... of which are NOT interfacial under interfaceEps
double area
the sum this rung exists to get right
long cells
inner cells that received area