peclet-dem 1.1.0
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
42 // particle blown up by a bad coupling/DEM transient — would otherwise overlap ~every other box
43 // and make the ArborX query allocate billions of intersections (observed: a 36 GiB "bp::values"
44 // OOM). SANE_BOX parks such a particle in a degenerate far box that matches nothing, so one bad
45 // particle can't 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) || (boxCap > 0.0f && b > boxCap)) { \
49 px = py = pz = -1.0e30f; \
50 b = 0.0f; \
51 }
52
53 // AABBs over all particles (these are the BVH primitives).
54 Kokkos::View<Box*, BpMem> boxes(
55 Kokkos::view_alloc(space, "peclet::dem::bp::boxes", Kokkos::WithoutInitializing),
56 numParticles);
57 Kokkos::parallel_for(
58 "peclet::dem::bp::aabb", Kokkos::RangePolicy<BpExec>(space, 0, numParticles),
59 KOKKOS_LAMBDA(int i) {
60 PECLET_DEM_SANE_BOX(i, px, py, pz, b);
61 boxes(i) = Box{{px - b, py - b, pz - b}, {px + b, py + b, pz + b}};
62 });
63
64 ArborX::BoundingVolumeHierarchy const tree(space, ArborX::Experimental::attach_indices(boxes));
65
66 // One intersection query per real particle (box of the same half-width).
67 using Predicate = decltype(ArborX::intersects(std::declval<Box>()));
68 Kokkos::View<Predicate*, BpMem> preds(
69 Kokkos::view_alloc(space, "peclet::dem::bp::preds", Kokkos::WithoutInitializing), numReal);
70 Kokkos::parallel_for(
71 "peclet::dem::bp::preds", Kokkos::RangePolicy<BpExec>(space, 0, numReal),
72 KOKKOS_LAMBDA(int i) {
73 PECLET_DEM_SANE_BOX(i, px, py, pz, b);
74 preds(i) = ArborX::intersects(Box{{px - b, py - b, pz - b}, {px + b, py + b, pz + b}});
75 });
76#undef PECLET_DEM_SANE_BOX
77
78 Kokkos::View<typename decltype(tree)::value_type*, BpMem> values("peclet::dem::bp::values", 0);
79 Kokkos::View<int*, BpMem> offsets("peclet::dem::bp::offsets", 0);
80 tree.query(space, preds, values, offsets);
81
82 // Compact the (i, j) results with i<j into the output buffer (matches cuBQL's i<j filter).
83 Kokkos::deep_copy(space, outCount, 0);
84 const int maxPairs = static_cast<int>(outPairs.extent(0));
85 Kokkos::View<int* [2], BpMem> pairs = outPairs;
86 Kokkos::View<int, BpMem> cnt = outCount;
87 Kokkos::parallel_for(
88 "peclet::dem::bp::emit", Kokkos::RangePolicy<BpExec>(space, 0, numReal),
89 KOKKOS_LAMBDA(int i) {
90 for (int k = offsets(i); k < offsets(i + 1); ++k) {
91 const int j = values(k).index;
92 if (i < j) {
93 const int slot = Kokkos::atomic_fetch_add(&cnt(), 1);
94 if (slot < maxPairs) {
95 pairs(slot, 0) = i;
96 pairs(slot, 1) = j;
97 }
98 }
99 }
100 });
101 space.fence();
102
103 int h_count = 0;
104 Kokkos::deep_copy(h_count, outCount);
105 return h_count;
106}
107
108} // namespace peclet::dem
109
110#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.