10 template <
typename TNode,
typename TMetric>
17 const Size currentCnt = nodes.size();
21 entireBox.extend(i.value());
29 const Size nodeCnt =
max(2 * points.
size() / config.leafSize + 1, currentCnt);
30 nodes.resize(nodeCnt);
33 this->buildTree(scheduler, ROOT_PARENT_NODE,
KdChild(-1), 0, points.
size(), entireBox, 0, 0);
38 nodes.resize(nodeCounter);
40 SPH_ASSERT(this->sanityCheck(), this->sanityCheck().error());
43 template <
typename TNode,
typename TMetric>
50 const Size slidingCnt,
59 bool slidingMidpoint =
false;
60 bool degeneratedBox =
false;
62 if (to - from <= config.leafSize) {
64 this->addLeaf(parent, child, from, to);
68 for (
Size dim = 0; dim < 3; ++dim) {
69 if (this->isSingular(from, to, splitIdx)) {
70 boxSize[splitIdx] = 0.f;
72 splitIdx =
argMax(boxSize);
74 if (boxSize ==
Vector(0._f)) {
77 SPH_ASSERT(
false,
"Too many overlapping points, something is probably wrong ...");
78 degeneratedBox =
true;
88 std::make_signed_t<Size> n1 = from, n2 = to - 1;
90 if (slidingCnt <= 5 && !degeneratedBox) {
92 for (; n1 < int(to) && this->values[idxs[n1]][splitIdx] <= splitPosition; ++n1)
94 for (; n2 >= int(from) && this->values[idxs[n2]][splitIdx] >= splitPosition; --n2)
101 if (n1 ==
int(from)) {
103 splitPosition = this->values[idxs[from]][splitIdx];
104 for (
Size i = from + 1; i < to; ++i) {
105 const Float x1 = this->values[idxs[i]][splitIdx];
106 if (x1 < splitPosition) {
113 slidingMidpoint =
true;
114 }
else if (n1 ==
int(to)) {
116 splitPosition = this->values[idxs[from]][splitIdx];
117 for (
Size i = from + 1; i < to; ++i) {
118 const Float x2 = this->values[idxs[i]][splitIdx];
119 if (x2 > splitPosition) {
126 slidingMidpoint =
true;
129 tie(box1, box2) = box.
split(splitIdx, splitPosition);
131 n1 = (from + to) >> 1;
134 if (!degeneratedBox) {
135 std::nth_element(iter + from, iter + n1, iter + to, [
this, splitIdx](
Size i1,
Size i2) {
136 return this->values[i1][splitIdx] < this->values[i2][splitIdx];
140 tie(box1, box2) = box.
split(splitIdx, this->values[idxs[n1]][splitIdx]);
144 SPH_ASSERT(this->checkBoxes(from, to, n1, box1, box2));
147 const Size index = this->addInner(parent, child, splitPosition, splitIdx);
150 const Size nextSlidingCnt = slidingMidpoint ? slidingCnt + 1 : 0;
151 auto processRightSubTree = [
this, &scheduler, index, to, n1, box2, nextSlidingCnt, depth] {
152 this->buildTree(scheduler, index,
KdChild::RIGHT, n1, to, box2, nextSlidingCnt, depth + 1);
154 if (depth < config.maxParallelDepth) {
157 scheduler.
submit(processRightSubTree);
160 processRightSubTree();
162 this->buildTree(scheduler, index,
KdChild::LEFT, from, n1, box1, nextSlidingCnt, depth + 1);
166 template <
typename TNode,
typename TMetric>
168 const Size index = nodeCounter++;
169 if (index >= nodes.size()) {
173 nodes.resize(
max(2 * index, nodes.size()));
177 nodesMutex.lock_shared();
178 auto releaseLock =
finally([
this] { nodesMutex.unlock_shared(); });
193 for (
Size i = from; i < to; ++i) {
194 box.
extend(this->values[idxs[i]]);
198 if (parent == ROOT_PARENT_NODE) {
205 parentNode.
left = index;
209 parentNode.
right = index;
213 template <
typename TNode,
typename TMetric>
216 const Float splitPosition,
217 const Size splitIdx) {
219 "Invalid values of KdNode::Type enum");
221 const Size index = nodeCounter++;
222 if (index >= nodes.size()) {
226 nodes.resize(
max(2 * index, nodes.size()));
230 nodesMutex.lock_shared();
231 auto releaseLock =
finally([
this] { nodesMutex.unlock_shared(); });
243 if (parent == ROOT_PARENT_NODE) {
251 parentNode.
left = index;
256 parentNode.
right = index;
262 template <
typename TNode,
typename TMetric>
270 template <
typename TNode,
typename TMetric>
272 for (
Size i = from; i < to; ++i) {
273 if (this->values[idxs[i]][splitIdx] != this->values[idxs[to - 1]][splitIdx]) {
280 template <
typename TNode,
typename TMetric>
285 const Box& box2)
const {
286 for (
Size i = from; i < to; ++i) {
287 if (i < mid && !box1.
contains(this->values[idxs[i]])) {
290 if (i >= mid && !box2.
contains(this->values[idxs[i]])) {
314 template <
typename TNode,
typename TMetric>
315 template <
bool FindAll>
323 const Vector maxDistSqr =
sqr(
max(
Vector(0._f), entireBox.lower() - r0, r0 - entireBox.upper()));
332 while (node.distanceSqr < radiusSqr) {
333 if (nodes[node.idx].isLeaf()) {
336 if (leaf.
size() > 0) {
337 const Float leafDistSqr =
338 metric(
max(
Vector(0._f), leaf.box.lower() - r0, r0 - leaf.box.upper()));
339 if (leafDistSqr < radiusSqr) {
342 const Size actIndex = idxs[i];
343 const Float distSqr = metric(this->values[actIndex] - r0);
344 if (distSqr < radiusSqr && (FindAll || this->rank[actIndex] < this->rank[index])) {
358 const Size splitDimension =
Size(inner.type);
361 if (r0[splitDimension] < splitPosition) {
364 node.idx = inner.
left;
366 const Float dx = splitPosition - r0[splitDimension];
377 node.idx = inner.
right;
378 const Float dx = splitPosition - r0[splitDimension];
390 return neighbours.
size();
393 template <
typename TNode,
typename TMetric>
395 if (this->values.size() != idxs.size()) {
396 return makeFailed(
"Number of values does not match the number of indices");
400 for (
const Vector& v : this->values) {
401 if (!entireBox.contains(v)) {
402 return makeFailed(
"Points are not strictly within the bounding box");
408 std::set<Size> indices;
416 if (idx >= nodes.size()) {
417 return makeFailed(
"Invalid index found: ", idx,
" (", nodes.size(),
")");
421 if (!nodes[idx].isLeaf()) {
423 return countNodes(inner.
left) && countNodes(inner.
right);
427 if (leaf.
to == leaf.
from) {
432 if (!leaf.box.contains(this->values[idxs[i]])) {
433 return makeFailed(
"Leaf points do not fit inside the bounding box");
435 if (indices.find(i) != indices.end()) {
444 const Outcome result = countNodes(0);
449 if (counter != nodes.size()) {
450 return makeFailed(
"Unexpected number of nodes: ", counter,
" == ", nodes.size());
454 for (
Size idx : indices) {
457 return makeFailed(
"Invalid index: ", idx,
" == ", i);
464 template <IterateDirection Dir,
typename TNode,
typename TMetric,
typename TFunctor>
467 const TFunctor& functor,
469 const Size depthLimit) {
470 TNode& node = tree.
getNode(nodeIdx);
473 functor(node,
nullptr,
nullptr);
482 if (!node.isLeaf()) {
485 const Size newDepth = depthLimit == 0 ? 0 : depthLimit - 1;
486 auto iterateRightSubtree = [&tree, &scheduler, &functor, &inner, newDepth] {
487 iterateTree<Dir>(tree, scheduler, functor, inner.
right, newDepth);
490 task = scheduler.submit(iterateRightSubtree);
492 iterateRightSubtree();
494 iterateTree<Dir>(tree, scheduler, functor, inner.
left, newDepth);
501 functor(node,
nullptr,
nullptr);
510 template <IterateDirection Dir,
typename TNode,
typename TMetric,
typename TFunctor>
513 const TFunctor& functor,
515 const Size depthLimit) {
517 auto actFunctor = [&functor](TNode& node, TNode* left, TNode* right)
518 INL {
return functor(
asConst(node), left, right); };
#define SPH_ASSERT(x,...)
uint32_t Size
Integral type used to index arrays (by default).
double Float
Precision used withing the code. Use Float instead of float or double where precision is important.
IndexAdapter< TContainer > iterateWithIndex(TContainer &&container)
K-d tree for efficient search of neighbouring particles.
@ BOTTOM_UP
From leaves to root.
@ TOP_DOWN
From root to leaves.
thread_local Array< ProcessedNode > nodeStack
Cached stack to avoid reallocation.
void iterateTree(KdTree< TNode, TMetric > &tree, IScheduler &scheduler, const TFunctor &functor, const Size nodeIdx, const Size depthLimit)
Calls a functor for every node of a K-d tree tree in specified direction.
#define VERBOSE_LOG
Helper macro, creating.
constexpr INLINE T max(const T &f1, const T &f2)
constexpr INLINE T sqr(const T &f) noexcept
Return a squared value.
#define NAMESPACE_SPH_END
BasicOutcome< std::string > Outcome
Alias for string error message.
const SuccessTag SUCCESS
Global constant for successful outcome.
INLINE Outcome makeFailed(TArgs &&... args)
Constructs failed object with error message.
StaticArray< T0 &, sizeof...(TArgs)+1 > tie(T0 &t0, TArgs &... rest)
Creates a static array from a list of l-value references.
INLINE const T & asConst(T &ref)
Converts a non-const reference to const one.
INLINE Size argMax(const Vector &v)
Returns the index of the maximum element.
BasicVector< Float > Vector
INLINE Float l1Norm(const Vector &v)
Returns the L1 norm (sum of absolute values) of the vector.
Object providing safe access to continuous memory of data.
INLINE bool empty() const
INLINE TCounter size() const
Generic dynamically allocated resizable storage.
INLINE void push(U &&u)
Adds new element to the end of the array, resizing the array if necessary.
INLINE TCounter size() const noexcept
INLINE bool empty() const noexcept
Helper object defining three-dimensional interval (box).
INLINE void extend(const Vector &v)
Enlarges the box to contain the vector.
INLINE Vector center() const
Returns the center of the box.
INLINE bool contains(const Vector &v) const
Checks if the vector lies inside the box.
INLINE Vector size() const
Returns box dimensions.
INLINE Pair< Box > split(const Size dim, const Float x) const
Splits the box along given coordinate.
Interface that allows unified implementation of sequential and parallelized versions of algorithms.
virtual SharedPtr< ITask > submit(const Function< void()> &task)=0
Submits a task to be potentially executed asynchronously.
Simple (forward) iterator over continuous array of objects of type T.
K-d tree, used for hierarchical clustering of particles and accelerated Kn queries.
INLINE TNode & getNode(const Size nodeIdx)
Returns the node with given index.
virtual void buildImpl(IScheduler &scheduler, ArrayView< const Vector > points) override
Builds finder from set of vectors.
Size find(const Vector &pos, const Size index, const Float radius, Array< NeighbourRecord > &neighs) const
Outcome sanityCheck() const
Performs some checks of KdTree consistency, returns SUCCESS if everything is OK.
void swap(Sph::Array< T, TCounter > &ar1, Sph::Array< T, TCounter > &ar2)
Size right
Index of right child node.
Size left
Index of left child node.
float splitPosition
Position where the selected dimension is split.
Type
Here X, Y, Z must be 0, 1, 2.
Leaf (bucket) node of K-d tree.
Size to
One-past-last index of particles belonging to the leaf.
INLINE Size size() const
Returns the number of points in the leaf. Can be zero.
Size from
First index of particlse belonging to the leaf.
Holds information about a neighbour particles.
Object used during traversal.
Size idx
Index into the nodeStack array. We cannot use pointers because the array might get reallocated.