peclet.voro 1.0.4
Device-native moving-particle Voronoi dynamics
Loading...
Searching...
No Matches
sdf.hpp
Go to the documentation of this file.
1
20#ifndef PECLET_VORO_SDF_HPP
21#define PECLET_VORO_SDF_HPP
22
23#include <Kokkos_Core.hpp>
24#include <string>
25#include <type_traits>
26
27#include "peclet/core/common/view.hpp"
28#include "peclet/core/geom/primitives.hpp"
29#include "peclet/core/geom/scene.hpp"
31
32namespace peclet::voro {
33
36constexpr int kBoundaryFacet = -2;
37
39struct NoSdf {};
40
44template <class Sdf>
45struct TangentOnly : Sdf {
46 static constexpr bool kTangentOnly = true;
47};
48namespace detail {
49template <class T, class = void>
50struct tangentOnly : std::false_type {};
51template <class T>
52struct tangentOnly<T, std::void_t<decltype(T::kTangentOnly)>>
53 : std::bool_constant<T::kTangentOnly> {};
54template <class T>
55inline constexpr bool tangentOnly_v = tangentOnly<T>::value;
56} // namespace detail
57
61template <class Real>
62struct SdfSphere {
63 Real cx = 0, cy = 0, cz = 0, radius = 1;
64 KOKKOS_INLINE_FUNCTION Real eval(Real x, Real y, Real z) const {
65 return peclet::core::geom::prim::Sphere<Real>{radius}.eval(
66 peclet::core::Vec3<Real>{x - cx, y - cy, z - cz});
67 }
68 KOKKOS_INLINE_FUNCTION Real gradH() const { return Real(1e-4); }
69};
70
75template <class Real>
76struct SdfBox {
77 Real cx = 0, cy = 0, cz = 0, hx = Real(0.5), hy = Real(0.5), hz = Real(0.5);
78 KOKKOS_INLINE_FUNCTION Real eval(Real x, Real y, Real z) const {
79 return peclet::core::geom::prim::Box<Real>{hx, hy, hz}.eval(
80 peclet::core::Vec3<Real>{x - cx, y - cy, z - cz});
81 }
82 KOKKOS_INLINE_FUNCTION Real gradH() const { return Real(1e-4); }
83};
84
91template <class Real>
93 Real cx = 0, cy = 0, cz = 0, rOuter = 1, rInner = Real(0.5), height = 1;
94 int axis = 2;
95 KOKKOS_INLINE_FUNCTION Real eval(Real x, Real y, Real z) const {
96 Real d[3] = {x - cx, y - cy, z - cz};
97 int a0 = (axis + 1) % 3, a1 = (axis + 2) % 3;
98 Real r = Kokkos::sqrt(d[a0] * d[a0] + d[a1] * d[a1]);
99 return peclet::core::geom::prim::HollowCylinderShell<Real>{rOuter, rInner, height}.evalRZ(
100 r, d[axis]);
101 }
102 KOKKOS_INLINE_FUNCTION Real gradH() const { return Real(1e-4); }
103};
104
118template <class Real>
119struct SdfGrid {
120 Kokkos::View<const float*, peclet::core::MemSpace> values; // i + nx*(j + ny*k)
121 peclet::core::geom::GridDesc<Real> desc{};
122
130 KOKKOS_INLINE_FUNCTION static SdfGrid fromSpacing(
131 Kokkos::View<const float*, peclet::core::MemSpace> v, int nx, int ny, int nz, Real ox,
132 Real oy, Real oz, Real sx, Real sy, Real sz) {
133 SdfGrid g;
134 g.values = v;
135 g.desc.nx = nx;
136 g.desc.ny = ny;
137 g.desc.nz = nz;
138 g.desc.offset = 0;
139 g.desc.origin = peclet::core::Vec3<Real>{ox, oy, oz};
140 g.desc.invSpacing = peclet::core::Vec3<Real>{sx != Real(0) ? Real(1) / sx : Real(0),
141 sy != Real(0) ? Real(1) / sy : Real(0),
142 sz != Real(0) ? Real(1) / sz : Real(0)};
143 g.desc.extension = peclet::core::geom::GridExtension::kClamp;
144 return g;
145 }
146
147 KOKKOS_INLINE_FUNCTION Real eval(Real x, Real y, Real z) const {
148 return peclet::core::geom::sampleGrid(peclet::core::Vec3<Real>{x, y, z}, desc, values);
149 }
150 KOKKOS_INLINE_FUNCTION Real gradH() const {
151 // quarter of the smallest cell, as before -- recovered from the stored inverse spacing
152 const Real ix = desc.invSpacing.x, iy = desc.invSpacing.y, iz = desc.invSpacing.z;
153 Real mx = ix > iy ? ix : iy;
154 mx = mx > iz ? mx : iz; // largest inverse == smallest spacing
155 return mx > Real(0) ? Real(0.25) / mx : Real(1e-4);
156 }
157};
158
168template <class Real>
169struct SdfScene {
170 Kokkos::View<const peclet::core::geom::ShapeNode<Real>*, peclet::core::MemSpace> nodes;
171 Kokkos::View<const peclet::core::geom::GridDesc<Real>*, peclet::core::MemSpace> grids;
172 Kokkos::View<const float*, peclet::core::MemSpace> samples;
173 int nodeCount = 0;
174 int root = 0;
175 Real h = Real(1e-4);
176
177 KOKKOS_INLINE_FUNCTION Real eval(Real x, Real y, Real z) const {
178 return peclet::core::geom::evalTree<Real>(nodes, nodeCount, root,
179 peclet::core::Vec3<Real>{x, y, z}, grids, samples);
180 }
181 KOKKOS_INLINE_FUNCTION Real gradH() const { return h; }
182};
183
190template <class Real, class Mem = peclet::core::MemSpace>
192 Kokkos::View<const Real*, Mem> cen; // 3*M
193 Kokkos::View<const Real*, Mem> rad; // M
194 int n = 0;
195 Real L = 0;
196 KOKKOS_INLINE_FUNCTION Real eval(Real x, Real y, Real z) const {
197 Real m = Real(1e30);
198 for (int i = 0; i < n; ++i) {
199 Real dx = x - cen(3 * i), dy = y - cen(3 * i + 1), dz = z - cen(3 * i + 2);
200 if (L > Real(0)) {
201 dx -= L * Kokkos::round(dx / L);
202 dy -= L * Kokkos::round(dy / L);
203 dz -= L * Kokkos::round(dz / L);
204 }
205 const Real d = Kokkos::sqrt(dx * dx + dy * dy + dz * dz) - rad(i);
206 if (d < m)
207 m = d;
208 }
209 return m;
210 }
211 KOKKOS_INLINE_FUNCTION Real gradH() const { return Real(1e-4); }
212};
213
215template <class Real, class Sdf>
216KOKKOS_INLINE_FUNCTION void sdfGradient(const Sdf& s, Real x, Real y, Real z, Real g[3]) {
217 const Real h = s.gradH();
218 g[0] = (s.eval(x + h, y, z) - s.eval(x - h, y, z)) / (2 * h);
219 g[1] = (s.eval(x, y + h, z) - s.eval(x, y - h, z)) / (2 * h);
220 g[2] = (s.eval(x, y, z + h) - s.eval(x, y, z - h)) / (2 * h);
221}
222
225template <class Real, class Sdf>
226KOKKOS_INLINE_FUNCTION void sdfHessian(const Sdf& s, Real x, Real y, Real z, Real H[3][3]) {
227 const Real h = s.gradH();
228 Real gp[3], gm[3];
229 sdfGradient<Real>(s, x + h, y, z, gp);
230 sdfGradient<Real>(s, x - h, y, z, gm);
231 for (int r = 0; r < 3; ++r)
232 H[r][0] = (gp[r] - gm[r]) / (2 * h);
233 sdfGradient<Real>(s, x, y + h, z, gp);
234 sdfGradient<Real>(s, x, y - h, z, gm);
235 for (int r = 0; r < 3; ++r)
236 H[r][1] = (gp[r] - gm[r]) / (2 * h);
237 sdfGradient<Real>(s, x, y, z + h, gp);
238 sdfGradient<Real>(s, x, y, z - h, gm);
239 for (int r = 0; r < 3; ++r)
240 H[r][2] = (gp[r] - gm[r]) / (2 * h);
241 for (int a = 0; a < 3; ++a)
242 for (int b = a + 1; b < 3; ++b) {
243 const Real m = Real(0.5) * (H[a][b] + H[b][a]);
244 H[a][b] = H[b][a] = m;
245 }
246}
247
252template <class Real, class Sdf>
253KOKKOS_INLINE_FUNCTION void sdfWallChain(const Sdf& sdf, Real x, Real y, Real z, const double gw[3],
254 double fSelf[3]) {
255 if constexpr (std::is_same_v<Sdf, NoSdf>) {
256 (void)sdf;
257 (void)x;
258 (void)y;
259 (void)z;
260 (void)gw;
261 (void)fSelf;
262 return;
263 } else {
264 if (gw[0] == 0.0 && gw[1] == 0.0 && gw[2] == 0.0)
265 return;
266 Real gg[3];
267 sdfGradient<Real>(sdf, x, y, z, gg);
268 const double gn =
269 Kokkos::sqrt((double)gg[0] * gg[0] + (double)gg[1] * gg[1] + (double)gg[2] * gg[2]);
270 if (gn <= 1e-12)
271 return; // degenerate gradient (crease / corner): no wall force (guard)
272 const double u0 = gg[0] / gn, u1 = gg[1] / gn, u2 = gg[2] / gn;
273 const double ug = u0 * gw[0] + u1 * gw[1] + u2 * gw[2];
274 const double p0 = gw[0] - ug * u0, p1 = gw[1] - ug * u1, p2 = gw[2] - ug * u2;
275 Real Hm[3][3];
276 sdfHessian<Real>(sdf, x, y, z, Hm);
277 const double Hp0 = Hm[0][0] * p0 + Hm[0][1] * p1 + Hm[0][2] * p2;
278 const double Hp1 = Hm[1][0] * p0 + Hm[1][1] * p1 + Hm[1][2] * p2;
279 const double Hp2 = Hm[2][0] * p0 + Hm[2][1] * p1 + Hm[2][2] * p2;
280 const double cH = (double)sdf.eval(x, y, z) / gn;
281 fSelf[0] += -ug * u0 - cH * Hp0;
282 fSelf[1] += -ug * u1 - cH * Hp1;
283 fSelf[2] += -ug * u2 - cH * Hp2;
284 }
285}
286
296template <class Real, int MAXP, int MAXT, bool TrackAdj, class Sdf>
297KOKKOS_INLINE_FUNCTION void addSdfWallForce(const ConvexCell<Real, MAXP, MAXT, TrackAdj>& c,
298 const Real seed[3], const Sdf& sdf, const Real* gx,
299 const Real* gy, const Real* gz, Real fSelf[3]) {
300 if constexpr (std::is_same_v<Sdf, NoSdf>) {
301 (void)c;
302 (void)seed;
303 (void)sdf;
304 (void)gx;
305 (void)gy;
306 (void)gz;
307 (void)fSelf;
308 return;
309 } else {
310 // Aggregate the wall facets' geometry gradients (all move together under the seed-foot model).
311 Real gw[3] = {Real(0), Real(0), Real(0)};
312 bool any = false;
313 for (int k = 0; k < c.np; ++k)
314 if (c.pnbr[k] == kBoundaryFacet) {
315 gw[0] += gx[k];
316 gw[1] += gy[k];
317 gw[2] += gz[k];
318 any = true;
319 }
320 if (!any)
321 return;
322 // Rung A3: the shared |∇φ|-independent seed-foot chain (foot vector n = −φ∇φ/|∇φ|²). The
323 // earlier body carried an extra |∇φ| factor from the n = −φû shortcut (identical for a true
324 // SDF, |∇φ| = 1; wrong for a general level set).
325 const double gwd[3] = {(double)gw[0], (double)gw[1], (double)gw[2]};
326 double fw[3] = {0.0, 0.0, 0.0};
327 sdfWallChain<Real>(sdf, seed[0], seed[1], seed[2], gwd, fw);
328 for (int d = 0; d < 3; ++d)
329 fSelf[d] += (Real)fw[d];
330 }
331}
332
335constexpr int kMaxWallPlanes = 24;
336
351template <class Real>
352struct WallStore {
353 using MemSpace = peclet::core::MemSpace;
354 static constexpr int kMax = kMaxWallPlanes;
355 Kokkos::View<Real*, MemSpace> rec; // N*kMax*4 : (ux, uy, uz, h) per wall plane
356 Kokkos::View<int*, MemSpace> cnt; // N : wall planes stored for the cell
357
358 void alloc(int n) {
359 rec = Kokkos::View<Real*, MemSpace>(
360 Kokkos::view_alloc(std::string("wall.rec"), Kokkos::WithoutInitializing),
361 (size_t)n * kMax * 4);
362 cnt = Kokkos::View<int*, MemSpace>("wall.cnt", n);
363 }
364 KOKKOS_INLINE_FUNCTION bool active() const { return cnt.extent(0) > 0; }
365
368 template <class Cell>
369 KOKKOS_INLINE_FUNCTION int save(int i, const Cell& c) const {
370 if (!active())
371 return 0;
372 int r = 0;
373 for (int k = 6; k < c.np && r < kMax; ++k) {
374 if (c.pnbr[k] >= 0)
375 continue;
376 const Real h = Kokkos::sqrt(c.nn[k]);
377 const Real ih = h > Real(0) ? Real(1) / h : Real(0);
378 const size_t o = ((size_t)i * kMax + r) * 4;
379 rec(o + 0) = c.n[k][0] * ih;
380 rec(o + 1) = c.n[k][1] * ih;
381 rec(o + 2) = c.n[k][2] * ih;
382 rec(o + 3) = h;
383 ++r;
384 }
385 cnt(i) = r;
386 return r;
387 }
388
392 template <class Cell>
393 KOKKOS_INLINE_FUNCTION bool load(int i, Cell& c, Real dx, Real dy, Real dz) const {
394 if (!active())
395 return true;
396 bool ok = true;
397 int r = 0;
398 const int nrec = cnt(i);
399 for (int k = 6; k < c.np; ++k) {
400 if (c.pnbr[k] >= 0)
401 continue;
402 if (r >= nrec) { // store out of sync with the topology: treat as invalid
403 ok = false;
404 break;
405 }
406 const size_t o = ((size_t)i * kMax + r) * 4;
407 const Real ux = rec(o + 0), uy = rec(o + 1), uz = rec(o + 2);
408 const Real h = rec(o + 3) - (ux * dx + uy * dy + uz * dz);
409 c.n[k][0] = h * ux;
410 c.n[k][1] = h * uy;
411 c.n[k][2] = h * uz;
412 c.nn[k] = h * h;
413 if (!(h > Real(0)))
414 ok = false;
415 ++r;
416 }
417 return ok;
418 }
419};
420
437template <class Real, int MAXP, int MAXT, bool TrackAdj, class Sdf>
439 const Real seed[3], const Sdf& sdf, const int* planes,
440 const Real* tangency, int nPlanes, Real* delta) {
442 int nz = 0;
443 for (int q = 0; q < nPlanes; ++q) {
444 delta[q] = Real(0);
445 const int k = planes[q];
446 if (k < 0)
447 continue; // chord plane: no tangency
448 Real fx[Cell::MAXFV], fy[Cell::MAXFV], fz[Cell::MAXFV];
449 const int m = c.faceOrdered(k, fx, fy, fz);
450 if (m < 3)
451 continue;
452 const Real hk = Kokkos::sqrt(c.nn[k]);
453 if (!(hk > Real(0)))
454 continue;
455 const Real u[3] = {c.n[k][0] / hk, c.n[k][1] / hk, c.n[k][2] / hk}; // seed -> wall
456 const Real* surf = tangency + 3 * q; // world tangency point
457 const Real p0[3] = {surf[0] - seed[0], surf[1] - seed[1], surf[2] - seed[2]};
458 Real A = 0, M[3][3] = {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}};
459 for (int i = 0; i < m; ++i) {
460 const int j = (i + 1) % m;
461 const Real a[3] = {fx[i] - p0[0], fy[i] - p0[1], fz[i] - p0[2]};
462 const Real b[3] = {fx[j] - p0[0], fy[j] - p0[1], fz[j] - p0[2]};
463 const Real cr[3] = {a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2],
464 a[0] * b[1] - a[1] * b[0]};
465 const Real At = Real(0.5) * (cr[0] * u[0] + cr[1] * u[1] + cr[2] * u[2]);
466 A += At;
467 const Real sm[3] = {a[0] + b[0], a[1] + b[1], a[2] + b[2]};
468 const Real w = At / Real(12);
469 for (int r = 0; r < 3; ++r)
470 for (int qq = 0; qq < 3; ++qq)
471 M[r][qq] += w * (a[r] * a[qq] + b[r] * b[qq] + sm[r] * sm[qq]);
472 }
473 if (A < Real(0)) {
474 A = -A;
475 for (int r = 0; r < 3; ++r)
476 for (int qq = 0; qq < 3; ++qq)
477 M[r][qq] = -M[r][qq];
478 }
479 if (!(A > Real(0)))
480 continue;
481 Real g[3];
482 sdfGradient<Real>(sdf, surf[0], surf[1], surf[2], g);
483 const Real gn = Kokkos::sqrt(g[0] * g[0] + g[1] * g[1] + g[2] * g[2]);
484 if (!(gn > Real(0)))
485 continue;
486 Real H[3][3];
487 sdfHessian<Real>(sdf, surf[0], surf[1], surf[2], H);
488 Real tr = 0;
489 for (int r = 0; r < 3; ++r)
490 for (int qq = 0; qq < 3; ++qq)
491 tr += H[r][qq] * M[r][qq];
492 Real d = Real(0.5) * tr / gn / A;
493 if (d < Real(-0.5) * hk) // a concave shift keeps the plane on the far side of the seed
494 d = Real(-0.5) * hk;
495 if (Kokkos::fabs(d) < Real(1e-8) * hk)
496 continue; // Hessian-stencil round-off (flat wall): no shift
497 delta[q] = d;
498 ++nz;
499 }
500 return nz;
501}
502
511template <class Real, int MAXP, int MAXT, bool TrackAdj, class Sdf>
512KOKKOS_INLINE_FUNCTION bool sdfCutPlane(const ConvexCell<Real, MAXP, MAXT, TrackAdj>& c,
513 const Real seed[3], const Sdf& sdf, const Real probe[3],
514 Real radius, Real pv[3], Real& off, Real surf[3]) {
515 (void)c;
516 Real g[3];
517 sdfGradient<Real>(sdf, probe[0], probe[1], probe[2], g);
518 const Real gsq = g[0] * g[0] + g[1] * g[1] + g[2] * g[2];
519 if (gsq <= Real(0))
520 return false;
521 const Real phi = sdf.eval(probe[0], probe[1], probe[2]);
522 const Real invGsq = Real(1) / gsq, gn = Kokkos::sqrt(gsq), invG = Real(1) / gn;
523 Real normal[3];
524 for (int k = 0; k < 3; ++k) {
525 surf[k] = probe[k] - phi * g[k] * invGsq;
526 normal[k] = g[k] * invG;
527 }
528 // orient the normal into the fluid (positive side)
529 Real eps = Real(1e-3) * (radius + Real(1));
530 if (eps < Real(1e-6))
531 eps = Real(1e-6);
532 if (sdf.eval(surf[0] + eps * normal[0], surf[1] + eps * normal[1], surf[2] + eps * normal[2]) <=
533 Real(0))
534 for (int k = 0; k < 3; ++k)
535 normal[k] = -normal[k];
536 pv[0] = -normal[0];
537 pv[1] = -normal[1];
538 pv[2] = -normal[2];
539 off = pv[0] * (surf[0] - seed[0]) + pv[1] * (surf[1] - seed[1]) + pv[2] * (surf[2] - seed[2]);
540 return true;
541}
542
551template <class Real, int MAXP, int MAXT, bool TrackAdj, class Sdf>
552KOKKOS_INLINE_FUNCTION bool sdfWouldClip(const ConvexCell<Real, MAXP, MAXT, TrackAdj>& c,
553 const Real seed[3], const Sdf& sdf) {
554 const Real tol = Real(1e-8);
555 const Real phiCenter = sdf.eval(seed[0], seed[1], seed[2]);
556 if (phiCenter <= Real(0))
557 return true;
558 const Real maxRsq = c.maxVertexRsq();
559 const Real radius = Kokkos::sqrt(maxRsq > 0 ? maxRsq : Real(0));
560 if (phiCenter > radius + tol)
561 return false;
562 // first cut: the same plane clipCellAgainstSdf would commit for probe = seed (tangent at the
563 // seed's foot point, sagitta-shifted unless TangentOnly). ConvexCell::clip commits iff some live
564 // vertex satisfies nf·v > nf·nf with nf = (off/|pv|²) pv; |pv| = 1 here, so the test is
565 // pv·v > off. The clip skips a first cut whose offset is not positive, so only a positive
566 // offset can commit here.
567 {
568 Real pv[3], off, surf[3];
569 if (sdfCutPlane(c, seed, sdf, seed, radius, pv, off, surf) && off > Real(0))
570 for (int t = 0; t < c.nt; ++t) {
571 if (!c.alive[t])
572 continue;
573 if (pv[0] * c.vx[t] + pv[1] * c.vy[t] + pv[2] * c.vz[t] > off)
574 return true;
575 }
576 }
577 // later cuts: the most violating vertex must be inside the solid by more than tol
578 for (int t = 0; t < c.nt; ++t) {
579 if (!c.alive[t])
580 continue;
581 if (sdf.eval(seed[0] + c.vx[t], seed[1] + c.vy[t], seed[2] + c.vz[t]) < -tol)
582 return true;
583 }
584 return false;
585}
586
598template <class Real, int MAXP, int MAXT, bool TrackAdj, class Sdf>
600 const Real seed[3], const Sdf& sdf) {
601 const Real tol = Real(1e-8);
602 const int maxCuts = 24;
603 const Real phiCenter = sdf.eval(seed[0], seed[1], seed[2]);
604 if (phiCenter <= Real(0)) { // seed inside solid -> no cell
605 for (int t = 0; t < c.nt; ++t)
606 c.alive[t] = false;
607 return true;
608 }
609 // cell circumradius (dual vertices are seed-relative)
610 const Real maxRsq = c.maxVertexRsq();
611 const Real radius = Kokkos::sqrt(maxRsq > 0 ? maxRsq : Real(0));
612 if (phiCenter > radius + tol)
613 return false; // cell fully in fluid
614 // Make room for the wall planes: the committed-plane list carries the clip's redundant
615 // candidates, and maxCuts more would overflow MAXP on a wall-hugging cell (see compactPlanes).
616 if (c.np + maxCuts > MAXP)
617 c.compactPlanes();
618
619 // Rung A1: the un-cut cell is kept for the second-order RE-CLIP (see sdfSagittaShifts), and
620 // every committed plane is recorded with its half-space and tangency point (chord planes get
621 // plane index -1: no tangency, no shift).
622 constexpr bool kSagitta = !detail::tangentOnly_v<Sdf>;
624 if constexpr (kSagitta)
625 saved = c;
626 int tpPlane[kMaxWallPlanes];
627 Real tpSurf[kMaxWallPlanes * 3], tpPv[kMaxWallPlanes * 3], tpOff[kMaxWallPlanes];
628 int nTp = 0;
629
630 bool seedPlaneApplied = false;
631 for (int iter = 0; iter < maxCuts; ++iter) {
632 Real probe[3] = {seed[0], seed[1], seed[2]};
633 Real probePhi = phiCenter;
634 if (seedPlaneApplied) {
635 bool found = false;
636 for (int t = 0; t < c.nt; ++t) {
637 if (!c.alive[t])
638 continue;
639 Real x = seed[0] + c.vx[t], y = seed[1] + c.vy[t], z = seed[2] + c.vz[t];
640 Real phi = sdf.eval(x, y, z);
641 if (!found || phi < probePhi) {
642 probe[0] = x;
643 probe[1] = y;
644 probe[2] = z;
645 probePhi = phi;
646 found = true;
647 }
648 }
649 if (!found || probePhi >= -tol)
650 break;
651 }
652 // the cut plane: tangent at the probe's foot point, sagitta-shifted (rung A1) unless
653 // TangentOnly; in seed-relative coords dist(v) = v·pv − off, vertices in the solid (off the
654 // fluid side) get dist > 0 and are removed.
655 Real pv[3], off, surf[3];
656 if (!sdfCutPlane(c, seed, sdf, probe, radius, pv, off, surf))
657 break;
658 if (off <= Real(0)) {
659 // The tangent plane at the probe's foot point puts the SEED on its solid side: the tangent
660 // approximation is invalid here (surface curvature strong on the scale of the cell — e.g. a
661 // seed hugging a sphere while a far vertex sits inside it near the equator). The foot-point
662 // half-space {n·x <= n·n} cannot even represent such a plane (n·n >= 0): committing it cut
663 // the WRONG side, the violating vertex survived, the same cut was re-applied maxCuts times
664 // and the cell overflowed into a silent zero-volume "dead cell" (the rim/collapse symptom
665 // seen in pore meshing). Cut instead with the CHORD plane: through the surface crossing of
666 // the seed->probe segment, normal along the segment — always a valid half-space that
667 // contains the seed and excludes the probe, so the iteration makes progress.
668 if (!seedPlaneApplied) { // probe == seed: nothing to chord; leave the first cut out
669 seedPlaneApplied = true;
670 continue;
671 }
672 Real d[3] = {probe[0] - seed[0], probe[1] - seed[1], probe[2] - seed[2]};
673 const Real len = Kokkos::sqrt(d[0] * d[0] + d[1] * d[1] + d[2] * d[2]);
674 if (len <= Real(0))
675 break;
676 for (int k = 0; k < 3; ++k)
677 d[k] /= len;
678 Real lo = Real(0), hi = len; // phi(seed) > 0 >= phi(probe)
679 for (int it2 = 0; it2 < 40; ++it2) {
680 const Real mid = Real(0.5) * (lo + hi);
681 const Real ph = sdf.eval(seed[0] + mid * d[0], seed[1] + mid * d[1], seed[2] + mid * d[2]);
682 if (ph > Real(0))
683 lo = mid;
684 else
685 hi = mid;
686 }
687 const Real t = Real(0.5) * (lo + hi);
688 if (t <= Real(0))
689 break;
690 if (c.clip(d, t, kBoundaryFacet) && nTp < kMaxWallPlanes) {
691 tpPlane[nTp] = -1; // chord plane: re-clipped verbatim
692 for (int k = 0; k < 3; ++k) {
693 tpPv[3 * nTp + k] = d[k];
694 tpSurf[3 * nTp + k] = Real(0);
695 }
696 tpOff[nTp] = t;
697 ++nTp;
698 }
699 if (c.empty())
700 break;
701 continue;
702 }
703 const bool committed = c.clip(pv, off, kBoundaryFacet);
704 seedPlaneApplied = true;
705 if (c.empty())
706 break;
707 if (committed) {
708 if (nTp < kMaxWallPlanes) {
709 tpPlane[nTp] = c.np - 1;
710 for (int k = 0; k < 3; ++k) {
711 tpSurf[3 * nTp + k] = surf[k];
712 tpPv[3 * nTp + k] = pv[k];
713 }
714 tpOff[nTp] = off;
715 ++nTp;
716 }
717 } else if (iter > 0) {
718 break; // the violating vertex is not cut by its own plane: no progress possible
719 }
720 }
721 if constexpr (kSagitta) {
722 if (nTp > 0 && !c.empty()) {
723 Real delta[kMaxWallPlanes];
724 if (sdfSagittaShifts(c, seed, sdf, tpPlane, tpSurf, nTp, delta) > 0) {
725 // second pass: the same planes, in the same order, translated into the solid by δ_k
726 c = saved;
727 for (int q = 0; q < nTp; ++q) {
728 const Real pv2[3] = {tpPv[3 * q], tpPv[3 * q + 1], tpPv[3 * q + 2]};
729 c.clip(pv2, tpOff[q] + delta[q], kBoundaryFacet);
730 if (c.empty())
731 break;
732 }
733 }
734 }
735 }
736 return false;
737}
738
747template <class Real, int MAXP, int MAXT, bool TrackAdj, class Sdf>
748KOKKOS_INLINE_FUNCTION void sdfWallFD(const ConvexCell<Real, MAXP, MAXT, TrackAdj>& unclipped,
749 const Real seed[3], const Sdf& sdf, Real h, Real dV[3],
750 Real dA[3]) {
751 for (int d = 0; d < 3; ++d) {
752 Real vpm[2], apm[2];
753 for (int s = 0; s < 2; ++s) {
755 Real sd[3] = {seed[0], seed[1], seed[2]};
756 sd[d] += (s == 0 ? h : -h);
757 clipCellAgainstSdf<Real, MAXP, MAXT, TrackAdj>(c, sd, sdf);
758 vpm[s] = (c.empty() || c.overflow) ? Real(0) : c.volumePerVertex();
759 Real area[MAXP];
760 for (int k = 0; k < c.np; ++k)
761 area[k] = Real(0);
762 if (!(c.empty() || c.overflow))
763 c.facetAreasPerVertex(area);
764 Real aw = Real(0);
765 for (int k = 0; k < c.np; ++k)
766 if (c.pnbr[k] == kBoundaryFacet)
767 aw += area[k];
768 apm[s] = aw;
769 }
770 dV[d] = (vpm[0] - vpm[1]) / (Real(2) * h);
771 dA[d] = (apm[0] - apm[1]) / (Real(2) * h);
772 }
773}
774
775} // namespace peclet::voro
776
777#endif // PECLET_VORO_SDF_HPP
KOKKOS_INLINE_FUNCTION Dual< Real, K > dnum(Real c)
Definition convex_cell.hpp:53
constexpr bool tangentOnly_v
Definition sdf.hpp:55
Device-native Voronoi tessellation, geometry and finite-volume physics.
Definition convex_cell.hpp:41
KOKKOS_INLINE_FUNCTION void sdfHessian(const Sdf &s, Real x, Real y, Real z, Real H[3][3])
Definition sdf.hpp:226
KOKKOS_INLINE_FUNCTION bool sdfWouldClip(const ConvexCell< Real, MAXP, MAXT, TrackAdj > &c, const Real seed[3], const Sdf &sdf)
Definition sdf.hpp:552
KOKKOS_INLINE_FUNCTION void addSdfWallForce(const ConvexCell< Real, MAXP, MAXT, TrackAdj > &c, const Real seed[3], const Sdf &sdf, const Real *gx, const Real *gy, const Real *gz, Real fSelf[3])
Definition sdf.hpp:297
constexpr int kMaxWallPlanes
Definition sdf.hpp:335
KOKKOS_INLINE_FUNCTION void sdfWallFD(const ConvexCell< Real, MAXP, MAXT, TrackAdj > &unclipped, const Real seed[3], const Sdf &sdf, Real h, Real dV[3], Real dA[3])
Definition sdf.hpp:748
constexpr int kBoundaryFacet
Definition sdf.hpp:36
KOKKOS_INLINE_FUNCTION bool clipCellAgainstSdf(ConvexCell< Real, MAXP, MAXT, TrackAdj > &c, const Real seed[3], const Sdf &sdf)
Definition sdf.hpp:599
KOKKOS_INLINE_FUNCTION void sdfGradient(const Sdf &s, Real x, Real y, Real z, Real g[3])
Central-difference gradient of any provider with eval() (matches peclet::core::geom::gradient).
Definition sdf.hpp:216
KOKKOS_INLINE_FUNCTION int sdfSagittaShifts(const ConvexCell< Real, MAXP, MAXT, TrackAdj > &c, const Real seed[3], const Sdf &sdf, const int *planes, const Real *tangency, int nPlanes, Real *delta)
Definition sdf.hpp:438
KOKKOS_INLINE_FUNCTION void sdfWallChain(const Sdf &sdf, Real x, Real y, Real z, const double gw[3], double fSelf[3])
Definition sdf.hpp:253
KOKKOS_INLINE_FUNCTION bool sdfCutPlane(const ConvexCell< Real, MAXP, MAXT, TrackAdj > &c, const Real seed[3], const Sdf &sdf, const Real probe[3], Real radius, Real pv[3], Real &off, Real surf[3])
Definition sdf.hpp:512
Definition convex_cell.hpp:140
KOKKOS_INLINE_FUNCTION Real volumePerVertex() const
Definition convex_cell.hpp:1192
KOKKOS_INLINE_FUNCTION int compactPlanes()
Definition convex_cell.hpp:548
KOKKOS_INLINE_FUNCTION void facetAreasPerVertex(Real *area) const
Definition convex_cell.hpp:1241
bool overflow
set if MAXP/MAXT exceeded -> cell invalid, caller falls back
Definition convex_cell.hpp:157
int nt
triangle high-water mark
Definition convex_cell.hpp:156
Real nn[MAXP]
cached n·n, i.e. the half-space offset (so the cull test is n·x <= nn)
Definition convex_cell.hpp:150
Real n[MAXP][3]
Definition convex_cell.hpp:147
KOKKOS_INLINE_FUNCTION int faceOrdered(int k, Real fx[MAXFV], Real fy[MAXFV], Real fz[MAXFV]) const
Definition convex_cell.hpp:1004
Real vy[MAXT]
Definition convex_cell.hpp:154
KOKKOS_INLINE_FUNCTION bool empty() const
Definition convex_cell.hpp:896
int pnbr[MAXP]
neighbour seed id per plane (<0 => bounding box)
Definition convex_cell.hpp:151
KOKKOS_INLINE_FUNCTION Real maxVertexRsq() const
Largest squared dual-vertex radius over live triangles (drives the security radius).
Definition convex_cell.hpp:591
Real vx[MAXT]
Definition convex_cell.hpp:154
int np
number of planes
Definition convex_cell.hpp:152
KOKKOS_INLINE_FUNCTION bool clip(const Real pdir[3], Real d, int nbr, Real *gapOut=nullptr)
Definition convex_cell.hpp:688
bool alive[MAXT]
triangle live flag
Definition convex_cell.hpp:155
Real vz[MAXT]
cached dual-vertex position per triangle
Definition convex_cell.hpp:154
Sentinel "no geometry" provider — the default; the clip stage is skipped.
Definition sdf.hpp:39
Definition sdf.hpp:76
Real hx
Definition sdf.hpp:77
KOKKOS_INLINE_FUNCTION Real gradH() const
Definition sdf.hpp:82
Real cz
Definition sdf.hpp:77
Real cy
Definition sdf.hpp:77
Real cx
Definition sdf.hpp:77
Real hz
Definition sdf.hpp:77
Real hy
Definition sdf.hpp:77
KOKKOS_INLINE_FUNCTION Real eval(Real x, Real y, Real z) const
Definition sdf.hpp:78
Definition sdf.hpp:119
Kokkos::View< const float *, peclet::core::MemSpace > values
Definition sdf.hpp:120
static KOKKOS_INLINE_FUNCTION SdfGrid fromSpacing(Kokkos::View< const float *, peclet::core::MemSpace > v, int nx, int ny, int nz, Real ox, Real oy, Real oz, Real sx, Real sy, Real sz)
Definition sdf.hpp:130
peclet::core::geom::GridDesc< Real > desc
Definition sdf.hpp:121
KOKKOS_INLINE_FUNCTION Real gradH() const
Definition sdf.hpp:150
KOKKOS_INLINE_FUNCTION Real eval(Real x, Real y, Real z) const
Definition sdf.hpp:147
Definition sdf.hpp:92
KOKKOS_INLINE_FUNCTION Real gradH() const
Definition sdf.hpp:102
Real cx
Definition sdf.hpp:93
Real rInner
Definition sdf.hpp:93
int axis
Definition sdf.hpp:94
Real height
Definition sdf.hpp:93
Real cy
Definition sdf.hpp:93
Real rOuter
Definition sdf.hpp:93
Real cz
Definition sdf.hpp:93
KOKKOS_INLINE_FUNCTION Real eval(Real x, Real y, Real z) const
Definition sdf.hpp:95
Definition sdf.hpp:169
KOKKOS_INLINE_FUNCTION Real gradH() const
Definition sdf.hpp:181
int nodeCount
Definition sdf.hpp:173
int root
Definition sdf.hpp:174
KOKKOS_INLINE_FUNCTION Real eval(Real x, Real y, Real z) const
Definition sdf.hpp:177
Kokkos::View< const float *, peclet::core::MemSpace > samples
Definition sdf.hpp:172
Kokkos::View< const peclet::core::geom::GridDesc< Real > *, peclet::core::MemSpace > grids
Definition sdf.hpp:171
Kokkos::View< const peclet::core::geom::ShapeNode< Real > *, peclet::core::MemSpace > nodes
Definition sdf.hpp:170
Real h
Definition sdf.hpp:175
Definition sdf.hpp:62
Real radius
Definition sdf.hpp:63
Real cy
Definition sdf.hpp:63
Real cz
Definition sdf.hpp:63
KOKKOS_INLINE_FUNCTION Real gradH() const
Definition sdf.hpp:68
KOKKOS_INLINE_FUNCTION Real eval(Real x, Real y, Real z) const
Definition sdf.hpp:64
Real cx
Definition sdf.hpp:63
Definition sdf.hpp:191
int n
Definition sdf.hpp:194
Real L
Definition sdf.hpp:195
KOKKOS_INLINE_FUNCTION Real eval(Real x, Real y, Real z) const
Definition sdf.hpp:196
Kokkos::View< const Real *, Mem > cen
Definition sdf.hpp:192
KOKKOS_INLINE_FUNCTION Real gradH() const
Definition sdf.hpp:211
Kokkos::View< const Real *, Mem > rad
Definition sdf.hpp:193
Definition sdf.hpp:45
static constexpr bool kTangentOnly
Definition sdf.hpp:46
Definition sdf.hpp:352
peclet::core::MemSpace MemSpace
Definition sdf.hpp:353
void alloc(int n)
Definition sdf.hpp:358
KOKKOS_INLINE_FUNCTION bool active() const
Definition sdf.hpp:364
KOKKOS_INLINE_FUNCTION int save(int i, const Cell &c) const
Definition sdf.hpp:369
Kokkos::View< int *, MemSpace > cnt
Definition sdf.hpp:356
static constexpr int kMax
Definition sdf.hpp:354
Kokkos::View< Real *, MemSpace > rec
Definition sdf.hpp:355
KOKKOS_INLINE_FUNCTION bool load(int i, Cell &c, Real dx, Real dy, Real dz) const
Definition sdf.hpp:393