peclet-dem
Performance-portable XPBD Discrete Element Method (Kokkos + ArborX)
Loading...
Searching...
No Matches
broadphase_arborx.hpp
Go to the documentation of this file.
1
12#ifndef DEM_BROADPHASE_ARBORX_HPP
13#define DEM_BROADPHASE_ARBORX_HPP
14
15#include <ArborX.hpp>
16#include <Kokkos_Core.hpp>
17#include <utility>
18
19namespace peclet::dem {
20
21using BpExec = Kokkos::DefaultExecutionSpace;
22using BpMem = BpExec::memory_space;
23
32// View params are templated so callers can pass managed Views (tests) OR unmanaged Views wrapping
33// raw device buffers (the CUDA dem module's float4*/int2* arrays). Element types/layout must be
34// the usual (positions [N][3], radii [N], pairs [N][2], count scalar).
35template <class PosV, class RadV, class PairsV, class CountV>
36inline int findCollisionsArborX(PosV pos, RadV rad, int numParticles, int numReal, float margin,
37 PairsV outPairs, CountV outCount, float boxCap = 0.0f) {
38 BpExec space;
39 using Box = ArborX::Box<3>;
40
41 // Sanitize the AABB half-width + centre: a non-finite (NaN/inf) or absurdly large box — a particle
42 // blown up by a bad coupling/DEM transient — would otherwise overlap ~every other box and make the
43 // ArborX query allocate billions of intersections (observed: a 36 GiB "bp::values" OOM). SANE_BOX
44 // parks such a particle in a degenerate far box that matches nothing, so one bad particle can't
45 // crash the run. boxCap<=0 disables the size clamp (NaN/inf are always caught).
46#define PECLET_DEM_SANE_BOX(i, px, py, pz, b) \
47 float px = pos(i, 0), py = pos(i, 1), pz = pos(i, 2), b = rad(i) + margin; \
48 if (!(b == b) || !(px == px) || !(py == py) || !(pz == pz) || \
49 (boxCap > 0.0f && b > boxCap)) { \
50 px = py = pz = -1.0e30f; \
51 b = 0.0f; \
52 }
53
54 // AABBs over all particles (these are the BVH primitives).
55 Kokkos::View<Box*, BpMem> boxes(
56 Kokkos::view_alloc(space, "peclet::dem::bp::boxes", Kokkos::WithoutInitializing),
57 numParticles);
58 Kokkos::parallel_for(
59 "peclet::dem::bp::aabb", Kokkos::RangePolicy<BpExec>(space, 0, numParticles),
60 KOKKOS_LAMBDA(int i) {
61 PECLET_DEM_SANE_BOX(i, px, py, pz, b);
62 boxes(i) = Box{{px - b, py - b, pz - b}, {px + b, py + b, pz + b}};
63 });
64
65 ArborX::BoundingVolumeHierarchy const tree(space, ArborX::Experimental::attach_indices(boxes));
66
67 // One intersection query per real particle (box of the same half-width).
68 using Predicate = decltype(ArborX::intersects(std::declval<Box>()));
69 Kokkos::View<Predicate*, BpMem> preds(
70 Kokkos::view_alloc(space, "peclet::dem::bp::preds", Kokkos::WithoutInitializing), numReal);
71 Kokkos::parallel_for(
72 "peclet::dem::bp::preds", Kokkos::RangePolicy<BpExec>(space, 0, numReal),
73 KOKKOS_LAMBDA(int i) {
74 PECLET_DEM_SANE_BOX(i, px, py, pz, b);
75 preds(i) = ArborX::intersects(Box{{px - b, py - b, pz - b}, {px + b, py + b, pz + b}});
76 });
77#undef PECLET_DEM_SANE_BOX
78
79 Kokkos::View<typename decltype(tree)::value_type*, BpMem> values("peclet::dem::bp::values", 0);
80 Kokkos::View<int*, BpMem> offsets("peclet::dem::bp::offsets", 0);
81 tree.query(space, preds, values, offsets);
82
83 // Compact the (i, j) results with i<j into the output buffer (matches cuBQL's i<j filter).
84 Kokkos::deep_copy(space, outCount, 0);
85 const int maxPairs = static_cast<int>(outPairs.extent(0));
86 Kokkos::View<int* [2], BpMem> pairs = outPairs;
87 Kokkos::View<int, BpMem> cnt = outCount;
88 Kokkos::parallel_for(
89 "peclet::dem::bp::emit", Kokkos::RangePolicy<BpExec>(space, 0, numReal),
90 KOKKOS_LAMBDA(int i) {
91 for (int k = offsets(i); k < offsets(i + 1); ++k) {
92 const int j = values(k).index;
93 if (i < j) {
94 const int slot = Kokkos::atomic_fetch_add(&cnt(), 1);
95 if (slot < maxPairs) {
96 pairs(slot, 0) = i;
97 pairs(slot, 1) = j;
98 }
99 }
100 }
101 });
102 space.fence();
103
104 int h_count = 0;
105 Kokkos::deep_copy(h_count, outCount);
106 return h_count;
107}
108
109} // namespace peclet::dem
110
111#endif // DEM_BROADPHASE_ARBORX_HPP
#define PECLET_DEM_SANE_BOX(i, px, py, pz, b)
Kokkos::DefaultExecutionSpace BpExec
BpExec::memory_space BpMem
int findCollisionsArborX(PosV pos, RadV rad, int numParticles, int numReal, float margin, PairsV outPairs, CountV outCount, float boxCap=0.0f)
Emit candidate collision pairs (i<j) for real particles into outPairs/outCount.