1 #pragma once
2
3 /// \file NeighborFinder.h
4 /// \brief Base class defining interface for kNN queries.
5 /// \author Pavel Sevecek (sevecek at sirrah.troja.mff.cuni.cz)
6 /// \date 2016-2021
7
8 #include "objects/containers/ArrayView.h"
9 #include "objects/finders/Order.h"
10 #include "objects/geometry/Vector.h"
11 #include "objects/wrappers/Flags.h"
12
13 NAMESPACE_SPH_BEGIN
14
15 class IScheduler;
16
17 /// \brief Holds information about a neighbor particles
18 struct NeighborRecord {
19 /// Index of particle in the storage
20 Size index;
21
22 /// Squared distance of the particle from the queried particle / position
23 Float distanceSqr;
24
25 bool operator!=(const NeighborRecord& other) const {
26 return index != other.index || distanceSqr != other.distanceSqr;
27 }
28
29 /// Sort by the distance
30 bool operator<(const NeighborRecord& other) const {
31 return distanceSqr < other.distanceSqr;
32 }
33 };
34
35 /// \brief Interface of objects finding neighboring particles.
36 ///
37 /// Provides queries for searching particles within given radius from given particle or given point in space.
38 /// Object has to be built before neighbor queries can be made.
39 class IBasicFinder : public Polymorphic {
40 protected:
41 /// View of the source datapoints, updated every time (re)build is called
42 ArrayView<const Vector> values;
43
44 public:
45 /// \brief Constructs the struct with an array of vectors.
46 ///
47 /// Must be called before \ref findAll is called or if the referenced array is invalidated.
48 /// \param scheduler Scheduler that can be used for parallelization.
49 /// \param points View of the array of points in space.
50 void build(IScheduler& scheduler, ArrayView<const Vector> points);
51
52 /// \brief Finds all neighbors within given radius from the point given by index.
53 ///
54 /// Point view passed in \ref build must not be invalidated, in particular the number of particles must
55 /// not change before function \ref findAll is called. Note that the particle itself (index-th particle)
56 /// is also included in the list of neighbors.
57 /// \param index Index of queried particle in the view given in \ref build function.
58 /// \param radius Radius in which the neighbors are searched. Must be a positive value.
59 /// \param neighbors Output parameter, containing the list of neighbors as indices to the array. The
60 /// array is cleared by the function.
61 /// \return The number of located neighbors.
62 virtual Size findAll(const Size index, const Float radius, Array<NeighborRecord>& neighbors) const = 0;
63
64 /// \brief Finds all points within given radius from given position.
65 ///
66 /// The position may not correspond to any point.
67 virtual Size findAll(const Vector& pos, const Float radius, Array<NeighborRecord>& neighbors) const = 0;
68
69 protected:
70 /// \brief Builds finder from set of vectors.
71 ///
72 /// This must be called before \ref findAll, can be called more than once.
73 /// \param scheduler Scheduler that can be used for parallelization.
74 /// \param points View of the array of points in space.
75 virtual void buildImpl(IScheduler& scheduler, ArrayView<const Vector> points) = 0;
76 };
77
78 enum class FinderFlag {
79 /// Creates the ranks of particles. Without this flag, only the IBasicFinder interface can be used.
80 MAKE_RANK = 1 << 0,
81
82 /// The rank of particles is not created. 'Dummy' option that can be used to improve readability.
83 SKIP_RANK = 0,
84 };
85
86 /// \brief Generates the ranks of particles, according to generic predicate.
87 template <typename TCompare>
makeRank(const Size size,TCompare && comp)88 static Order makeRank(const Size size, TCompare&& comp) {
89 Order tmp(size);
90 // sort using the given comparator
91 tmp.shuffle(comp);
92 // invert to get the rank
93 return tmp.getInverted();
94 }
95
96 /// \brief Extension of IBasicFinder, allowing to search only particles with lower rank in smoothing length.
97 ///
98 /// This is useful to find each pair of neighboring particles only once; if i-th particle 'sees' j-th
99 /// particle, j-th particle does not 'see' i-th particle. This can be a significant optimization as only half
100 /// of the neighbors is evaluated.
101 class ISymmetricFinder : public IBasicFinder {
102 protected:
103 /// Ranks of particles according to their smoothing lengths
104 Order rank;
105
106 public:
107 /// \brief Constructs the struct with an array of vectors.
108 void build(IScheduler& scheduler,
109 ArrayView<const Vector> points,
110 Flags<FinderFlag> flags = FinderFlag::MAKE_RANK);
111
112 /// \brief Constructs the struct with custom predicate for ordering particles.
113 template <typename TCompare>
buildWithRank(IScheduler & scheduler,ArrayView<const Vector> points,TCompare && comp)114 void buildWithRank(IScheduler& scheduler, ArrayView<const Vector> points, TCompare&& comp) {
115 values = points;
116 rank = makeRank(values.size(), comp);
117 this->buildImpl(scheduler, values);
118 }
119
120 /// \brief Finds all points within radius that have a lower rank in smoothing length.
121 ///
122 /// The sorting of particles with equal smoothing length is not specified, but it is ensured that all
123 /// found neighbors will not find the queries particle if \ref findAsymmetric is called with that
124 /// particle. Note that this does NOT find the queried particle itself.
125 /// \param index Index of queried particle in the view given in \ref build function.
126 /// \param radius Radius in which the neighbors are searched. Must be a positive value.
127 /// \param neighbors Output parameter, containing the list of neighbors as indices to the array. The
128 /// array is cleared by the function.
129 /// \return The number of located neighbors. Can be zero.
130 virtual Size findLowerRank(const Size index,
131 const Float radius,
132 Array<NeighborRecord>& neighbors) const = 0;
133 };
134
135 /// \brief Helper template, allowing to define all three functions with a single function.
136 template <typename TDerived>
137 class FinderTemplate : public ISymmetricFinder {
138 public:
findAll(const Size index,const Float radius,Array<NeighborRecord> & neighbors)139 virtual Size findAll(const Size index,
140 const Float radius,
141 Array<NeighborRecord>& neighbors) const override {
142 neighbors.clear();
143 return static_cast<const TDerived*>(this)->template find<true>(
144 values[index], index, radius, neighbors);
145 }
146
findAll(const Vector & pos,const Float radius,Array<NeighborRecord> & neighbors)147 virtual Size findAll(const Vector& pos,
148 const Float radius,
149 Array<NeighborRecord>& neighbors) const override {
150 neighbors.clear();
151 if (SPH_UNLIKELY(values.empty())) {
152 return 0;
153 }
154 // the index here is irrelevant, so let's use something that would cause assert in case we messed
155 // something up
156 const Size index = values.size();
157 return static_cast<const TDerived*>(this)->template find<true>(pos, index, radius, neighbors);
158 }
159
findLowerRank(const Size index,const Float radius,Array<NeighborRecord> & neighbors)160 virtual Size findLowerRank(const Size index,
161 const Float radius,
162 Array<NeighborRecord>& neighbors) const override {
163 neighbors.clear();
164 return static_cast<const TDerived*>(this)->template find<false>(
165 values[index], index, radius, neighbors);
166 }
167 };
168
169 NAMESPACE_SPH_END
170