pure-cpp 1.0.0
A C++ physics simulation benchmark comparing performance with Python implementations
kdtree.hpp
Go to the documentation of this file.
1#ifndef KDTREE_HPP
2#define KDTREE_HPP
3
4/**
5 * \file kdtree.hpp
6 * \brief A k-d tree wrapper for broad-phase collision detection using
7 * nanoflann.
8 * \author Le Bars, Yoann
9 * \ingroup PhysicsAcceleration
10 *
11 * This file is part of the pure C++ benchmark.
12 */
13
14#include <nanoflann.hpp>
15
16#include "body.hpp"
17
18namespace Model {
19
20 /**
21 * \brief An adapter to allow nanoflann to work directly with our `Bodies`
22 * SoA container.
23 *
24 * Note: The method names in this class follow the `snake_case` convention
25 * required by the nanoflann library's adapter interface, rather than the
26 * project's standard `camelCase`.
27 * \ingroup PhysicsAcceleration
28 */
30 public:
31 /**
32 * \brief Construct a new Bodies Adaptor object.
33 * \param in_bodies The `Bodies` container.
34 */
35 explicit BodiesAdaptor(const Bodies& in_bodies) : bodies_(in_bodies) {}
36
37 /**
38 * \brief Returns the number of bodies (points) in the dataset.
39 * \return The number of bodies.
40 */
41 [[nodiscard]] inline std::size_t kdtree_get_point_count() const {
42 return bodies_.size();
43 }
44
45 /**
46 * \brief Returns the value of a single dimension of a point.
47 * \param idx The index of the point.
48 * \param dim The dimension (0 for x, 1 for y, 2 for z).
49 * \return The coordinate value.
50 */
51 [[nodiscard]] inline double kdtree_get_pt(const std::size_t idx,
52 const std::size_t dim) const {
53 return bodies_.x_[idx][dim];
54 }
55
56 /**
57 * \brief Optional bounding box calculation (not used here).
58 * \tparam BBOX The type of the bounding box.
59 * \return Always false.
60 */
61 template <class BBOX>
62 bool kdtree_get_bbox(BBOX& /*bb*/) const {
63 return false;
64 }
65
66 private:
67 /// \brief A const reference to the bodies container.
69 };
70
71 /**
72 * \brief A k-d tree for fast nearest neighbour searches, specialized for
73 * our `Bodies` container.
74 */
75 class KDTree {
76 public:
77 /// \brief The specific KD-Tree type using our adapter.
78 using TreeType = nanoflann::KDTreeSingleIndexAdaptor<
79 nanoflann::L2_Simple_Adaptor<double, BodiesAdaptor>,
80 BodiesAdaptor, // NOLINT
81 3 /* dimensionality */, std::size_t /* index type */>;
82
83 /**
84 * \brief Construct a new KDTree object and build the index.
85 * \param in_bodies The `Bodies` container to build the tree from.
86 */
87 explicit KDTree(const Bodies& in_bodies)
88 : adaptor_(in_bodies),
89 tree_(3 /* dimensionality */, adaptor_,
90 // Set leaf_max_size to 10. This is a good balance for
91 // performance.
92 nanoflann::KDTreeSingleIndexAdaptorParams(10)) {
93 tree_.buildIndex();
94 }
95
96 /**
97 * \brief Finds all bodies within a given radius of a query point.
98 *
99 * \param query_point The centre point of the search.
100 * \param search_radius The radius of the search sphere.
101 * \param out_results A vector to store the results (pairs of index and
102 * squared distance).
103 * \return The number of neighbours found.
104 */
105 template <typename VectorOfPairs>
106 std::size_t radiusSearch(const double* query_point,
107 const double search_radius,
108 VectorOfPairs& out_results) {
109/* The SearchParameters class was renamed from SearchParams in v1.5.0.
110 We check for the legacy version macro to decide which API to use. */
111#if defined(NANOFLANN_VERSION) && NANOFLANN_VERSION < 0x150
112 // Use the legacy API for nanoflann < 1.5.0
113 return tree_.radiusSearch(query_point,
114 search_radius * search_radius,
115 out_results, nanoflann::SearchParams());
116#else
117 // Use the modern API for nanoflann >= 1.5.0
118 return tree_.radiusSearch(
119 query_point, search_radius * search_radius, out_results,
120 nanoflann::SearchParameters());
121#endif
122 }
123
124 private:
125 /// \brief The adapter that lets nanoflann access our data.
127 /// \brief The underlying nanoflann k-d tree index.
129 };
130
131} // namespace Model
132
133#endif // KDTREE_HPP
SoA container for simulation bodies and proxies for AoS-like access.
An adapter to allow nanoflann to work directly with our Bodies SoA container.
Definition: kdtree.hpp:29
bool kdtree_get_bbox(BBOX &) const
Optional bounding box calculation (not used here).
Definition: kdtree.hpp:62
BodiesAdaptor(const Bodies &in_bodies)
Construct a new Bodies Adaptor object.
Definition: kdtree.hpp:35
double kdtree_get_pt(const std::size_t idx, const std::size_t dim) const
Returns the value of a single dimension of a point.
Definition: kdtree.hpp:51
const Bodies & bodies_
A const reference to the bodies container.
Definition: kdtree.hpp:68
std::size_t kdtree_get_point_count() const
Returns the number of bodies (points) in the dataset.
Definition: kdtree.hpp:41
Structure-of-Arrays (SoA) container for all bodies in the simulation.
Definition: body.hpp:274
std::size_t size() const
Get number of bodies in the simulation.
Definition: body.hpp:371
std::vector< Vector3d > x_
Bodies' positions.
Definition: body.hpp:470
A k-d tree for fast nearest neighbour searches, specialized for our Bodies container.
Definition: kdtree.hpp:75
BodiesAdaptor adaptor_
The adapter that lets nanoflann access our data.
Definition: kdtree.hpp:126
nanoflann::KDTreeSingleIndexAdaptor< nanoflann::L2_Simple_Adaptor< double, BodiesAdaptor >, BodiesAdaptor, 3, std::size_t > TreeType
The specific KD-Tree type using our adapter.
Definition: kdtree.hpp:81
TreeType tree_
The underlying nanoflann k-d tree index.
Definition: kdtree.hpp:128
std::size_t radiusSearch(const double *query_point, const double search_radius, VectorOfPairs &out_results)
Finds all bodies within a given radius of a query point.
Definition: kdtree.hpp:106
KDTree(const Bodies &in_bodies)
Construct a new KDTree object and build the index.
Definition: kdtree.hpp:87