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