peclet.voro 1.0.0
Device-native moving-particle Voronoi dynamics
Loading...
Searching...
No Matches
convex_cell.hpp
Go to the documentation of this file.
1
24#ifndef PECLET_VORO_CONVEX_CELL_HPP
25#define PECLET_VORO_CONVEX_CELL_HPP
26
27#include <Kokkos_Core.hpp>
28
29#include "peclet/voro/plane_policy.hpp" // Voronoi/Power plane-from-DOF policies (leaf: Kokkos only)
30
31// Loop unroll hint, applied ONLY in the CUDA/HIP device passes (where it lets the compiler
32// scalarize small dynamically-indexed local arrays into registers); a no-op on the host so gcc sees
33// no unknown pragma.
34#if defined(__CUDA_ARCH__) || defined(__HIP_DEVICE_COMPILE__)
35#define VOR_UNROLL _Pragma("unroll")
36#else
37#define VOR_UNROLL
38#endif
39
40namespace peclet::voro {
41
42namespace detail {
43// Minimal forward-mode dual number (value + K partials) used by ConvexCell::geomVolumeAreaGrad (the
44// geomFull area-Jacobian). Templated on Real so it routes through nvcc/hipcc like the rest of the
45// header.
46template <class Real, int K>
47struct Dual {
48 Real v;
49 Real d[K];
50};
51template <class Real, int K>
52KOKKOS_INLINE_FUNCTION Dual<Real, K> dnum(Real c) {
54 r.v = c;
55 for (int i = 0; i < K; ++i)
56 r.d[i] = Real(0);
57 return r;
58}
59template <class Real, int K>
60KOKKOS_INLINE_FUNCTION Dual<Real, K> dseed(Real c, int s) {
62 r.d[s] = Real(1);
63 return r;
64}
65template <class Real, int K>
68 r.v = a.v + b.v;
69 for (int i = 0; i < K; ++i)
70 r.d[i] = a.d[i] + b.d[i];
71 return r;
72}
73template <class Real, int K>
76 r.v = a.v - b.v;
77 for (int i = 0; i < K; ++i)
78 r.d[i] = a.d[i] - b.d[i];
79 return r;
80}
81template <class Real, int K>
84 r.v = a.v * b.v;
85 for (int i = 0; i < K; ++i)
86 r.d[i] = a.d[i] * b.v + a.v * b.d[i];
87 return r;
88}
89template <class Real, int K>
92 const Real inv = Real(1) / b.v;
93 r.v = a.v * inv;
94 for (int i = 0; i < K; ++i)
95 r.d[i] = (a.d[i] - r.v * b.d[i]) * inv;
96 return r;
97}
98template <class Real, int K>
101 r.v = Kokkos::sqrt(a.v);
102 const Real inv = (r.v > Real(0)) ? Real(1) / (Real(2) * r.v) : Real(0);
103 for (int i = 0; i < K; ++i)
104 r.d[i] = a.d[i] * inv;
105 return r;
106}
107template <class Real, int K>
109 return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
110}
111template <class Real, int K>
113 Dual<Real, K> o[3]) {
114 o[0] = a[1] * b[2] - a[2] * b[1];
115 o[1] = a[2] * b[0] - a[0] * b[2];
116 o[2] = a[0] * b[1] - a[1] * b[0];
117}
118template <class Real, int K>
120 const Dual<Real, K> c[3]) {
121 Dual<Real, K> bc[3];
122 dcross(b, c, bc);
123 return ddot(a, bc);
124}
125template <class Real, int K>
127 Dual<Real, K> f[3]) {
128 const Dual<Real, K> s = ddot(v, c) / ddot(c, c);
129 f[0] = v[0] - s * c[0];
130 f[1] = v[1] - s * c[1];
131 f[2] = v[2] - s * c[2];
132}
133} // namespace detail
134
138template <class Real, int MAXP = 64, int MAXT = 96, bool TrackAdj = false>
140 static_assert(MAXP <= 255, "plane index must fit in unsigned char");
141 static constexpr bool kTrackAdj =
142 TrackAdj;
143 Real n[MAXP]
144 [3];
147 Real nn[MAXP];
148 int pnbr[MAXP];
149 int np;
150 unsigned char t0[MAXT], t1[MAXT], t2[MAXT];
151 Real vx[MAXT], vy[MAXT], vz[MAXT];
152 bool alive[MAXT];
153 int nt;
154 bool overflow;
155
169 Kokkos::Array<int, (TrackAdj ? MAXT : 0) * 3> adj;
170
173 KOKKOS_INLINE_FUNCTION void initBox(Real L0, Real L1, Real L2) {
174 const Real h[3] = {Real(0.5) * L0, Real(0.5) * L1, Real(0.5) * L2};
175 for (int ax = 0; ax < 3; ++ax) {
176 for (int s = 0; s < 2; ++s) {
177 const int k = 2 * ax + s;
178 n[k][0] = n[k][1] = n[k][2] = 0;
179 n[k][ax] = (s == 0) ? h[ax] : -h[ax]; // foot point = (±h) e_ax
180 nn[k] = h[ax] * h[ax];
181 pnbr[k] = -1;
182 }
183 }
184 np = 6;
185 nt = 0;
186 overflow = false;
187 // 8 corners: x-plane (0|1), y-plane (2|3), z-plane (4|5)
188 for (int sx = 0; sx < 2; ++sx)
189 for (int sy = 0; sy < 2; ++sy)
190 for (int sz = 0; sz < 2; ++sz) {
191 t0[nt] = (unsigned char)(0 + sx);
192 t1[nt] = (unsigned char)(2 + sy);
193 t2[nt] = (unsigned char)(4 + sz);
194 alive[nt] = true;
196 ++nt;
197 }
198 if constexpr (TrackAdj)
199 rebuildAdjacency(); // one-time bootstrap (~12 box triangles)
200 }
201
206 KOKKOS_INLINE_FUNCTION void computeVertex(int t) {
207 const Real* a = n[t0[t]];
208 const Real* b = n[t1[t]];
209 const Real* c = n[t2[t]];
210 const Real da = nn[t0[t]], db = nn[t1[t]], dc = nn[t2[t]];
211 const Real bc[3] = {b[1] * c[2] - b[2] * c[1], b[2] * c[0] - b[0] * c[2],
212 b[0] * c[1] - b[1] * c[0]};
213 const Real ca[3] = {c[1] * a[2] - c[2] * a[1], c[2] * a[0] - c[0] * a[2],
214 c[0] * a[1] - c[1] * a[0]};
215 const Real ab[3] = {a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2],
216 a[0] * b[1] - a[1] * b[0]};
217 const Real det = a[0] * bc[0] + a[1] * bc[1] + a[2] * bc[2];
218 const Real inv = det != Real(0) ? Real(1) / det : Real(0);
219 vx[t] = (da * bc[0] + db * ca[0] + dc * ab[0]) * inv;
220 vy[t] = (da * bc[1] + db * ca[1] + dc * ab[1]) * inv;
221 vz[t] = (da * bc[2] + db * ca[2] + dc * ab[2]) * inv;
222 }
223
228 KOKKOS_INLINE_FUNCTION void initBoxPlanes(Real L0, Real L1, Real L2) {
229 const Real h[3] = {Real(0.5) * L0, Real(0.5) * L1, Real(0.5) * L2};
230 for (int ax = 0; ax < 3; ++ax)
231 for (int s = 0; s < 2; ++s) {
232 const int k = 2 * ax + s;
233 n[k][0] = n[k][1] = n[k][2] = 0;
234 n[k][ax] = (s == 0) ? h[ax] : -h[ax]; // foot point = (±h) e_ax
235 nn[k] = h[ax] * h[ax];
236 pnbr[k] = -1;
237 }
238 np = 6;
239 overflow = false;
240 }
241
254 template <class Policy = Voronoi>
255 KOKKOS_INLINE_FUNCTION void reevalGeometry(Real sx, Real sy, Real sz, const Real* pos, Real L,
256 Real wSelf = Real(0), const Real* weight = nullptr) {
257 const Real Lh = Real(0.5) * L;
258 for (int k = 0; k < np; ++k) {
259 if (pnbr[k] < 0)
260 continue; // bounding-box / SDF-wall plane — does not move
261 const int g = pnbr[k];
262 Real rx = pos[3 * g] - sx, ry = pos[3 * g + 1] - sy, rz = pos[3 * g + 2] - sz;
263 rx = rx > Lh ? rx - L : (rx < -Lh ? rx + L : rx);
264 ry = ry > Lh ? ry - L : (ry < -Lh ? ry + L : ry);
265 rz = rz > Lh ? rz - L : (rz < -Lh ? rz + L : rz);
266 if constexpr (Policy::kHasWeightDof) {
267 // radical foot point: n = (d/|r|²) r, d = ½(|r|² + w_self − w_nbr)
268 const Real r[3] = {rx, ry, rz};
269 const Real rho = rx * rx + ry * ry + rz * rz;
270 const Real off = Policy::template offsetFromRel<Real>(r, wSelf, weight[g]);
271 const Real a = rho > Real(0) ? off / rho : Real(0);
272 n[k][0] = a * rx;
273 n[k][1] = a * ry;
274 n[k][2] = a * rz;
275 nn[k] = n[k][0] * n[k][0] + n[k][1] * n[k][1] + n[k][2] * n[k][2];
276 } else {
277 // foot point of the Voronoi bisector: n = ½r, offset nn = |n|² = ¼|r|²
278 const Real hx = Real(0.5) * rx, hy = Real(0.5) * ry, hz = Real(0.5) * rz;
279 n[k][0] = hx;
280 n[k][1] = hy;
281 n[k][2] = hz;
282 nn[k] = hx * hx + hy * hy + hz * hz;
283 }
284 }
285 for (int t = 0; t < nt; ++t)
286 if (alive[t])
287 computeVertex(t);
288 }
289
300 KOKKOS_INLINE_FUNCTION bool isSelfConsistent(Real tol) const {
301 // Branch-free max-slack accumulation (no per-thread early-out divergence): the whole-cell worst
302 // perpendicular poke is compared to `tol` once at the end. The brute scan is the complete
303 // certificate — it catches far-pokes by NON-adjacent planes too, which the local (Lawson) form
304 // deliberately does not. (An early-out variant would only help when np is large AND fail-fast
305 // rate is high; measure first.)
306 Real maxd = Real(0);
307 for (int t = 0; t < nt; ++t) {
308 if (!alive[t])
309 continue;
310 const Real v[3] = {vx[t], vy[t], vz[t]};
311 for (int k = 0; k < np; ++k) {
312 if (t0[t] == k || t1[t] == k || t2[t] == k)
313 continue; // a plane that defines this vertex
314 const Real s = n[k][0] * v[0] + n[k][1] * v[1] + n[k][2] * v[2] - nn[k];
315 if (s >
316 Real(0)) { // v outside plane k; convert to a perpendicular distance for the tol test
317 const Real invlen = (nn[k] > Real(0)) ? Real(1) / Kokkos::sqrt(nn[k]) : Real(0);
318 maxd = Kokkos::max(maxd, s * invlen);
319 }
320 }
321 }
322 return maxd <= tol;
323 }
324
342 KOKKOS_INLINE_FUNCTION bool isSelfConsistentPartners(Real tol, int* outPartners, int maxPartners,
343 int& nPartners) const {
344 nPartners = 0;
345 bool consistent = true;
346 for (int t = 0; t < nt; ++t) {
347 if (!alive[t])
348 continue;
349 const Real v[3] = {vx[t], vy[t], vz[t]};
350 for (int k = 0; k < np; ++k) {
351 if (t0[t] == k || t1[t] == k || t2[t] == k)
352 continue; // a plane that defines this vertex
353 const Real s = n[k][0] * v[0] + n[k][1] * v[1] + n[k][2] * v[2] - nn[k];
354 if (s > Real(0)) { // v outside plane k; perpendicular distance vs tol
355 const Real invlen = (nn[k] > Real(0)) ? Real(1) / Kokkos::sqrt(nn[k]) : Real(0);
356 if (s * invlen > tol) {
357 consistent = false;
358 const int part = pnbr[k];
359 if (part >= 0) { // box/SDF planes (pnbr<0) have no partner cell
360 bool seen = false;
361 for (int q = 0; q < nPartners; ++q)
362 if (outPartners[q] == part) {
363 seen = true;
364 break;
365 }
366 if (!seen && nPartners < maxPartners)
367 outPartners[nPartners++] = part;
368 }
369 }
370 }
371 }
372 }
373 return consistent;
374 }
375
378 KOKKOS_INLINE_FUNCTION int oppositePlane(int s, int x, int y) const {
379 const int a = t0[s], b = t1[s], c = t2[s];
380 return (a != x && a != y) ? a : (b != x && b != y) ? b : c;
381 }
382
391 KOKKOS_INLINE_FUNCTION void computePoke4(unsigned char* out) const {
392 static_assert(TrackAdj, "computePoke4 requires TrackAdj");
393 for (int t = 0; t < nt; ++t) {
394 if (!alive[t]) {
395 out[t * 4 + 0] = out[t * 4 + 1] = out[t * 4 + 2] = out[t * 4 + 3] = 0xff;
396 continue;
397 }
398 const int vt[3] = {t0[t], t1[t], t2[t]};
399 int eo[3];
400 for (int e = 0; e < 3; ++e) {
401 const int s = adj[t * 3 + e];
402 eo[e] = (s >= 0 && s < nt && alive[s]) ? oppositePlane(s, vt[e], vt[(e + 1) % 3]) : 0xff;
403 out[t * 4 + e] = (unsigned char)(eo[e] & 0xff);
404 }
405 const Real v[3] = {vx[t], vy[t], vz[t]};
406 Real best = Real(3.4e38);
407 int g = 0xff;
408 for (int k = 0; k < np; ++k) {
409 if (k == vt[0] || k == vt[1] || k == vt[2])
410 continue; // defining plane
411 if (k == eo[0] || k == eo[1] || k == eo[2])
412 continue; // already an edge-opposite
413 const Real slack = nn[k] - (n[k][0] * v[0] + n[k][1] * v[1] + n[k][2] * v[2]);
414 if (slack >= Real(0) && slack < best) {
415 best = slack;
416 g = k;
417 }
418 }
419 out[t * 4 + 3] = (unsigned char)(g & 0xff);
420 }
421 }
422
429 KOKKOS_INLINE_FUNCTION bool isLocallyConvex(const unsigned char* poke4, Real tol) const {
430 Real maxd = Real(0);
431 for (int t = 0; t < nt; ++t) {
432 if (!alive[t])
433 continue;
434 const Real v[3] = {vx[t], vy[t], vz[t]};
435 for (int j = 0; j < 4; ++j) {
436 const int k = (int)poke4[t * 4 + j];
437 if (k == 0xff)
438 continue;
439 const Real sl = n[k][0] * v[0] + n[k][1] * v[1] + n[k][2] * v[2] - nn[k];
440 if (sl > Real(0)) {
441 const Real invlen = (nn[k] > Real(0)) ? Real(1) / Kokkos::sqrt(nn[k]) : Real(0);
442 maxd = Kokkos::max(maxd, sl * invlen);
443 }
444 }
445 }
446 return maxd <= tol;
447 }
448
453 KOKKOS_INLINE_FUNCTION bool isLocallyConvexPartners(const unsigned char* poke4, Real tol,
454 int* outPartners, int maxPartners,
455 int& nPartners) const {
456 nPartners = 0;
457 bool consistent = true;
458 for (int t = 0; t < nt; ++t) {
459 if (!alive[t])
460 continue;
461 const Real v[3] = {vx[t], vy[t], vz[t]};
462 for (int j = 0; j < 4; ++j) {
463 const int k = (int)poke4[t * 4 + j];
464 if (k == 0xff)
465 continue;
466 const Real sl = n[k][0] * v[0] + n[k][1] * v[1] + n[k][2] * v[2] - nn[k];
467 if (sl > Real(0)) {
468 const Real invlen = (nn[k] > Real(0)) ? Real(1) / Kokkos::sqrt(nn[k]) : Real(0);
469 if (sl * invlen > tol) {
470 consistent = false;
471 const int part = pnbr[k];
472 if (part >= 0) {
473 bool seen = false;
474 for (int q = 0; q < nPartners; ++q)
475 if (outPartners[q] == part) {
476 seen = true;
477 break;
478 }
479 if (!seen && nPartners < maxPartners)
480 outPartners[nPartners++] = part;
481 }
482 }
483 }
484 }
485 }
486 return consistent;
487 }
488
490 KOKKOS_INLINE_FUNCTION Real maxVertexRsq() const {
491 Real m = 0;
492 for (int t = 0; t < nt; ++t) {
493 if (!alive[t])
494 continue;
495 const Real r = vx[t] * vx[t] + vy[t] * vy[t] + vz[t] * vz[t];
496 if (r > m)
497 m = r;
498 }
499 return m;
500 }
501
508 KOKKOS_INLINE_FUNCTION int findSharing(int self, int x, int y) const {
509 for (int s = 0; s < nt; ++s) {
510 if (s == self || !alive[s])
511 continue;
512 const int a = t0[s], b = t1[s], c = t2[s];
513 const bool hasX = (a == x || b == x || c == x);
514 const bool hasY = (a == y || b == y || c == y);
515 if (hasX && hasY)
516 return s;
517 }
518 return -1;
519 }
520
523 KOKKOS_INLINE_FUNCTION void rebuildAdjacency() {
524 if constexpr (TrackAdj) {
525 for (int t = 0; t < nt; ++t) {
526 if (!alive[t])
527 continue;
528 const int vt[3] = {t0[t], t1[t], t2[t]};
529 for (int e = 0; e < 3; ++e)
530 adj[t * 3 + e] = findSharing(t, vt[e], vt[(e + 1) % 3]);
531 }
532 }
533 }
534
539 KOKKOS_INLINE_FUNCTION bool checkAdjacencyInvariant() const {
540 if constexpr (!TrackAdj) {
541 return true;
542 } else {
543 for (int t = 0; t < nt; ++t) {
544 if (!alive[t])
545 continue;
546 const int vt[3] = {t0[t], t1[t], t2[t]};
547 for (int e = 0; e < 3; ++e) {
548 const int x = vt[e], y = vt[(e + 1) % 3];
549 const int s = adj[t * 3 + e];
550 if (s < 0 || s >= nt || !alive[s])
551 return false; // live target
552 const int vs[3] = {t0[s], t1[s], t2[s]};
553 const bool hasX = (vs[0] == x || vs[1] == x || vs[2] == x);
554 const bool hasY = (vs[0] == y || vs[1] == y || vs[2] == y);
555 if (!(hasX && hasY))
556 return false; // shares the edge
557 bool back = false; // symmetry
558 for (int f = 0; f < 3; ++f)
559 if (adj[s * 3 + f] == t)
560 back = true;
561 if (!back)
562 return false;
563 }
564 }
565 return true;
566 }
567 }
568
569 KOKKOS_INLINE_FUNCTION int allocTri() {
570 for (int s = 0; s < nt; ++s)
571 if (!alive[s])
572 return s;
573 if (nt < MAXT)
574 return nt++;
575 overflow = true;
576 return -1;
577 }
578
584 KOKKOS_INLINE_FUNCTION bool clip(const Real pdir[3], Real d, int nbr) {
585 if (np >= MAXP) {
586 overflow = true;
587 return false;
588 }
589 const int pi = np; // tentative index
590 const Real l2 = pdir[0] * pdir[0] + pdir[1] * pdir[1] + pdir[2] * pdir[2];
591 const Real a = (l2 > Real(0)) ? d / l2 : Real(0); // foot scale: nf = a·pdir
592 n[pi][0] = a * pdir[0];
593 n[pi][1] = a * pdir[1];
594 n[pi][2] = a * pdir[2];
595 nn[pi] = n[pi][0] * n[pi][0] + n[pi][1] * n[pi][1] + n[pi][2] * n[pi][2];
596 pnbr[pi] = nbr;
597
598 // Mark triangles whose dual vertex is outside the new half-space (nf·v > nf·nf).
599 bool kill[MAXT];
600 bool any = false;
601 for (int t = 0; t < nt; ++t) {
602 kill[t] = false;
603 if (!alive[t])
604 continue;
605 const Real s = n[pi][0] * vx[t] + n[pi][1] * vy[t] + n[pi][2] * vz[t] - nn[pi];
606 if (s > 0) {
607 kill[t] = true;
608 any = true;
609 }
610 }
611 if (!any)
612 return false; // candidate does not cut -> no-op, plane not committed
613
614 np = pi + 1; // commit the plane
615
616 if constexpr (!TrackAdj) {
617 // ---- Lean path (unchanged): collect the horizon by findSharing, then retriangulate. ----
618 // For each killed triangle edge whose sharing triangle is alive, the new plane gets a
619 // triangle (x, y, pi). Collect first, then mutate.
620 unsigned char nA[MAXT], nB[MAXT];
621 int nnew = 0;
622 for (int t = 0; t < nt; ++t) {
623 if (!kill[t])
624 continue;
625 const int vtx[3] = {t0[t], t1[t], t2[t]};
626 for (int e = 0; e < 3; ++e) {
627 const int x = vtx[e], y = vtx[(e + 1) % 3];
628 const int other = findSharing(t, x, y);
629 if (other >= 0 && !kill[other]) {
630 if (nnew < MAXT) {
631 nA[nnew] = (unsigned char)x;
632 nB[nnew] = (unsigned char)y;
633 ++nnew;
634 } else {
635 overflow = true;
636 }
637 }
638 }
639 }
640 // Remove killed triangles.
641 for (int t = 0; t < nt; ++t)
642 if (kill[t])
643 alive[t] = false;
644 // Add the new fan around plane pi.
645 for (int i = 0; i < nnew; ++i) {
646 const int slot = allocTri();
647 if (slot < 0)
648 break;
649 t0[slot] = nA[i];
650 t1[slot] = nB[i];
651 t2[slot] = (unsigned char)pi;
652 alive[slot] = true;
653 computeVertex(slot);
654 }
655 } else {
656 // ---- TrackAdj path: walk the conflict-region boundary as ONE oriented cycle via `adj`
657 // (O(conflict), no findSharing), then stitch the new fan's adjacency in O(1) per edge. The
658 // conflict region (vertices outside the new half-space) is connected and its boundary is a
659 // single cycle for a convex clip. ----
660 int hA[MAXT], hB[MAXT], hSurv[MAXT],
661 hBack[MAXT]; // horizon edge {A,B}, survivor across it, slot back
662 int H = 0;
663 // Start edge: a killed triangle with a surviving neighbour across some slot.
664 int t0k = -1, e0 = -1;
665 for (int t = 0; t < nt && t0k < 0; ++t) {
666 if (!kill[t])
667 continue;
668 for (int e = 0; e < 3; ++e) {
669 const int s = adj[t * 3 + e];
670 if (s >= 0 && alive[s] && !kill[s]) {
671 t0k = t;
672 e0 = e;
673 break;
674 }
675 }
676 }
677 if (t0k < 0) {
678 overflow = true;
679 return true;
680 } // no surviving boundary (cannot happen for a real cut)
681 const int vstart[3] = {t0[t0k], t1[t0k], t2[t0k]};
682 int A = vstart[e0], B = vstart[(e0 + 1) % 3];
683 int tt = t0k, ee = e0;
684 const int guardMax = 6 * nt + 12;
685 int guard = 0;
686 do {
687 const int surv = adj[tt * 3 + ee];
688 int back = 0;
689 for (int f = 0; f < 3; ++f)
690 if (adj[surv * 3 + f] == tt) {
691 back = f;
692 break;
693 }
694 if (H < MAXT) {
695 hA[H] = A;
696 hB[H] = B;
697 hSurv[H] = surv;
698 hBack[H] = back;
699 ++H;
700 } else {
701 overflow = true;
702 break;
703 }
704 // Pivot around vertex B, rotating through killed triangles, until we exit to the next
705 // survivor.
706 const int pivot = B;
707 int rt = tt, arrival = ee, rotGuard = 0;
708 while (true) {
709 const int rv[3] = {t0[rt], t1[rt], t2[rt]};
710 const int lv = (rv[0] == pivot) ? 0
711 : (rv[1] == pivot) ? 1
712 : 2; // local index of pivot in rt
713 const int crossSlot = (arrival == lv) ? (lv + 2) % 3 : lv; // the OTHER B-edge of rt
714 const int rn = adj[rt * 3 + crossSlot];
715 if (rn >= 0 && kill[rn]) { // still inside the conflict region: keep rotating around B
716 int f = (adj[rn * 3 + 0] == rt) ? 0 : (adj[rn * 3 + 1] == rt) ? 1 : 2;
717 rt = rn;
718 arrival = f;
719 if (++rotGuard > guardMax) {
720 overflow = true;
721 break;
722 }
723 continue;
724 }
725 // rn survives -> edge crossSlot of rt is the next horizon edge, oriented out of the
726 // pivot.
727 const int u = rv[crossSlot], w = rv[(crossSlot + 1) % 3];
728 A = pivot;
729 B = (u == pivot) ? w : u;
730 tt = rt;
731 ee = crossSlot;
732 break;
733 }
734 if (overflow)
735 break;
736 if (++guard > guardMax) {
737 overflow = true;
738 break;
739 }
740 } while (!(tt == t0k && ee == e0));
741
742 // Remove killed triangles (frees their slots for the fan via allocTri).
743 for (int t = 0; t < nt; ++t)
744 if (kill[t])
745 alive[t] = false;
746 // Allocate the whole fan first (so τ_{i±1} cross-references resolve), then set topology +
747 // adjacency.
748 int tau[MAXT];
749 bool ok = !overflow;
750 for (int i = 0; i < H && ok; ++i) {
751 tau[i] = allocTri();
752 if (tau[i] < 0)
753 ok = false;
754 else
755 alive[tau[i]] = true; // reserve the slot so allocTri doesn't hand it out again
756 }
757 if (ok) {
758 for (int i = 0; i < H; ++i) {
759 const int s = tau[i];
760 t0[s] = (unsigned char)hA[i];
761 t1[s] = (unsigned char)hB[i];
762 t2[s] = (unsigned char)pi;
763 alive[s] = true;
764 computeVertex(s);
765 adj[s * 3 + 0] = hSurv[i]; // slot 0 = edge {A_i,B_i} -> survivor across the horizon
766 adj[s * 3 + 1] = tau[(i + 1) % H]; // slot 1 = edge {B_i,P} -> next fan triangle
767 adj[s * 3 + 2] = tau[(i - 1 + H) % H]; // slot 2 = edge {P,A_i} -> prev fan triangle
768 adj[hSurv[i] * 3 + hBack[i]] = s; // back-patch the survivor's slot to this fan triangle
769 }
770 }
771 }
772 return true;
773 }
774
775 KOKKOS_INLINE_FUNCTION bool empty() const {
776 for (int t = 0; t < nt; ++t)
777 if (alive[t])
778 return false;
779 return true;
780 }
781
782 KOKKOS_INLINE_FUNCTION int countFaces() const {
783 int nf = 0;
784 for (int k = 0; k < np; ++k) {
785 bool used = false;
786 for (int t = 0; t < nt && !used; ++t)
787 if (alive[t] && (t0[t] == k || t1[t] == k || t2[t] == k))
788 used = true;
789 if (used)
790 ++nf;
791 }
792 return nf;
793 }
794
795 static constexpr int MAXFV = 28; // max vertices on one face polygon
796
801 KOKKOS_INLINE_FUNCTION int faceOrdered(int k, Real fx[MAXFV], Real fy[MAXFV],
802 Real fz[MAXFV]) const {
803 int m = 0;
804 for (int t = 0; t < nt; ++t) {
805 if (!alive[t])
806 continue;
807 if (t0[t] != k && t1[t] != k && t2[t] != k)
808 continue;
809 if (m < MAXFV) {
810 fx[m] = vx[t];
811 fy[m] = vy[t];
812 fz[m] = vz[t];
813 ++m;
814 }
815 }
816 if (m < 3)
817 return m;
818 const Real nx = n[k][0], ny = n[k][1], nz = n[k][2];
819 const Real nlen = Kokkos::sqrt(nx * nx + ny * ny + nz * nz);
820 if (nlen == Real(0))
821 return 0;
822 const Real un[3] = {nx / nlen, ny / nlen, nz / nlen};
823 Real e1[3];
824 if (Kokkos::fabs(un[0]) <= Kokkos::fabs(un[1]) && Kokkos::fabs(un[0]) <= Kokkos::fabs(un[2])) {
825 e1[0] = 0;
826 e1[1] = -un[2];
827 e1[2] = un[1];
828 } else if (Kokkos::fabs(un[1]) <= Kokkos::fabs(un[2])) {
829 e1[0] = -un[2];
830 e1[1] = 0;
831 e1[2] = un[0];
832 } else {
833 e1[0] = -un[1];
834 e1[1] = un[0];
835 e1[2] = 0;
836 }
837 const Real e1l = Kokkos::sqrt(e1[0] * e1[0] + e1[1] * e1[1] + e1[2] * e1[2]);
838 e1[0] /= e1l;
839 e1[1] /= e1l;
840 e1[2] /= e1l;
841 const Real e2[3] = {un[1] * e1[2] - un[2] * e1[1], un[2] * e1[0] - un[0] * e1[2],
842 un[0] * e1[1] - un[1] * e1[0]};
843 Real cx = 0, cy = 0, cz = 0;
844 for (int i = 0; i < m; ++i) {
845 cx += fx[i];
846 cy += fy[i];
847 cz += fz[i];
848 }
849 cx /= m;
850 cy /= m;
851 cz /= m;
852 Real ang[MAXFV];
853 for (int i = 0; i < m; ++i) {
854 const Real dx = fx[i] - cx, dy = fy[i] - cy, dz = fz[i] - cz;
855 const Real px = dx * e1[0] + dy * e1[1] + dz * e1[2]; // in-plane coords
856 const Real py = dx * e2[0] + dy * e2[1] + dz * e2[2];
857 // diamond pseudo-angle: monotonic in true angle, in [0,4), NO atan2 (transcendental)
858 const Real s = Kokkos::fabs(px) + Kokkos::fabs(py);
859 const Real t = (s > Real(0)) ? py / s : Real(0); // [-1,1]
860 ang[i] = (px < Real(0)) ? (Real(2) - t) : (py < Real(0) ? Real(4) + t : t);
861 }
862 for (int i = 1; i < m; ++i) {
863 Real ka = ang[i], kx = fx[i], ky = fy[i], kz = fz[i];
864 int j = i - 1;
865 while (j >= 0 && ang[j] > ka) {
866 ang[j + 1] = ang[j];
867 fx[j + 1] = fx[j];
868 fy[j + 1] = fy[j];
869 fz[j + 1] = fz[j];
870 --j;
871 }
872 ang[j + 1] = ka;
873 fx[j + 1] = kx;
874 fy[j + 1] = ky;
875 fz[j + 1] = kz;
876 }
877 return m;
878 }
879
881 KOKKOS_INLINE_FUNCTION static void polyAreaVec(const Real fx[], const Real fy[], const Real fz[],
882 int m, Real A[3]) {
883 Real ax = 0, ay = 0, az = 0;
884 for (int i = 0; i < m; ++i) {
885 const int j = (i + 1 == m) ? 0 : i + 1;
886 ax += fy[i] * fz[j] - fz[i] * fy[j];
887 ay += fz[i] * fx[j] - fx[i] * fz[j];
888 az += fx[i] * fy[j] - fy[i] * fx[j];
889 }
890 A[0] = Real(0.5) * ax;
891 A[1] = Real(0.5) * ay;
892 A[2] = Real(0.5) * az;
893 }
894
895 static constexpr int MAXSV = 2 * MAXFV;
896
905 KOKKOS_INLINE_FUNCTION int sectionPolygon(const Real p0[3], const Real u[3], Real px[MAXSV],
906 Real py[MAXSV], Real pz[MAXSV]) const {
907 int m = 0;
908 for (int i = 0; i < nt; ++i) {
909 if (!alive[i])
910 continue;
911 const Real di = (vx[i] - p0[0]) * u[0] + (vy[i] - p0[1]) * u[1] + (vz[i] - p0[2]) * u[2];
912 const unsigned char a0 = t0[i], a1 = t1[i], a2 = t2[i];
913 for (int j = i + 1; j < nt; ++j) {
914 if (!alive[j])
915 continue;
916 const unsigned char b0 = t0[j], b1 = t1[j], b2 = t2[j];
917 const int common = (a0 == b0) + (a0 == b1) + (a0 == b2) + (a1 == b0) + (a1 == b1) +
918 (a1 == b2) + (a2 == b0) + (a2 == b1) + (a2 == b2);
919 if (common != 2)
920 continue; // not an edge (a primal edge = two triangles sharing exactly two planes)
921 const Real dj = (vx[j] - p0[0]) * u[0] + (vy[j] - p0[1]) * u[1] + (vz[j] - p0[2]) * u[2];
922 if ((di > Real(0)) == (dj > Real(0)))
923 continue; // both endpoints on the same side -> edge doesn't cross the plane
924 const Real denom = di - dj;
925 if (denom == Real(0))
926 continue;
927 const Real tt = di / denom;
928 if (m >= MAXSV)
929 return -1;
930 px[m] = vx[i] + tt * (vx[j] - vx[i]);
931 py[m] = vy[i] + tt * (vy[j] - vy[i]);
932 pz[m] = vz[i] + tt * (vz[j] - vz[i]);
933 ++m;
934 }
935 }
936 if (m < 3)
937 return m;
938 const Real ulen = Kokkos::sqrt(u[0] * u[0] + u[1] * u[1] + u[2] * u[2]);
939 if (ulen == Real(0))
940 return 0;
941 const Real un[3] = {u[0] / ulen, u[1] / ulen, u[2] / ulen};
942 Real e1[3];
943 if (Kokkos::fabs(un[0]) <= Kokkos::fabs(un[1]) && Kokkos::fabs(un[0]) <= Kokkos::fabs(un[2])) {
944 e1[0] = 0; e1[1] = -un[2]; e1[2] = un[1];
945 } else if (Kokkos::fabs(un[1]) <= Kokkos::fabs(un[2])) {
946 e1[0] = -un[2]; e1[1] = 0; e1[2] = un[0];
947 } else {
948 e1[0] = -un[1]; e1[1] = un[0]; e1[2] = 0;
949 }
950 const Real e1l = Kokkos::sqrt(e1[0] * e1[0] + e1[1] * e1[1] + e1[2] * e1[2]);
951 e1[0] /= e1l; e1[1] /= e1l; e1[2] /= e1l;
952 const Real e2[3] = {un[1] * e1[2] - un[2] * e1[1], un[2] * e1[0] - un[0] * e1[2],
953 un[0] * e1[1] - un[1] * e1[0]};
954 Real cx = 0, cy = 0, cz = 0;
955 for (int i = 0; i < m; ++i) { cx += px[i]; cy += py[i]; cz += pz[i]; }
956 cx /= m; cy /= m; cz /= m;
957 Real ang[MAXSV];
958 for (int i = 0; i < m; ++i) {
959 const Real dx = px[i] - cx, dy = py[i] - cy, dz = pz[i] - cz;
960 const Real qx = dx * e1[0] + dy * e1[1] + dz * e1[2];
961 const Real qy = dx * e2[0] + dy * e2[1] + dz * e2[2];
962 const Real s = Kokkos::fabs(qx) + Kokkos::fabs(qy);
963 const Real tt = (s > Real(0)) ? qy / s : Real(0);
964 ang[i] = (qx < Real(0)) ? (Real(2) - tt) : (qy < Real(0) ? Real(4) + tt : tt);
965 }
966 for (int i = 1; i < m; ++i) {
967 Real ka = ang[i], kx = px[i], ky = py[i], kz = pz[i];
968 int j = i - 1;
969 while (j >= 0 && ang[j] > ka) {
970 ang[j + 1] = ang[j];
971 px[j + 1] = px[j]; py[j + 1] = py[j]; pz[j + 1] = pz[j];
972 --j;
973 }
974 ang[j + 1] = ka; px[j + 1] = kx; py[j + 1] = ky; pz[j + 1] = kz;
975 }
976 return m;
977 }
978
981 KOKKOS_INLINE_FUNCTION Real volume() const {
982 Real vol = 0;
983 Real fx[MAXFV], fy[MAXFV], fz[MAXFV];
984 for (int k = 0; k < np; ++k) {
985 const int m = faceOrdered(k, fx, fy, fz);
986 if (m < 3)
987 continue;
988 Real A[3];
989 polyAreaVec(fx, fy, fz, m, A);
990 const Real area = Kokkos::sqrt(A[0] * A[0] + A[1] * A[1] + A[2] * A[2]);
991 const Real support = Kokkos::sqrt(nn[k]); // |n_k| = seed->plane distance
992 vol += support * area;
993 }
994 return vol * (Real(1) / Real(3));
995 }
996
997 // ---- Vertex-local, SORT-FREE geometry (Ray/Sokolov/Lefebvre/Lévy TOG 2018; geogram ConvexCell)
998 // ---- Plane stored as the (non-unit) foot-point normal n with interior {x : n·x ≤ n·n}; then x=n
999 // is the foot of the perpendicular from the origin onto the plane (the facet point), and |n| is
1000 // the origin→ plane distance. The cell now stores n directly (member `n`, with `nn = n·n`), so
1001 // this is just a read. Volume by the divergence theorem, coning every boundary flag (facet n_i,
1002 // edge foot f, vertex v) to the origin: each tetra (0,n_i,f,v) is LOCAL to one vertex + its 3
1003 // planes, and the signed sum is exact even when feet fall outside their faces. No vertex
1004 // ordering, no adjacency — a pure per-vertex scatter.
1005
1008 KOKKOS_INLINE_FUNCTION void planeN(int k, Real out[3]) const {
1009 out[0] = n[k][0];
1010 out[1] = n[k][1];
1011 out[2] = n[k][2];
1012 }
1013 KOKKOS_INLINE_FUNCTION static void xprod(const Real a[3], const Real b[3], Real o[3]) {
1014 o[0] = a[1] * b[2] - a[2] * b[1];
1015 o[1] = a[2] * b[0] - a[0] * b[2];
1016 o[2] = a[0] * b[1] - a[1] * b[0];
1017 }
1018 KOKKOS_INLINE_FUNCTION static Real dot3(const Real a[3], const Real b[3]) {
1019 return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
1020 }
1021 KOKKOS_INLINE_FUNCTION static Real det3(const Real a[3], const Real b[3], const Real c[3]) {
1022 Real bc[3];
1023 xprod(b, c, bc);
1024 return dot3(a, bc);
1025 }
1027 KOKKOS_INLINE_FUNCTION static void edgeFoot(const Real v[3], const Real ck[3], Real f[3]) {
1028 const Real c2 = dot3(ck, ck), s = (c2 > Real(0)) ? dot3(v, ck) / c2 : Real(0);
1029 f[0] = v[0] - s * ck[0];
1030 f[1] = v[1] - s * ck[1];
1031 f[2] = v[2] - s * ck[2];
1032 }
1033
1036 KOKKOS_INLINE_FUNCTION Real volumePerVertex() const {
1037 Real vol = 0;
1038 for (int t = 0; t < nt; ++t) {
1039 if (!alive[t])
1040 continue;
1041 Real n1[3], n2[3], n3[3];
1042 planeN(t0[t], n1);
1043 planeN(t1[t], n2);
1044 planeN(t2[t], n3);
1045 Real c1[3], c2[3], c3[3];
1046 xprod(n2, n3, c1);
1047 xprod(n3, n1, c2);
1048 xprod(n1, n2, c3);
1049 Real D = dot3(n1, c1);
1050 if (D < Real(0)) // canonical order D>0: swap (n2,n3) and (c2,c3); v is unchanged
1051 for (int a = 0; a < 3; ++a) {
1052 Real tmp = n2[a];
1053 n2[a] = n3[a];
1054 n3[a] = tmp;
1055 tmp = c2[a];
1056 c2[a] = c3[a];
1057 c3[a] = tmp;
1058 }
1059 const Real v[3] = {vx[t], vy[t], vz[t]};
1060 Real f12[3], f23[3], f31[3];
1061 edgeFoot(v, c3, f12);
1062 edgeFoot(v, c1, f23);
1063 edgeFoot(v, c2, f31);
1064 Real e[3], d = 0;
1065 e[0] = n1[0] - n2[0];
1066 e[1] = n1[1] - n2[1];
1067 e[2] = n1[2] - n2[2];
1068 d += det3(e, f12, v);
1069 e[0] = n2[0] - n3[0];
1070 e[1] = n2[1] - n3[1];
1071 e[2] = n2[2] - n3[2];
1072 d += det3(e, f23, v);
1073 e[0] = n3[0] - n1[0];
1074 e[1] = n3[1] - n1[1];
1075 e[2] = n3[2] - n1[2];
1076 d += det3(e, f31, v);
1077 vol += d;
1078 }
1079 return vol * (Real(1) / Real(6));
1080 }
1081
1085 KOKKOS_INLINE_FUNCTION void facetAreasPerVertex(Real* area) const {
1086 for (int t = 0; t < nt; ++t) {
1087 if (!alive[t])
1088 continue;
1089 int k1 = t0[t], k2 = t1[t], k3 = t2[t];
1090 Real n1[3], n2[3], n3[3];
1091 planeN(k1, n1);
1092 planeN(k2, n2);
1093 planeN(k3, n3);
1094 Real c1[3], c2[3], c3[3];
1095 xprod(n2, n3, c1);
1096 xprod(n3, n1, c2);
1097 xprod(n1, n2, c3);
1098 Real D = dot3(n1, c1);
1099 if (D < Real(0)) { // canonical: swap planes 2,3 (normals, cross, AND indices)
1100 for (int a = 0; a < 3; ++a) {
1101 Real tm = n2[a];
1102 n2[a] = n3[a];
1103 n3[a] = tm;
1104 tm = c2[a];
1105 c2[a] = c3[a];
1106 c3[a] = tm;
1107 }
1108 int tk = k2;
1109 k2 = k3;
1110 k3 = tk;
1111 }
1112 const Real v[3] = {vx[t], vy[t], vz[t]};
1113 Real f12[3], f23[3], f31[3];
1114 edgeFoot(v, c3, f12);
1115 edgeFoot(v, c1, f23);
1116 edgeFoot(v, c2, f31);
1117 Real g[3];
1118 g[0] = f12[0] - f31[0];
1119 g[1] = f12[1] - f31[1];
1120 g[2] = f12[2] - f31[2];
1121 area[k1] += det3(n1, g, v) / (Real(2) * Kokkos::sqrt(dot3(n1, n1)));
1122 g[0] = f23[0] - f12[0];
1123 g[1] = f23[1] - f12[1];
1124 g[2] = f23[2] - f12[2];
1125 area[k2] += det3(n2, g, v) / (Real(2) * Kokkos::sqrt(dot3(n2, n2)));
1126 g[0] = f31[0] - f23[0];
1127 g[1] = f31[1] - f23[1];
1128 g[2] = f31[2] - f23[2];
1129 area[k3] += det3(n3, g, v) / (Real(2) * Kokkos::sqrt(dot3(n3, n3)));
1130 }
1131 }
1132
1136 KOKKOS_INLINE_FUNCTION static void scatterFacetMoment(int ki, const Real ni[3], const Real ff[3],
1137 const Real fl[3], const Real v[3],
1138 Real* area, Real* mx, Real* my, Real* mz) {
1139 const Real inv2n = Real(0.5) / Kokkos::sqrt(dot3(ni, ni));
1140 const Real sa = det3(ni, ff, v) * inv2n; // triangle (ni, ff, v)
1141 const Real sb = -det3(ni, fl, v) * inv2n; // triangle (ni, v, fl)
1142 area[ki] += sa + sb;
1143 mx[ki] += sa * (ni[0] + ff[0] + v[0]) / Real(3) + sb * (ni[0] + v[0] + fl[0]) / Real(3);
1144 my[ki] += sa * (ni[1] + ff[1] + v[1]) / Real(3) + sb * (ni[1] + v[1] + fl[1]) / Real(3);
1145 mz[ki] += sa * (ni[2] + ff[2] + v[2]) / Real(3) + sb * (ni[2] + v[2] + fl[2]) / Real(3);
1146 }
1147
1152 KOKKOS_INLINE_FUNCTION void facetMomentsPerVertex(Real* area, Real* mx, Real* my,
1153 Real* mz) const {
1154 for (int t = 0; t < nt; ++t) {
1155 if (!alive[t])
1156 continue;
1157 int k1 = t0[t], k2 = t1[t], k3 = t2[t];
1158 Real n1[3], n2[3], n3[3];
1159 planeN(k1, n1);
1160 planeN(k2, n2);
1161 planeN(k3, n3);
1162 Real c1[3], c2[3], c3[3];
1163 xprod(n2, n3, c1);
1164 xprod(n3, n1, c2);
1165 xprod(n1, n2, c3);
1166 if (dot3(n1, c1) < Real(0)) { // canonical: swap planes 2,3 (normals, cross, indices)
1167 for (int a = 0; a < 3; ++a) {
1168 Real tm = n2[a];
1169 n2[a] = n3[a];
1170 n3[a] = tm;
1171 tm = c2[a];
1172 c2[a] = c3[a];
1173 c3[a] = tm;
1174 }
1175 int tk = k2;
1176 k2 = k3;
1177 k3 = tk;
1178 }
1179 const Real v[3] = {vx[t], vy[t], vz[t]};
1180 Real f12[3], f23[3], f31[3];
1181 edgeFoot(v, c3, f12);
1182 edgeFoot(v, c1, f23);
1183 edgeFoot(v, c2, f31);
1184 scatterFacetMoment(k1, n1, f12, f31, v, area, mx, my, mz);
1185 scatterFacetMoment(k2, n2, f23, f12, v, area, mx, my, mz);
1186 scatterFacetMoment(k3, n3, f31, f23, v, area, mx, my, mz);
1187 }
1188 }
1189
1196 KOKKOS_INLINE_FUNCTION static void scatterGradRaw(int ki, const Real ni[3], const Real ff[3],
1197 const Real fl[3], const Real v[3], Real* dgx,
1198 Real* dgy, Real* dgz) {
1199 const Real sa = det3(ni, ff, v); // raw signed area of triangle (ni, ff, v)
1200 const Real sb = -det3(ni, fl, v); // raw signed area of triangle (ni, v, fl)
1201 const Real da = sa + sb;
1202 const Real momx = sa * (ni[0] + ff[0] + v[0]) / Real(3) + sb * (ni[0] + v[0] + fl[0]) / Real(3);
1203 const Real momy = sa * (ni[1] + ff[1] + v[1]) / Real(3) + sb * (ni[1] + v[1] + fl[1]) / Real(3);
1204 const Real momz = sa * (ni[2] + ff[2] + v[2]) / Real(3) + sb * (ni[2] + v[2] + fl[2]) / Real(3);
1205 dgx[ki] += Real(2) * da * ni[0] - momx; // 2·da·n_k − mom (numerator piece)
1206 dgy[ki] += Real(2) * da * ni[1] - momy;
1207 dgz[ki] += Real(2) * da * ni[2] - momz;
1208 }
1209
1218 KOKKOS_INLINE_FUNCTION void geomVolumeGrad(Real& vol, Real* dgx, Real* dgy, Real* dgz) const {
1219 Real V = 0;
1220 for (int t = 0; t < nt; ++t) {
1221 if (!alive[t])
1222 continue;
1223 int k1 = t0[t], k2 = t1[t], k3 = t2[t];
1224 Real n1[3], n2[3], n3[3];
1225 planeN(k1, n1);
1226 planeN(k2, n2);
1227 planeN(k3, n3);
1228 Real c1[3], c2[3], c3[3];
1229 xprod(n2, n3, c1);
1230 xprod(n3, n1, c2);
1231 xprod(n1, n2, c3);
1232 if (dot3(n1, c1) < Real(0)) { // canonical D>0: swap planes 2,3 (normals, cross, indices)
1233 for (int a = 0; a < 3; ++a) {
1234 Real tm = n2[a];
1235 n2[a] = n3[a];
1236 n3[a] = tm;
1237 tm = c2[a];
1238 c2[a] = c3[a];
1239 c3[a] = tm;
1240 }
1241 int tk = k2;
1242 k2 = k3;
1243 k3 = tk;
1244 }
1245 const Real v[3] = {vx[t], vy[t], vz[t]};
1246 Real f12[3], f23[3], f31[3];
1247 edgeFoot(v, c3, f12);
1248 edgeFoot(v, c1, f23);
1249 edgeFoot(v, c2, f31);
1250 Real e[3];
1251 e[0] = n1[0] - n2[0];
1252 e[1] = n1[1] - n2[1];
1253 e[2] = n1[2] - n2[2];
1254 V += det3(e, f12, v);
1255 e[0] = n2[0] - n3[0];
1256 e[1] = n2[1] - n3[1];
1257 e[2] = n2[2] - n3[2];
1258 V += det3(e, f23, v);
1259 e[0] = n3[0] - n1[0];
1260 e[1] = n3[1] - n1[1];
1261 e[2] = n3[2] - n1[2];
1262 V += det3(e, f31, v);
1263 scatterGradRaw(k1, n1, f12, f31, v, dgx, dgy, dgz);
1264 scatterGradRaw(k2, n2, f23, f12, v, dgx, dgy, dgz);
1265 scatterGradRaw(k3, n3, f31, f23, v, dgx, dgy, dgz);
1266 }
1267 for (int k = 0; k < np; ++k) { // per-facet fold, sqrt-free: numerator/(2·nn)
1268 const Real inv = (nn[k] > Real(0)) ? Real(1) / (Real(2) * nn[k]) : Real(0);
1269 dgx[k] *= inv;
1270 dgy[k] *= inv;
1271 dgz[k] *= inv;
1272 }
1273 vol = V * (Real(1) / Real(6));
1274 }
1275
1280 KOKKOS_INLINE_FUNCTION static void scatterAreaMomentRaw(int ki, const Real ni[3],
1281 const Real ff[3], const Real fl[3],
1282 const Real v[3], Real* sa_acc, Real* smx,
1283 Real* smy, Real* smz) {
1284 const Real sa = det3(ni, ff, v); // raw signed area of triangle (ni, ff, v)
1285 const Real sb = -det3(ni, fl, v); // raw signed area of triangle (ni, v, fl)
1286 sa_acc[ki] += sa + sb;
1287 smx[ki] += sa * (ni[0] + ff[0] + v[0]) / Real(3) + sb * (ni[0] + v[0] + fl[0]) / Real(3);
1288 smy[ki] += sa * (ni[1] + ff[1] + v[1]) / Real(3) + sb * (ni[1] + v[1] + fl[1]) / Real(3);
1289 smz[ki] += sa * (ni[2] + ff[2] + v[2]) / Real(3) + sb * (ni[2] + v[2] + fl[2]) / Real(3);
1290 }
1291
1301 KOKKOS_INLINE_FUNCTION void geomVolumeArea(Real& vol, Real* avx, Real* avy, Real* avz, Real* dgx,
1302 Real* dgy, Real* dgz) const {
1303 Real V = 0;
1304 for (int t = 0; t < nt; ++t) {
1305 if (!alive[t])
1306 continue;
1307 int k1 = t0[t], k2 = t1[t], k3 = t2[t];
1308 Real n1[3], n2[3], n3[3];
1309 planeN(k1, n1);
1310 planeN(k2, n2);
1311 planeN(k3, n3);
1312 Real c1[3], c2[3], c3[3];
1313 xprod(n2, n3, c1);
1314 xprod(n3, n1, c2);
1315 xprod(n1, n2, c3);
1316 if (dot3(n1, c1) < Real(0)) { // canonical D>0: swap planes 2,3 (normals, cross, indices)
1317 for (int a = 0; a < 3; ++a) {
1318 Real tm = n2[a];
1319 n2[a] = n3[a];
1320 n3[a] = tm;
1321 tm = c2[a];
1322 c2[a] = c3[a];
1323 c3[a] = tm;
1324 }
1325 int tk = k2;
1326 k2 = k3;
1327 k3 = tk;
1328 }
1329 const Real v[3] = {vx[t], vy[t], vz[t]};
1330 Real f12[3], f23[3], f31[3];
1331 edgeFoot(v, c3, f12);
1332 edgeFoot(v, c1, f23);
1333 edgeFoot(v, c2, f31);
1334 Real e[3];
1335 e[0] = n1[0] - n2[0];
1336 e[1] = n1[1] - n2[1];
1337 e[2] = n1[2] - n2[2];
1338 V += det3(e, f12, v);
1339 e[0] = n2[0] - n3[0];
1340 e[1] = n2[1] - n3[1];
1341 e[2] = n2[2] - n3[2];
1342 V += det3(e, f23, v);
1343 e[0] = n3[0] - n1[0];
1344 e[1] = n3[1] - n1[1];
1345 e[2] = n3[2] - n1[2];
1346 V += det3(e, f31, v);
1347 scatterAreaMomentRaw(k1, n1, f12, f31, v, avx, dgx, dgy,
1348 dgz); // avx <- S_a, (dgx,dgy,dgz) <- S_m
1349 scatterAreaMomentRaw(k2, n2, f23, f12, v, avx, dgx, dgy, dgz);
1350 scatterAreaMomentRaw(k3, n3, f31, f23, v, avx, dgx, dgy, dgz);
1351 }
1352 for (int k = 0; k < np; ++k) { // per-facet fold, sqrt-free
1353 const Real inv = (nn[k] > Real(0)) ? Real(1) / (Real(2) * nn[k]) : Real(0);
1354 const Real sa = avx[k]; // raw area S_a (accumulated here during the scatter)
1355 const Real smx = dgx[k], smy = dgy[k], smz = dgz[k];
1356 dgx[k] = (Real(2) * sa * n[k][0] - smx) * inv; // dV/dn_k
1357 dgy[k] = (Real(2) * sa * n[k][1] - smy) * inv;
1358 dgz[k] = (Real(2) * sa * n[k][2] - smz) * inv;
1359 avx[k] = sa * n[k][0] * inv; // outward area-vector A_k·n_k/|n_k|
1360 avy[k] = sa * n[k][1] * inv;
1361 avz[k] = sa * n[k][2] * inv;
1362 }
1363 vol = V * (Real(1) / Real(6));
1364 }
1365
1368 KOKKOS_INLINE_FUNCTION static void edgeCrossAdj(int j, int p, int q, const Real Nn[3][3],
1369 const Real w[3], Real out[3]) {
1370 if (j == p) {
1371 xprod(Nn[q], w, out);
1372 } else if (j == q) {
1373 Real tm[3];
1374 xprod(Nn[p], w, tm);
1375 out[0] = -tm[0];
1376 out[1] = -tm[1];
1377 out[2] = -tm[2];
1378 } else {
1379 out[0] = out[1] = out[2] = Real(0);
1380 }
1381 }
1382
1394 KOKKOS_INLINE_FUNCTION void geomVolumeAreaGrad(int t, int pl[3], Real contrib[3],
1395 Real grad[3][3][3]) const {
1396 // planes + canonical swap (matches facetAreasPerVertex), reusing the cached vertex — nothing
1397 // recomputed. Every loop is VOR_UNROLL'd so the small local arrays scalarize into registers on
1398 // GPU (no local-memory spill from runtime indices); on CPU the macro is empty.
1399 int kk[3] = {t0[t], t1[t], t2[t]};
1400 Real Nn[3][3];
1401 VOR_UNROLL for (int a = 0; a < 3; ++a) {
1402 Nn[a][0] = n[kk[a]][0];
1403 Nn[a][1] = n[kk[a]][1];
1404 Nn[a][2] = n[kk[a]][2];
1405 }
1406 {
1407 Real c01[3];
1408 xprod(Nn[1], Nn[2], c01);
1409 if (dot3(Nn[0], c01) < Real(0)) {
1410 VOR_UNROLL for (int d = 0; d < 3; ++d) {
1411 Real tm = Nn[1][d];
1412 Nn[1][d] = Nn[2][d];
1413 Nn[2][d] = tm;
1414 }
1415 int tk = kk[1];
1416 kk[1] = kk[2];
1417 kk[2] = tk;
1418 }
1419 }
1420 pl[0] = kk[0];
1421 pl[1] = kk[1];
1422 pl[2] = kk[2];
1423 const Real v[3] = {vx[t], vy[t], vz[t]};
1424 Real nn3[3], u[3][3]; // u[a] = 2 n_a − v
1425 VOR_UNROLL for (int a = 0; a < 3; ++a) {
1426 nn3[a] = dot3(Nn[a], Nn[a]);
1427 VOR_UNROLL for (int d = 0; d < 3; ++d) u[a][d] = Real(2) * Nn[a][d] - v[d];
1428 }
1429 // edge directions e[m] = N[eP]×N[eQ] for edges (0,1),(1,2),(2,0); cofactor of plane j is c_j =
1430 // e[(j+1)%3]
1431 const int eP[3] = {0, 1, 2}, eQ[3] = {1, 2, 0};
1432 Real e[3][3], ecc[3], es[3], F[3][3];
1433 VOR_UNROLL for (int m = 0; m < 3; ++m) {
1434 xprod(Nn[eP[m]], Nn[eQ[m]], e[m]);
1435 ecc[m] = dot3(e[m], e[m]);
1436 const Real evc = dot3(v, e[m]);
1437 es[m] = (ecc[m] > Real(0)) ? evc / ecc[m] : Real(0);
1438 VOR_UNROLL for (int d = 0; d < 3; ++d) F[m][d] =
1439 v[d] - es[m] * e[m][d]; // foot of v on edge line m
1440 }
1441 const Real D = dot3(Nn[0], e[1]); // e[1] = N1×N2 = cofactor c_0; D = det(n0,n1,n2) > 0
1442 Real g[3][3], den[3];
1443 VOR_UNROLL for (int i = 0; i < 3; ++i) { // facet i: g_i = F[i] − F[(i+2)%3]
1444 const int mb = (i + 2) % 3;
1445 VOR_UNROLL for (int d = 0; d < 3; ++d) g[i][d] = F[i][d] - F[mb][d];
1446 const Real Ni = det3(Nn[i], g[i], v);
1447 den[i] = Real(2) * Kokkos::sqrt(nn3[i]);
1448 contrib[i] = (den[i] > Real(0)) ? Ni / den[i] : Real(0);
1449 }
1450 VOR_UNROLL for (int i = 0; i < 3; ++i) {
1451 Real wi[3];
1452 xprod(v, Nn[i], wi); // w_i = v×n_i (∂det3(n_i,·,v)/∂g)
1453 Real nixg[3];
1454 xprod(Nn[i], g[i], nixg); // n_i×g_i (∂det3(n_i,g_i,·)/∂v)
1455 const int mb = (i + 2) % 3;
1456 VOR_UNROLL for (int j = 0; j < 3; ++j) {
1457 const Real* cj = e[(j + 1) % 3]; // cofactor of plane j
1458 Real gN[3] = {0, 0, 0};
1459 if (i == j) {
1460 Real gxv[3];
1461 xprod(g[i], v, gxv);
1462 VOR_UNROLL for (int d = 0; d < 3; ++d) gN[d] += gxv[d];
1463 } // ∂/∂n_i
1464 const Real alpha = dot3(nixg, cj) / D; // ∂/∂v (rank-1)
1465 VOR_UNROLL for (int d = 0; d < 3; ++d) gN[d] += alpha * u[j][d];
1466 // ∂/∂g = (∂F[i]/∂n_j − ∂F[(i+2)%3]/∂n_j)^T w_i — the two foot adjoints
1467 VOR_UNROLL for (int s = 0; s < 2; ++s) {
1468 const int m = (s == 0) ? i : mb;
1469 const Real sgn = (s == 0) ? Real(1) : Real(-1);
1470 const int p = eP[m], q = eQ[m];
1471 const Real cjw = dot3(cj, wi); // for vAdj
1472 const Real cje = dot3(cj, e[m]); // for dS
1473 const Real emw = dot3(e[m], wi);
1474 Real eaW[3], eaV[3], eaE[3];
1475 edgeCrossAdj(j, p, q, Nn, wi, eaW);
1476 edgeCrossAdj(j, p, q, Nn, v, eaV);
1477 edgeCrossAdj(j, p, q, Nn, e[m], eaE);
1478 const Real inv_ecc = (ecc[m] > Real(0)) ? Real(1) / ecc[m] : Real(0);
1479 Real dS[3]; // ∂s_m/∂n_j
1480 VOR_UNROLL for (int d = 0; d < 3; ++d) dS[d] =
1481 ((cje / D) * u[j][d] + eaV[d]) * inv_ecc - (es[m] * inv_ecc) * Real(2) * eaE[d];
1482 // (∂F[m]/∂n_j)^T w_i = (c_j·w_i/D) u_j − (e_m·w_i) dS − s_m (∂e_m/∂n_j)^T w_i
1483 VOR_UNROLL for (int d = 0; d < 3; ++d) gN[d] +=
1484 sgn * ((cjw / D) * u[j][d] - emw * dS[d] - es[m] * eaW[d]);
1485 }
1486 const Real invden = (den[i] > Real(0)) ? Real(1) / den[i] : Real(0);
1487 VOR_UNROLL for (int d = 0; d < 3; ++d) grad[i][j][d] = gN[d] * invden;
1488 if (i == j) { // 1/(2|n_i|) factor: −A_i n_i/nn_i
1489 const Real f = (nn3[i] > Real(0)) ? contrib[i] / nn3[i] : Real(0);
1490 VOR_UNROLL for (int d = 0; d < 3; ++d) grad[i][j][d] -= f * Nn[i][d];
1491 }
1492 }
1493 }
1494 }
1495
1500 KOKKOS_INLINE_FUNCTION void geomVolumeAreaGradAD(int t, int pl[3], Real contrib[3],
1501 Real grad[3][3][3]) const {
1502 using D = detail::Dual<Real, 9>;
1503 int k1 = t0[t], k2 = t1[t], k3 = t2[t];
1504 Real rn1[3] = {n[k1][0], n[k1][1], n[k1][2]};
1505 Real rn2[3] = {n[k2][0], n[k2][1], n[k2][2]};
1506 Real rn3[3] = {n[k3][0], n[k3][1], n[k3][2]};
1507 { // canonical D>0 swap on the real values (same as facetAreasPerVertex) so contributions land
1508 // right
1509 Real cc[3];
1510 xprod(rn2, rn3, cc);
1511 if (dot3(rn1, cc) < Real(0)) {
1512 for (int a = 0; a < 3; ++a) {
1513 Real tm = rn2[a];
1514 rn2[a] = rn3[a];
1515 rn3[a] = tm;
1516 }
1517 int tk = k2;
1518 k2 = k3;
1519 k3 = tk;
1520 }
1521 }
1522 pl[0] = k1;
1523 pl[1] = k2;
1524 pl[2] = k3;
1525 D n1[3], n2[3], n3[3];
1526 for (int a = 0; a < 3; ++a) { // seed: n1 <- slots 0..2, n2 <- 3..5, n3 <- 6..8
1527 n1[a] = detail::dseed<Real, 9>(rn1[a], a);
1528 n2[a] = detail::dseed<Real, 9>(rn2[a], 3 + a);
1529 n3[a] = detail::dseed<Real, 9>(rn3[a], 6 + a);
1530 }
1531 const D nn1 = detail::ddot<Real, 9>(n1, n1), nn2 = detail::ddot<Real, 9>(n2, n2),
1532 nn3 = detail::ddot<Real, 9>(n3, n3);
1533 D c1[3], c2[3], c3[3];
1534 detail::dcross<Real, 9>(n2, n3, c1);
1535 detail::dcross<Real, 9>(n3, n1, c2);
1536 detail::dcross<Real, 9>(n1, n2, c3);
1537 const D Dd = detail::ddot<Real, 9>(n1, c1); // det
1538 D v[3];
1539 for (int a = 0; a < 3; ++a)
1540 v[a] = (nn1 * c1[a] + nn2 * c2[a] + nn3 * c3[a]) / Dd; // Cramer vertex
1541 D f12[3], f23[3], f31[3];
1542 detail::dedgeFoot<Real, 9>(v, c3, f12);
1543 detail::dedgeFoot<Real, 9>(v, c1, f23);
1544 detail::dedgeFoot<Real, 9>(v, c2, f31);
1545 const D two = detail::dnum<Real, 9>(Real(2));
1546 D g[3], aa[3];
1547 for (int a = 0; a < 3; ++a)
1548 g[a] = f12[a] - f31[a];
1549 aa[0] = detail::ddet3<Real, 9>(n1, g, v) / (two * detail::dsqrt<Real, 9>(nn1));
1550 for (int a = 0; a < 3; ++a)
1551 g[a] = f23[a] - f12[a];
1552 aa[1] = detail::ddet3<Real, 9>(n2, g, v) / (two * detail::dsqrt<Real, 9>(nn2));
1553 for (int a = 0; a < 3; ++a)
1554 g[a] = f31[a] - f23[a];
1555 aa[2] = detail::ddet3<Real, 9>(n3, g, v) / (two * detail::dsqrt<Real, 9>(nn3));
1556 for (int i = 0; i < 3; ++i) {
1557 contrib[i] = aa[i].v;
1558 for (int j = 0; j < 3; ++j)
1559 for (int c = 0; c < 3; ++c)
1560 grad[i][j][c] = aa[i].d[3 * j + c];
1561 }
1562 }
1563
1571 KOKKOS_INLINE_FUNCTION void geometryPerVertex(Real& vol, Real* area, Real* mx, Real* my,
1572 Real* mz) const {
1573 Real V = 0;
1574 for (int t = 0; t < nt; ++t) {
1575 if (!alive[t])
1576 continue;
1577 int k1 = t0[t], k2 = t1[t], k3 = t2[t];
1578 Real n1[3], n2[3], n3[3];
1579 planeN(k1, n1);
1580 planeN(k2, n2);
1581 planeN(k3, n3);
1582 Real c1[3], c2[3], c3[3];
1583 xprod(n2, n3, c1);
1584 xprod(n3, n1, c2);
1585 xprod(n1, n2, c3);
1586 if (dot3(n1, c1) < Real(0)) { // canonical D>0: swap planes 2,3 (normals, cross, indices)
1587 for (int a = 0; a < 3; ++a) {
1588 Real tm = n2[a];
1589 n2[a] = n3[a];
1590 n3[a] = tm;
1591 tm = c2[a];
1592 c2[a] = c3[a];
1593 c3[a] = tm;
1594 }
1595 int tk = k2;
1596 k2 = k3;
1597 k3 = tk;
1598 }
1599 const Real v[3] = {vx[t], vy[t], vz[t]};
1600 Real f12[3], f23[3], f31[3];
1601 edgeFoot(v, c3, f12);
1602 edgeFoot(v, c1, f23);
1603 edgeFoot(v, c2, f31);
1604 Real e[3];
1605 e[0] = n1[0] - n2[0];
1606 e[1] = n1[1] - n2[1];
1607 e[2] = n1[2] - n2[2];
1608 V += det3(e, f12, v);
1609 e[0] = n2[0] - n3[0];
1610 e[1] = n2[1] - n3[1];
1611 e[2] = n2[2] - n3[2];
1612 V += det3(e, f23, v);
1613 e[0] = n3[0] - n1[0];
1614 e[1] = n3[1] - n1[1];
1615 e[2] = n3[2] - n1[2];
1616 V += det3(e, f31, v);
1617 scatterFacetMoment(k1, n1, f12, f31, v, area, mx, my, mz);
1618 scatterFacetMoment(k2, n2, f23, f12, v, area, mx, my, mz);
1619 scatterFacetMoment(k3, n3, f31, f23, v, area, mx, my, mz);
1620 }
1621 vol = V * (Real(1) / Real(6));
1622 }
1623
1633 KOKKOS_INLINE_FUNCTION bool facetGeometry(int k, Real areaVec[3], Real dv[3],
1634 Real conn[3]) const {
1635 Real fx[MAXFV], fy[MAXFV], fz[MAXFV];
1636 const int m = faceOrdered(k, fx, fy, fz);
1637 if (m < 3)
1638 return false;
1639 Real A[3];
1640 polyAreaVec(fx, fy, fz, m, A);
1641 const Real r[3] = {Real(2) * n[k][0], Real(2) * n[k][1],
1642 Real(2) * n[k][2]}; // connector r = 2·foot
1643 if (A[0] * r[0] + A[1] * r[1] + A[2] * r[2] < Real(0)) { // orient outward (toward neighbour)
1644 A[0] = -A[0];
1645 A[1] = -A[1];
1646 A[2] = -A[2];
1647 }
1648 const Real area = Kokkos::sqrt(A[0] * A[0] + A[1] * A[1] + A[2] * A[2]);
1649 // area-weighted centroid (fan-triangulate from v0)
1650 Real cx = 0, cy = 0, cz = 0, asum = 0;
1651 for (int i = 1; i + 1 < m; ++i) {
1652 const Real ux = fx[i] - fx[0], uy = fy[i] - fy[0], uz = fz[i] - fz[0];
1653 const Real wx = fx[i + 1] - fx[0], wy = fy[i + 1] - fy[0], wz = fz[i + 1] - fz[0];
1654 const Real tx = uy * wz - uz * wy, ty = uz * wx - ux * wz, tz = ux * wy - uy * wx;
1655 const Real at = Real(0.5) * Kokkos::sqrt(tx * tx + ty * ty + tz * tz);
1656 cx += at * (fx[0] + fx[i] + fx[i + 1]);
1657 cy += at * (fy[0] + fy[i] + fy[i + 1]);
1658 cz += at * (fz[0] + fz[i] + fz[i + 1]);
1659 asum += at;
1660 }
1661 const Real inv = asum > Real(0) ? Real(1) / (Real(3) * asum) : Real(0);
1662 cx *= inv;
1663 cy *= inv;
1664 cz *= inv;
1665 const Real s = area / Kokkos::sqrt(r[0] * r[0] + r[1] * r[1] + r[2] * r[2]);
1666 areaVec[0] = A[0];
1667 areaVec[1] = A[1];
1668 areaVec[2] = A[2];
1669 dv[0] = s * (r[0] - cx);
1670 dv[1] = s * (r[1] - cy);
1671 dv[2] = s * (r[2] - cz);
1672 conn[0] = r[0];
1673 conn[1] = r[1];
1674 conn[2] = r[2];
1675 return true;
1676 }
1677};
1678
1686template <class Real, int MAXP, int MAXT, bool TrackAdj, class Policy = Voronoi>
1688 const Real L[3], const Real* relx, const Real* rely,
1689 const Real* relz, const int* ids, int nNbr,
1690 Real wSelf = Real(0), const Real* weights = nullptr) {
1691 c.initBox(L[0], L[1], L[2]);
1692 for (int i = 0; i < nNbr; ++i) {
1693 const Real n[3] = {relx[i], rely[i], relz[i]};
1694 Real wNbr = Real(0);
1695 if constexpr (Policy::kHasWeightDof) wNbr = weights[i];
1696 const Real off = Policy::template offsetFromRel<Real>(n, wSelf, wNbr);
1697 if constexpr (!Policy::kHasWeightDof) {
1698 if (!(off < Real(2) * c.maxVertexRsq()))
1699 break; // security radius (bisector certificate; Voronoi only)
1700 }
1701 c.clip(n, off, ids[i]);
1702 if (c.overflow)
1703 return;
1704 }
1705}
1706
1707} // namespace peclet::voro
1708
1709#endif // PECLET_VORO_CONVEX_CELL_HPP
#define VOR_UNROLL
Definition convex_cell.hpp:37
KOKKOS_INLINE_FUNCTION Dual< Real, K > dnum(Real c)
Definition convex_cell.hpp:52
KOKKOS_INLINE_FUNCTION void dedgeFoot(const Dual< Real, K > v[3], const Dual< Real, K > c[3], Dual< Real, K > f[3])
Definition convex_cell.hpp:126
KOKKOS_INLINE_FUNCTION Dual< Real, K > ddet3(const Dual< Real, K > a[3], const Dual< Real, K > b[3], const Dual< Real, K > c[3])
Definition convex_cell.hpp:119
KOKKOS_INLINE_FUNCTION Dual< Real, K > operator-(const Dual< Real, K > &a, const Dual< Real, K > &b)
Definition convex_cell.hpp:74
KOKKOS_INLINE_FUNCTION Dual< Real, K > operator*(const Dual< Real, K > &a, const Dual< Real, K > &b)
Definition convex_cell.hpp:82
KOKKOS_INLINE_FUNCTION Dual< Real, K > operator+(const Dual< Real, K > &a, const Dual< Real, K > &b)
Definition convex_cell.hpp:66
KOKKOS_INLINE_FUNCTION Dual< Real, K > dsqrt(const Dual< Real, K > &a)
Definition convex_cell.hpp:99
KOKKOS_INLINE_FUNCTION Dual< Real, K > dseed(Real c, int s)
Definition convex_cell.hpp:60
KOKKOS_INLINE_FUNCTION Dual< Real, K > ddot(const Dual< Real, K > a[3], const Dual< Real, K > b[3])
Definition convex_cell.hpp:108
KOKKOS_INLINE_FUNCTION void dcross(const Dual< Real, K > a[3], const Dual< Real, K > b[3], Dual< Real, K > o[3])
Definition convex_cell.hpp:112
KOKKOS_INLINE_FUNCTION Dual< Real, K > operator/(const Dual< Real, K > &a, const Dual< Real, K > &b)
Definition convex_cell.hpp:90
Definition convex_cell.hpp:40
KOKKOS_INLINE_FUNCTION void buildConvexCell(ConvexCell< Real, MAXP, MAXT, TrackAdj > &c, const Real L[3], const Real *relx, const Real *rely, const Real *relz, const int *ids, int nNbr, Real wSelf=Real(0), const Real *weights=nullptr)
Definition convex_cell.hpp:1687
Definition convex_cell.hpp:139
KOKKOS_INLINE_FUNCTION Real volumePerVertex() const
Definition convex_cell.hpp:1036
static constexpr int MAXFV
Definition convex_cell.hpp:795
KOKKOS_INLINE_FUNCTION int findSharing(int self, int x, int y) const
Definition convex_cell.hpp:508
Kokkos::Array< int,(TrackAdj ? MAXT :0) *3 > adj
Definition convex_cell.hpp:169
KOKKOS_INLINE_FUNCTION bool isLocallyConvexPartners(const unsigned char *poke4, Real tol, int *outPartners, int maxPartners, int &nPartners) const
Definition convex_cell.hpp:453
KOKKOS_INLINE_FUNCTION int sectionPolygon(const Real p0[3], const Real u[3], Real px[MAXSV], Real py[MAXSV], Real pz[MAXSV]) const
Definition convex_cell.hpp:905
KOKKOS_INLINE_FUNCTION void facetMomentsPerVertex(Real *area, Real *mx, Real *my, Real *mz) const
Definition convex_cell.hpp:1152
static KOKKOS_INLINE_FUNCTION void scatterFacetMoment(int ki, const Real ni[3], const Real ff[3], const Real fl[3], const Real v[3], Real *area, Real *mx, Real *my, Real *mz)
Definition convex_cell.hpp:1136
KOKKOS_INLINE_FUNCTION int oppositePlane(int s, int x, int y) const
Definition convex_cell.hpp:378
unsigned char t1[MAXT]
Definition convex_cell.hpp:150
KOKKOS_INLINE_FUNCTION void facetAreasPerVertex(Real *area) const
Definition convex_cell.hpp:1085
static KOKKOS_INLINE_FUNCTION void edgeFoot(const Real v[3], const Real ck[3], Real f[3])
foot of the perpendicular from v onto the edge line of direction ck: v − (v·ck/ck·ck) ck.
Definition convex_cell.hpp:1027
static KOKKOS_INLINE_FUNCTION Real dot3(const Real a[3], const Real b[3])
Definition convex_cell.hpp:1018
KOKKOS_INLINE_FUNCTION void initBoxPlanes(Real L0, Real L1, Real L2)
Definition convex_cell.hpp:228
static constexpr bool kTrackAdj
lets templated consumers (store save/load) branch at compile time
Definition convex_cell.hpp:141
KOKKOS_INLINE_FUNCTION void initBox(Real L0, Real L1, Real L2)
Definition convex_cell.hpp:173
bool overflow
set if MAXP/MAXT exceeded -> cell invalid, caller falls back
Definition convex_cell.hpp:154
int nt
triangle high-water mark
Definition convex_cell.hpp:153
Real nn[MAXP]
cached n·n, i.e. the half-space offset (so the cull test is n·x <= nn)
Definition convex_cell.hpp:147
Real n[MAXP][3]
Definition convex_cell.hpp:144
static KOKKOS_INLINE_FUNCTION void polyAreaVec(const Real fx[], const Real fy[], const Real fz[], int m, Real A[3])
Polygon area vector (0.5 Σ vi × v(i+1)) of an ordered face.
Definition convex_cell.hpp:881
KOKKOS_INLINE_FUNCTION bool isLocallyConvex(const unsigned char *poke4, Real tol) const
Definition convex_cell.hpp:429
KOKKOS_INLINE_FUNCTION void rebuildAdjacency()
Definition convex_cell.hpp:523
KOKKOS_INLINE_FUNCTION int faceOrdered(int k, Real fx[MAXFV], Real fy[MAXFV], Real fz[MAXFV]) const
Definition convex_cell.hpp:801
Real vy[MAXT]
Definition convex_cell.hpp:151
KOKKOS_INLINE_FUNCTION bool isSelfConsistentPartners(Real tol, int *outPartners, int maxPartners, int &nPartners) const
Definition convex_cell.hpp:342
KOKKOS_INLINE_FUNCTION void geomVolumeAreaGrad(int t, int pl[3], Real contrib[3], Real grad[3][3][3]) const
Definition convex_cell.hpp:1394
KOKKOS_INLINE_FUNCTION bool empty() const
Definition convex_cell.hpp:775
int pnbr[MAXP]
neighbour seed id per plane (<0 => bounding box)
Definition convex_cell.hpp:148
static constexpr int MAXSV
max vertices on a cross-section polygon
Definition convex_cell.hpp:895
KOKKOS_INLINE_FUNCTION void geomVolumeGrad(Real &vol, Real *dgx, Real *dgy, Real *dgz) const
Definition convex_cell.hpp:1218
KOKKOS_INLINE_FUNCTION Real maxVertexRsq() const
Largest squared dual-vertex radius over live triangles (drives the security radius).
Definition convex_cell.hpp:490
static KOKKOS_INLINE_FUNCTION void edgeCrossAdj(int j, int p, int q, const Real Nn[3][3], const Real w[3], Real out[3])
Definition convex_cell.hpp:1368
KOKKOS_INLINE_FUNCTION bool isSelfConsistent(Real tol) const
Definition convex_cell.hpp:300
KOKKOS_INLINE_FUNCTION bool checkAdjacencyInvariant() const
Definition convex_cell.hpp:539
KOKKOS_INLINE_FUNCTION bool clip(const Real pdir[3], Real d, int nbr)
Definition convex_cell.hpp:584
Real vx[MAXT]
Definition convex_cell.hpp:151
KOKKOS_INLINE_FUNCTION void geometryPerVertex(Real &vol, Real *area, Real *mx, Real *my, Real *mz) const
Definition convex_cell.hpp:1571
KOKKOS_INLINE_FUNCTION int countFaces() const
Definition convex_cell.hpp:782
unsigned char t2[MAXT]
triangle = triple of plane indices
Definition convex_cell.hpp:150
KOKKOS_INLINE_FUNCTION void geomVolumeArea(Real &vol, Real *avx, Real *avy, Real *avz, Real *dgx, Real *dgy, Real *dgz) const
Definition convex_cell.hpp:1301
KOKKOS_INLINE_FUNCTION void computeVertex(int t)
Definition convex_cell.hpp:206
int np
number of planes
Definition convex_cell.hpp:149
KOKKOS_INLINE_FUNCTION Real volume() const
Definition convex_cell.hpp:981
KOKKOS_INLINE_FUNCTION void geomVolumeAreaGradAD(int t, int pl[3], Real contrib[3], Real grad[3][3][3]) const
Definition convex_cell.hpp:1500
static KOKKOS_INLINE_FUNCTION void scatterAreaMomentRaw(int ki, const Real ni[3], const Real ff[3], const Real fl[3], const Real v[3], Real *sa_acc, Real *smx, Real *smy, Real *smz)
Definition convex_cell.hpp:1280
KOKKOS_INLINE_FUNCTION void planeN(int k, Real out[3]) const
Definition convex_cell.hpp:1008
unsigned char t0[MAXT]
Definition convex_cell.hpp:150
KOKKOS_INLINE_FUNCTION void reevalGeometry(Real sx, Real sy, Real sz, const Real *pos, Real L, Real wSelf=Real(0), const Real *weight=nullptr)
Definition convex_cell.hpp:255
KOKKOS_INLINE_FUNCTION int allocTri()
Definition convex_cell.hpp:569
KOKKOS_INLINE_FUNCTION void computePoke4(unsigned char *out) const
Definition convex_cell.hpp:391
bool alive[MAXT]
triangle live flag
Definition convex_cell.hpp:152
static KOKKOS_INLINE_FUNCTION void scatterGradRaw(int ki, const Real ni[3], const Real ff[3], const Real fl[3], const Real v[3], Real *dgx, Real *dgy, Real *dgz)
Definition convex_cell.hpp:1196
KOKKOS_INLINE_FUNCTION bool facetGeometry(int k, Real areaVec[3], Real dv[3], Real conn[3]) const
Definition convex_cell.hpp:1633
Real vz[MAXT]
cached dual-vertex position per triangle
Definition convex_cell.hpp:151
static KOKKOS_INLINE_FUNCTION Real det3(const Real a[3], const Real b[3], const Real c[3])
Definition convex_cell.hpp:1021
static KOKKOS_INLINE_FUNCTION void xprod(const Real a[3], const Real b[3], Real o[3])
Definition convex_cell.hpp:1013
Definition convex_cell.hpp:47
Real d[K]
Definition convex_cell.hpp:49
Real v
Definition convex_cell.hpp:48