peclet.voro 1.0.4
Device-native moving-particle Voronoi dynamics
Loading...
Searching...
No Matches
pore_cells.hpp
Go to the documentation of this file.
1
29#ifndef PECLET_VORO_PORE_CELLS_HPP
30#define PECLET_VORO_PORE_CELLS_HPP
31
32#include <cstdint>
33#include <Kokkos_Core.hpp>
34#include <string>
35
36#include "peclet/core/common/view.hpp"
39#include "peclet/voro/sdf.hpp"
42
43namespace peclet::voro {
44
46template <class Real>
48 using MemSpace = peclet::core::MemSpace;
49 Kokkos::View<Real*, MemSpace> points;
51 Kokkos::View<int64_t*, MemSpace> faces;
52 Kokkos::View<int64_t*, MemSpace> faceOffset;
53 Kokkos::View<Real*, MemSpace> volume;
54 Kokkos::View<int*, MemSpace> boundary;
55 Kokkos::View<int*, MemSpace> seed;
56 long numOverflow = 0;
57 long numIncomplete = 0;
58};
59
62template <class Real>
64 using MemSpace = peclet::core::MemSpace;
65 Kokkos::View<Real*, MemSpace> verts;
66 Kokkos::View<int64_t*, MemSpace> offset;
67 Kokkos::View<Real*, MemSpace> volume;
68 Kokkos::View<int*, MemSpace> seed;
69 long numOverflow = 0;
70 long numIncomplete = 0;
71};
72
73namespace detail {
74
79template <class Cell>
80KOKKOS_INLINE_FUNCTION bool poreCellCount(const Cell& c, int& nPts, int& nEntries, bool& wall) {
81 if (c.empty() || c.overflow)
82 return false;
83 nPts = 0;
84 for (int t = 0; t < c.nt; ++t)
85 if (c.alive[t])
86 ++nPts;
87 if (nPts < 4)
88 return false;
89 nEntries = 1;
90 wall = false;
91 int nf = 0;
92 int fidx[Cell::MAXFV];
93 for (int k = 0; k < c.np; ++k) {
94 const int m = c.faceOrderedIdx(k, fidx);
95 if (m < 3)
96 continue;
97 ++nf;
98 nEntries += 1 + m;
99 if (c.pnbr[k] == kBoundaryFacet)
100 wall = true;
101 }
102 return nf >= 4;
103}
104
107template <class Cell, class Real>
108KOKKOS_INLINE_FUNCTION void poreCellFill(const Cell& c, const Real seed[3], int64_t ptBase,
109 int64_t fBase, Real* pts, int64_t* faces) {
110 int triToPt[Cell::kMaxT];
111 int q = 0;
112 for (int t = 0; t < c.nt; ++t) {
113 triToPt[t] = -1;
114 if (!c.alive[t])
115 continue;
116 triToPt[t] = q;
117 const int64_t o = 3 * (ptBase + q);
118 pts[o] = seed[0] + c.vx[t];
119 pts[o + 1] = seed[1] + c.vy[t];
120 pts[o + 2] = seed[2] + c.vz[t];
121 ++q;
122 }
123 int fidx[Cell::MAXFV];
124 int nf = 0;
125 for (int k = 0; k < c.np; ++k)
126 if (c.faceOrderedIdx(k, fidx) >= 3)
127 ++nf;
128 int64_t w = fBase;
129 faces[w++] = nf;
130 for (int k = 0; k < c.np; ++k) {
131 const int m = c.faceOrderedIdx(k, fidx);
132 if (m < 3)
133 continue;
134 faces[w++] = m;
135 for (int i = 0; i < m; ++i)
136 faces[w++] = ptBase + triToPt[fidx[i]];
137 }
138}
139
144template <class Real, class Sdf, int MAXP, int MAXT>
147 using Cell = typename Builder::Cell;
149
150 static PoreCellGather make(const TessGrid<Real>& grid, const Sdf& sdf) {
152 Builder& op = g.op;
153 op.binned = grid.binned;
154 op.posSorted = grid.posSorted;
155 op.wSorted = grid.wSorted;
156 op.gidSorted = grid.gidSorted;
157 op.cellStart = grid.cellStart;
158 op.wlOff = grid.wlOff;
159 op.wlRmin = grid.wlRmin;
160 op.icx = grid.icx;
161 op.icy = grid.icy;
162 op.icz = grid.icz;
163 op.Lx = grid.Lx;
164 op.Ly = grid.Ly;
165 op.Lz = grid.Lz;
166 op.minCsz = grid.minCsz;
167 op.wMaxAll = Real(0);
168 op.dimx = grid.dimx;
169 op.dimy = grid.dimy;
170 op.dimz = grid.dimz;
171 op.sw = grid.sw;
172 op.nOff = grid.nOff;
173 op.wlS = grid.wlS;
174 op.useMorton = grid.useMorton;
175 op.haveGid = grid.haveGid;
176 op.sdf = sdf;
177 return g;
178 }
179
183 Real wSelf;
184 int ncRec, nnRec;
185 op.gatherCell(pi, c, seedW[0], seedW[1], seedW[2], wSelf, ncRec, nnRec);
186 if (c.overflow)
187 return kOverflow;
189 if (c.overflow)
190 return kOverflow;
191 if (c.empty())
192 return kEmpty;
193 // Completeness of the EXPORTED (clipped) cell: every seed that could cut it lies within twice
194 // its reach, and the worklist examined everything within the walked window (grid.sw blocks,
195 // the clamped window the grid was actually built with). The cold build judges the un-clipped
196 // cell, which near a solid extends across the hole the clip removes — a spurious flag here.
197 const Real covSq = Real(op.sw) * op.minCsz * Real(op.sw) * op.minCsz;
198 return (covSq > Voronoi::template blockReachSq<Real>(c.maxVertexRsq(), wSelf, op.wMaxAll))
199 ? kOk
200 : kIncomplete;
201 }
202};
203
205template <class Count, class Off>
206inline void exclusiveScan(const Count& count, const Off& off, int N) {
207 using Exec = peclet::core::ExecSpace;
208 using T = typename Off::value_type;
209 Kokkos::parallel_scan(
210 "pore.scan", Kokkos::RangePolicy<Exec>(0, N + 1),
211 KOKKOS_LAMBDA(const int i, T& acc, const bool fin) {
212 const T v = (i < N) ? T(count(i)) : T(0);
213 if (fin)
214 off(i) = acc;
215 acc += v;
216 });
217}
218
219template <class Off>
220inline typename Off::value_type lastOf(const Off& off, int N) {
221 typename Off::value_type v;
222 Kokkos::deep_copy(v, Kokkos::subview(off, N));
223 return v;
224}
225
226template <class Status>
227inline void statusCounts(const Status& status, int N, long& overflow, long& incomplete) {
228 using Exec = peclet::core::ExecSpace;
229 long ov = 0, inc = 0;
230 Kokkos::parallel_reduce(
231 "pore.overflow", Kokkos::RangePolicy<Exec>(0, N),
232 KOKKOS_LAMBDA(const int i, long& a) { a += (status(i) & kOverflow) ? 1 : 0; }, ov);
233 Kokkos::parallel_reduce(
234 "pore.incomplete", Kokkos::RangePolicy<Exec>(0, N),
235 KOKKOS_LAMBDA(const int i, long& a) {
236 a += ((status(i) & kIncomplete) && !(status(i) & (kEmpty | kOverflow))) ? 1 : 0;
237 },
238 inc);
239 overflow = ov;
240 incomplete = inc;
241}
242
243} // namespace detail
244
256template <class Real, class Sdf, int MAXP = kMaxPlanes, int MAXT = kMaxTriangles>
257PoreCellsResult<Real> buildPoreCells(const Kokkos::View<Real*, peclet::core::MemSpace>& posFlat,
258 int N, const Real L[3], const Sdf& sdf,
259 int sw = kSearchWindow) {
260 using MemSpace = peclet::core::MemSpace;
261 using Exec = peclet::core::ExecSpace;
263 using Cell = typename Gather::Cell;
264 using Kokkos::view_alloc;
265 using Kokkos::WithoutInitializing;
266 Kokkos::View<Real*, MemSpace> noW;
267 Kokkos::View<long*, MemSpace> noGid;
268 auto grid = buildTessGrid<Real, false>(posFlat, noW, N, L, sw, N, noGid, nullptr);
269 const Gather g = Gather::make(grid, sdf);
270 auto binned = grid.binned;
271
272 // Count pass (written at the ORIGINAL seed index, so the scan below is in seed order).
273 Kokkos::View<int*, MemSpace> status("pore.status", N), emit("pore.emit", N), nPts("pore.nPts", N),
274 nEnt("pore.nEnt", N), wall("pore.wall", N);
275 Kokkos::parallel_for(
276 "pore.count", Kokkos::RangePolicy<Exec>(0, N), KOKKOS_LAMBDA(const int pi) {
277 const int i = binned(pi);
278 Cell c;
279 Real s[3];
280 const int st = g.clipped(pi, c, s);
281 status(i) = st;
282 if (st & (kEmpty | kOverflow))
283 return;
284 int np = 0, ne = 0;
285 bool w = false;
286 if (!detail::poreCellCount(c, np, ne, w))
287 return;
288 emit(i) = 1;
289 nPts(i) = np;
290 nEnt(i) = ne;
291 wall(i) = w ? 1 : 0;
292 });
293 Kokkos::View<int*, MemSpace> cellOff("pore.cellOff", N + 1);
294 Kokkos::View<int64_t*, MemSpace> ptOff("pore.ptOff", N + 1), entOff("pore.entOff", N + 1);
295 detail::exclusiveScan(emit, cellOff, N);
296 detail::exclusiveScan(nPts, ptOff, N);
297 detail::exclusiveScan(nEnt, entOff, N);
298 const int nCells = detail::lastOf(cellOff, N);
299 const int64_t nPoints = detail::lastOf(ptOff, N), nEntries = detail::lastOf(entOff, N);
300
302 res.points = Kokkos::View<Real*, MemSpace>(
303 view_alloc(std::string("pore.points"), WithoutInitializing), 3 * (size_t)nPoints);
304 res.faces = Kokkos::View<int64_t*, MemSpace>(
305 view_alloc(std::string("pore.faces"), WithoutInitializing), (size_t)nEntries);
306 res.faceOffset = Kokkos::View<int64_t*, MemSpace>("pore.faceOffset", nCells + 1);
307 res.volume = Kokkos::View<Real*, MemSpace>("pore.volume", nCells);
308 res.boundary = Kokkos::View<int*, MemSpace>("pore.boundary", nCells);
309 res.seed = Kokkos::View<int*, MemSpace>("pore.seed", nCells);
310 auto pts = res.points;
311 auto faces = res.faces;
312 auto faceOffset = res.faceOffset;
313 auto volume = res.volume;
314 auto boundary = res.boundary;
315 auto seed = res.seed;
316 Kokkos::deep_copy(Kokkos::subview(faceOffset, nCells), nEntries);
317 // Fill pass: the same gather again, written at the scanned offsets.
318 Kokkos::parallel_for(
319 "pore.fill", Kokkos::RangePolicy<Exec>(0, N), KOKKOS_LAMBDA(const int pi) {
320 const int i = binned(pi);
321 if (!emit(i))
322 return;
323 Cell c;
324 Real s[3];
325 g.clipped(pi, c, s);
326 const int ci = cellOff(i);
327 detail::poreCellFill(c, s, ptOff(i), entOff(i), pts.data(), faces.data());
328 faceOffset(ci) = entOff(i);
329 volume(ci) = c.volumePerVertex();
330 boundary(ci) = wall(i);
331 seed(ci) = i;
332 });
333 Kokkos::fence();
335 return res;
336}
337
344template <class Real, class Sdf, int MAXP = kMaxPlanes, int MAXT = kMaxTriangles>
345PoreSectionResult<Real> buildPoreSection(const Kokkos::View<Real*, peclet::core::MemSpace>& posFlat,
346 int N, const Real L[3], const Sdf& sdf,
347 const Real point[3], const Real normal[3],
348 int sw = kSearchWindow) {
349 using MemSpace = peclet::core::MemSpace;
350 using Exec = peclet::core::ExecSpace;
352 using Cell = typename Gather::Cell;
353 using Kokkos::view_alloc;
354 using Kokkos::WithoutInitializing;
355 Kokkos::View<Real*, MemSpace> noW;
356 Kokkos::View<long*, MemSpace> noGid;
357 auto grid = buildTessGrid<Real, false>(posFlat, noW, N, L, sw, N, noGid, nullptr);
358 const Gather g = Gather::make(grid, sdf);
359 auto binned = grid.binned;
360 const Real px = point[0], py = point[1], pz = point[2];
361 const Real ux = normal[0], uy = normal[1], uz = normal[2];
362
363 Kokkos::View<int*, MemSpace> status("pore.status", N), nV("pore.nV", N), emit("pore.emit", N);
364 Kokkos::parallel_for(
365 "pore.section.count", Kokkos::RangePolicy<Exec>(0, N), KOKKOS_LAMBDA(const int pi) {
366 const int i = binned(pi);
367 Cell c;
368 Real s[3];
369 const int st = g.clipped(pi, c, s);
370 status(i) = st;
371 if (st & (kEmpty | kOverflow))
372 return;
373 const Real p0[3] = {px - s[0], py - s[1], pz - s[2]}; // the plane in the cell frame
374 const Real u[3] = {ux, uy, uz};
375 Real spx[Cell::MAXSV], spy[Cell::MAXSV], spz[Cell::MAXSV];
376 const int m = c.sectionPolygon(p0, u, spx, spy, spz);
377 if (m < 3)
378 return;
379 emit(i) = 1;
380 nV(i) = m;
381 });
382 Kokkos::View<int*, MemSpace> polyOff("pore.polyOff", N + 1);
383 Kokkos::View<int64_t*, MemSpace> vOff("pore.vOff", N + 1);
384 detail::exclusiveScan(emit, polyOff, N);
385 detail::exclusiveScan(nV, vOff, N);
386 const int nPoly = detail::lastOf(polyOff, N);
387 const int64_t nVerts = detail::lastOf(vOff, N);
388
390 res.verts = Kokkos::View<Real*, MemSpace>(
391 view_alloc(std::string("pore.verts"), WithoutInitializing), 3 * (size_t)nVerts);
392 res.offset = Kokkos::View<int64_t*, MemSpace>("pore.offset", nPoly + 1);
393 res.volume = Kokkos::View<Real*, MemSpace>("pore.volume", nPoly);
394 res.seed = Kokkos::View<int*, MemSpace>("pore.seed", nPoly);
395 auto verts = res.verts;
396 auto offset = res.offset;
397 auto volume = res.volume;
398 auto seed = res.seed;
399 Kokkos::deep_copy(Kokkos::subview(offset, nPoly), nVerts);
400 Kokkos::parallel_for(
401 "pore.section.fill", Kokkos::RangePolicy<Exec>(0, N), KOKKOS_LAMBDA(const int pi) {
402 const int i = binned(pi);
403 if (!emit(i))
404 return;
405 Cell c;
406 Real s[3];
407 g.clipped(pi, c, s);
408 const Real p0[3] = {px - s[0], py - s[1], pz - s[2]};
409 const Real u[3] = {ux, uy, uz};
410 Real spx[Cell::MAXSV], spy[Cell::MAXSV], spz[Cell::MAXSV];
411 const int m = c.sectionPolygon(p0, u, spx, spy, spz);
412 const int64_t base = vOff(i);
413 for (int k = 0; k < m; ++k) {
414 verts(3 * (base + k)) = s[0] + spx[k];
415 verts(3 * (base + k) + 1) = s[1] + spy[k];
416 verts(3 * (base + k) + 2) = s[2] + spz[k];
417 }
418 const int p = polyOff(i);
419 offset(p) = base;
420 volume(p) = c.volumePerVertex();
421 seed(p) = i;
422 });
423 Kokkos::fence();
425 return res;
426}
427
428} // namespace peclet::voro
429
430#endif // PECLET_VORO_PORE_CELLS_HPP
KOKKOS_INLINE_FUNCTION Dual< Real, K > dnum(Real c)
Definition convex_cell.hpp:53
KOKKOS_INLINE_FUNCTION bool poreCellCount(const Cell &c, int &nPts, int &nEntries, bool &wall)
Definition pore_cells.hpp:80
void exclusiveScan(const Count &count, const Off &off, int N)
Exclusive scan of a per-seed count into offsets (N+1 entries; the last is the total).
Definition pore_cells.hpp:206
KOKKOS_INLINE_FUNCTION void poreCellFill(const Cell &c, const Real seed[3], int64_t ptBase, int64_t fBase, Real *pts, int64_t *faces)
Definition pore_cells.hpp:108
void statusCounts(const Status &status, int N, long &overflow, long &incomplete)
Definition pore_cells.hpp:227
Off::value_type lastOf(const Off &off, int N)
Definition pore_cells.hpp:220
Device-native Voronoi tessellation, geometry and finite-volume physics.
Definition convex_cell.hpp:41
PoreSectionResult< Real > buildPoreSection(const Kokkos::View< Real *, peclet::core::MemSpace > &posFlat, int N, const Real L[3], const Sdf &sdf, const Real point[3], const Real normal[3], int sw=kSearchWindow)
Definition pore_cells.hpp:345
@ kOverflow
Definition tessellator.hpp:50
@ kOk
Definition tessellator.hpp:49
@ kIncomplete
Definition tessellator.hpp:52
@ kEmpty
Definition tessellator.hpp:51
PoreCellsResult< Real > buildPoreCells(const Kokkos::View< Real *, peclet::core::MemSpace > &posFlat, int N, const Real L[3], const Sdf &sdf, int sw=kSearchWindow)
Definition pore_cells.hpp:257
constexpr int kBoundaryFacet
Definition sdf.hpp:36
constexpr int kSearchWindow
Definition params.hpp:28
The named engine defaults of peclet.voro — one header-level home for every capacity and default the l...
ConvexCell< Real, kMaxP, kMaxT, TrackAdj > Cell
Definition tessellator.hpp:241
int dimx
Definition tessellator.hpp:268
Kokkos::View< int *, MemSpace > binned
Definition tessellator.hpp:245
KOKKOS_INLINE_FUNCTION bool gatherCell(int pi, Cell &c, Real &pix, Real &piy, Real &piz, Real &wSelf, int &ncRec, int &nnRec) const
Definition tessellator.hpp:536
int wlS
Definition tessellator.hpp:268
Kokkos::View< Real *, MemSpace > posSorted
Definition tessellator.hpp:246
Kokkos::View< int *, MemSpace > wlOff
Definition tessellator.hpp:255
int sw
Definition tessellator.hpp:268
bool useMorton
Definition tessellator.hpp:269
Real minCsz
Definition tessellator.hpp:266
Real icx
Definition tessellator.hpp:266
Real Ly
Definition tessellator.hpp:266
Kokkos::View< Real *, MemSpace > wlRmin
Definition tessellator.hpp:256
Real icz
Definition tessellator.hpp:266
int dimy
Definition tessellator.hpp:268
Kokkos::View< int *, MemSpace > cellStart
Definition tessellator.hpp:249
Kokkos::View< gid_t *, MemSpace > gidSorted
Definition tessellator.hpp:248
bool haveGid
Definition tessellator.hpp:269
int nOff
Definition tessellator.hpp:268
Real wMaxAll
Definition tessellator.hpp:267
int dimz
Definition tessellator.hpp:268
Real Lz
Definition tessellator.hpp:266
Kokkos::View< Real *, MemSpace > wSorted
Definition tessellator.hpp:247
Real icy
Definition tessellator.hpp:266
Real Lx
Definition tessellator.hpp:266
Sdf sdf
Definition tessellator.hpp:271
The clipped polyhedra, packed as flat arrays in seed order (VTK_POLYHEDRON layout).
Definition pore_cells.hpp:47
long numIncomplete
exported cells whose gather coverage did not close (raise sw)
Definition pore_cells.hpp:57
peclet::core::MemSpace MemSpace
Definition pore_cells.hpp:48
Kokkos::View< Real *, MemSpace > points
Definition pore_cells.hpp:49
Kokkos::View< int64_t *, MemSpace > faces
Per exported cell: nf, then per face (m, m global point ids, CCW about the outward normal).
Definition pore_cells.hpp:51
long numOverflow
seeds skipped because their cell hit a capacity
Definition pore_cells.hpp:56
Kokkos::View< int64_t *, MemSpace > faceOffset
numCells+1: each cell's range in faces
Definition pore_cells.hpp:52
Kokkos::View< int *, MemSpace > boundary
numCells: 1 where a face is a wall facet
Definition pore_cells.hpp:54
Kokkos::View< int *, MemSpace > seed
numCells: the seed index of each cell
Definition pore_cells.hpp:55
Kokkos::View< Real *, MemSpace > volume
numCells: the clipped cell volume
Definition pore_cells.hpp:53
Definition pore_cells.hpp:63
Kokkos::View< Real *, MemSpace > verts
3·numVerts
Definition pore_cells.hpp:65
Kokkos::View< int *, MemSpace > seed
numPolygons: the seed index of the cut cell
Definition pore_cells.hpp:68
Kokkos::View< int64_t *, MemSpace > offset
numPolygons+1: each polygon's vertex range
Definition pore_cells.hpp:66
peclet::core::MemSpace MemSpace
Definition pore_cells.hpp:64
Kokkos::View< Real *, MemSpace > volume
numPolygons: the 3-D volume of the cut cell
Definition pore_cells.hpp:67
long numIncomplete
cut cells whose gather coverage did not close
Definition pore_cells.hpp:70
long numOverflow
seeds skipped because their cell hit a capacity
Definition pore_cells.hpp:69
Definition tess_grid.hpp:56
Definition pore_cells.hpp:145
typename Builder::Cell Cell
Definition pore_cells.hpp:147
Builder op
Definition pore_cells.hpp:148
KOKKOS_INLINE_FUNCTION int clipped(int pi, Cell &c, Real seedW[3]) const
Definition pore_cells.hpp:182
static PoreCellGather make(const TessGrid< Real > &grid, const Sdf &sdf)
Definition pore_cells.hpp:150