DOLFINx 0.10.0.0
DOLFINx C++ interface
Loading...
Searching...
No Matches
BoundingBoxTree.h
1// Copyright (C) 2013-2022 Chris N. Richardson, Anders Logg, Garth N. Wells,
2// Jørgen S. Dokken, Sarah Roggendorf
3//
4// This file is part of DOLFINx (https://www.fenicsproject.org)
5//
6// SPDX-License-Identifier: LGPL-3.0-or-later
7
8#pragma once
9
10#include <algorithm>
11#include <array>
12#include <cassert>
13#include <cstdint>
14#include <dolfinx/mesh/utils.h>
15#include <mpi.h>
16#include <optional>
17#include <span>
18#include <string>
19#include <vector>
20
22{
23namespace impl_bb
24{
25//-----------------------------------------------------------------------------
26// Compute bounding box of mesh entity. The bounding box is defined by (lower
27// left corner, top right corner). Storage flattened row-major
28template <std::floating_point T>
29std::array<T, 6> compute_bbox_of_entity(const mesh::Mesh<T>& mesh, int dim,
30 std::int32_t index)
31{
32 // Get the geometrical indices for the mesh entity
33 std::span<const T> xg = mesh.geometry().x();
34
35 // FIXME: return of small dynamic array is expensive
36 std::span<const std::int32_t> entity(&index, 1);
37 const std::vector<std::int32_t> vertex_indices
38 = mesh::entities_to_geometry(mesh, dim, entity, false);
39
40 std::array<T, 6> b;
41 std::span<T, 3> b0(b.data(), 3);
42 std::span<T, 3> b1(b.data() + 3, 3);
43
44 std::copy_n(std::next(xg.begin(), 3 * vertex_indices.front()), 3, b0.begin());
45 std::copy_n(std::next(xg.begin(), 3 * vertex_indices.front()), 3, b1.begin());
46
47 // Compute min and max over vertices
48 for (std::int32_t local_vertex : vertex_indices)
49 {
50 for (std::size_t j = 0; j < 3; ++j)
51 {
52 b0[j] = std::min(b0[j], xg[3 * local_vertex + j]);
53 b1[j] = std::max(b1[j], xg[3 * local_vertex + j]);
54 }
55 }
56
57 return b;
58}
59//-----------------------------------------------------------------------------
60// Compute bounding box of bounding boxes. Each bounding box is defined as a
61// tuple (corners, entity_index). The corners of the bounding box is flattened
62// row-major as (lower left corner, top right corner).
63template <std::floating_point T>
64std::array<T, 6> compute_bbox_of_bboxes(
65 std::span<const std::pair<std::array<T, 6>, std::int32_t>> leaf_bboxes)
66{
67 // Compute min and max over remaining boxes
68 std::array<T, 6> b = leaf_bboxes.front().first;
69 for (auto [box, _] : leaf_bboxes)
70 {
71 std::transform(box.cbegin(), std::next(box.cbegin(), 3), b.cbegin(),
72 b.begin(), [](auto a, auto b) { return std::min(a, b); });
73 std::transform(std::next(box.cbegin(), 3), box.cend(),
74 std::next(b.cbegin(), 3), std::next(b.begin(), 3),
75 [](auto a, auto b) { return std::max(a, b); });
76 }
77
78 return b;
79}
80//------------------------------------------------------------------------------
81template <std::floating_point T>
82std::int32_t _build_from_leaf(
83 std::span<std::pair<std::array<T, 6>, std::int32_t>> leaf_bboxes,
84 std::vector<int>& bboxes, std::vector<T>& bbox_coordinates)
85{
86 if (leaf_bboxes.size() == 1)
87 {
88 // Reached leaf
89
90 // Get bounding box coordinates for leaf
91 const auto [b, entity_index] = leaf_bboxes.front();
92
93 // Store bounding box data
94 bboxes.push_back(entity_index);
95 bboxes.push_back(entity_index);
96 std::copy_n(b.begin(), 6, std::back_inserter(bbox_coordinates));
97 return bboxes.size() / 2 - 1;
98 }
99 else
100 {
101 // Compute bounding box of all bounding boxes
102 std::array b = compute_bbox_of_bboxes<T>(leaf_bboxes);
103
104 // Sort bounding boxes along longest axis
105 std::array<T, 3> b_diff;
106 std::transform(std::next(b.cbegin(), 3), b.cend(), b.cbegin(),
107 b_diff.begin(), std::minus<T>());
108 const std::size_t axis = std::distance(
109 b_diff.begin(), std::max_element(b_diff.begin(), b_diff.end()));
110
111 auto middle = std::next(leaf_bboxes.begin(), leaf_bboxes.size() / 2);
112 std::nth_element(leaf_bboxes.begin(), middle, leaf_bboxes.end(),
113 [axis](auto& p0, auto& p1) -> bool
114 {
115 auto x0 = p0.first[axis] + p0.first[3 + axis];
116 auto x1 = p1.first[axis] + p1.first[3 + axis];
117 return x0 < x1;
118 });
119
120 // Split bounding boxes into two groups and call recursively
121 assert(!leaf_bboxes.empty());
122 std::size_t part = leaf_bboxes.size() / 2;
123 std::int32_t bbox0
124 = _build_from_leaf(leaf_bboxes.first(part), bboxes, bbox_coordinates);
125 std::int32_t bbox1 = _build_from_leaf(
126 leaf_bboxes.last(leaf_bboxes.size() - part), bboxes, bbox_coordinates);
127
128 // Store bounding box data. Note that root box will be added last.
129 bboxes.push_back(bbox0);
130 bboxes.push_back(bbox1);
131 std::copy_n(b.begin(), 6, std::back_inserter(bbox_coordinates));
132 return bboxes.size() / 2 - 1;
133 }
134}
135//-----------------------------------------------------------------------------
136template <std::floating_point T>
137std::pair<std::vector<std::int32_t>, std::vector<T>> build_from_leaf(
138 std::vector<std::pair<std::array<T, 6>, std::int32_t>>& leaf_bboxes)
139{
140 std::vector<std::int32_t> bboxes;
141 std::vector<T> bbox_coordinates;
142 impl_bb::_build_from_leaf<T>(leaf_bboxes, bboxes, bbox_coordinates);
143 return {std::move(bboxes), std::move(bbox_coordinates)};
144}
145//-----------------------------------------------------------------------------
146template <std::floating_point T>
147std::int32_t
148_build_from_point(std::span<std::pair<std::array<T, 3>, std::int32_t>> points,
149 std::vector<std::int32_t>& bboxes,
150 std::vector<T>& bbox_coordinates)
151{
152 // Reached leaf
153 if (points.size() == 1)
154 {
155 // Store bounding box data
156
157 // Index of entity contained in leaf
158 const std::int32_t c1 = points[0].second;
159 bboxes.push_back(c1);
160 bboxes.push_back(c1);
161 bbox_coordinates.insert(bbox_coordinates.end(), points[0].first.begin(),
162 points[0].first.end());
163 bbox_coordinates.insert(bbox_coordinates.end(), points[0].first.begin(),
164 points[0].first.end());
165 return bboxes.size() / 2 - 1;
166 }
167
168 // Compute bounding box of all points
169 auto [min, max] = std::ranges::minmax_element(points);
170 std::array<T, 3> b0 = min->first;
171 std::array<T, 3> b1 = max->first;
172
173 // Sort bounding boxes along longest axis
174 std::array<T, 3> b_diff;
175 std::ranges::transform(b1, b0, b_diff.begin(), std::minus<T>());
176 const std::size_t axis
177 = std::distance(b_diff.begin(), std::ranges::max_element(b_diff));
178
179 auto middle = std::next(points.begin(), points.size() / 2);
180 std::nth_element(points.begin(), middle, points.end(),
181 [axis](auto& p0, auto&& p1) -> bool
182 { return p0.first[axis] < p1.first[axis]; });
183
184 // Split bounding boxes into two groups and call recursively
185 assert(!points.empty());
186 std::size_t part = points.size() / 2;
187 std::int32_t bbox0
188 = _build_from_point(points.first(part), bboxes, bbox_coordinates);
189 std::int32_t bbox1 = _build_from_point(points.last(points.size() - part),
190 bboxes, bbox_coordinates);
191
192 // Store bounding box data. Note that root box will be added last.
193 bboxes.push_back(bbox0);
194 bboxes.push_back(bbox1);
195 bbox_coordinates.insert(bbox_coordinates.end(), b0.begin(), b0.end());
196 bbox_coordinates.insert(bbox_coordinates.end(), b1.begin(), b1.end());
197 return bboxes.size() / 2 - 1;
198}
199//-----------------------------------------------------------------------------
200} // namespace impl_bb
201
204template <std::floating_point T>
206{
207private:
213 static std::vector<std::int32_t> range(mesh::Topology& topology, int tdim)
214 {
215 topology.create_entities(tdim);
216 auto map = topology.index_map(tdim);
217 assert(map);
218 const std::int32_t num_entities = map->size_local() + map->num_ghosts();
219 std::vector<std::int32_t> r(num_entities);
220 std::iota(r.begin(), r.end(), 0);
221 return r;
222 }
223
224public:
235 std::optional<std::span<const std::int32_t>> entities
236 = std::nullopt,
237 double padding = 0)
238 : _tdim(tdim)
239 {
240 // Initialize entities of given dimension if they don't exist
241 mesh.topology_mutable()->create_entities(tdim);
242
243 // Get input entities. If not provided, get all local entities of the given
244 // dimension (including ghosts)
245 std::span<const std::int32_t> entities_span;
246 std::optional<std::vector<std::int32_t>> local_range(std::nullopt);
247 if (entities)
248 entities_span = entities.value();
249 else
250 {
251 local_range.emplace(range(*mesh.topology_mutable(), tdim));
252 entities_span = std::span<const std::int32_t>(local_range->data(),
253 local_range->size());
254 }
255
256 if (tdim < 0 or tdim > mesh.topology()->dim())
257 {
258 throw std::runtime_error(
259 "Dimension must be non-negative and less than or "
260 "equal to the topological dimension of the mesh");
261 }
262
263 mesh.topology_mutable()->create_connectivity(tdim, mesh.topology()->dim());
264
265 // Create bounding boxes for all mesh entities (leaves)
266 std::vector<std::pair<std::array<T, 6>, std::int32_t>> leaf_bboxes;
267 leaf_bboxes.reserve(entities_span.size());
268 for (std::int32_t e : entities_span)
269 {
270 std::array<T, 6> b = impl_bb::compute_bbox_of_entity(mesh, tdim, e);
271 std::transform(b.cbegin(), std::next(b.cbegin(), 3), b.begin(),
272 [padding](auto x) { return x - padding; });
273 std::transform(std::next(b.begin(), 3), b.end(), std::next(b.begin(), 3),
274 [padding](auto x) { return x + padding; });
275 leaf_bboxes.emplace_back(b, e);
276 }
277
278 // Recursively build the bounding box tree from the leaves
279 if (!leaf_bboxes.empty())
280 std::tie(_bboxes, _bbox_coordinates)
281 = impl_bb::build_from_leaf(leaf_bboxes);
282
283 spdlog::info("Computed bounding box tree with {} nodes for {} entities",
284 num_bboxes(), entities_span.size());
285 }
286
289 BoundingBoxTree(std::vector<std::pair<std::array<T, 3>, std::int32_t>> points)
290 : _tdim(0)
291 {
292 // Recursively build the bounding box tree from the leaves
293 if (!points.empty())
294 {
295 _bboxes.clear();
296 impl_bb::_build_from_point(std::span(points), _bboxes, _bbox_coordinates);
297 }
298
299 spdlog::info("Computed bounding box tree with {} nodes for {} points.",
300 num_bboxes(), points.size());
301 }
302
305
307 BoundingBoxTree(const BoundingBoxTree& tree) = delete;
308
311
313 BoundingBoxTree& operator=(const BoundingBoxTree& other) = default;
314
316 ~BoundingBoxTree() = default;
317
323 std::array<T, 6> get_bbox(std::size_t node) const
324 {
325 std::array<T, 6> x;
326 std::copy_n(_bbox_coordinates.data() + 6 * node, 6, x.begin());
327 return x;
328 }
329
336 {
337 // Build tree for each rank
338 const int mpi_size = dolfinx::MPI::size(comm);
339
340 // Send root node coordinates to all processes
341 // This is to counteract the fact that a process might have 0 bounding box
342 // causing false positives on process collisions around (0,0,0)
343 constexpr T max_val = std::numeric_limits<T>::max();
344 std::array<T, 6> send_bbox
345 = {max_val, max_val, max_val, max_val, max_val, max_val};
346 if (num_bboxes() > 0)
347 std::copy_n(std::prev(_bbox_coordinates.end(), 6), 6, send_bbox.begin());
348 std::vector<T> recv_bbox(mpi_size * 6);
349 MPI_Allgather(send_bbox.data(), 6, dolfinx::MPI::mpi_t<T>, recv_bbox.data(),
350 6, dolfinx::MPI::mpi_t<T>, comm);
351
352 std::vector<std::pair<std::array<T, 6>, std::int32_t>> _recv_bbox(mpi_size);
353 for (std::size_t i = 0; i < _recv_bbox.size(); ++i)
354 {
355 std::copy_n(std::next(recv_bbox.begin(), 6 * i), 6,
356 _recv_bbox[i].first.begin());
357 _recv_bbox[i].second = i;
358 }
359
360 auto [global_bboxes, global_coords] = impl_bb::build_from_leaf(_recv_bbox);
361 BoundingBoxTree global_tree(std::move(global_bboxes),
362 std::move(global_coords));
363
364 spdlog::info("Computed global bounding box tree with {} boxes.",
365 global_tree.num_bboxes());
366
367 return global_tree;
368 }
369
371 std::int32_t num_bboxes() const { return _bboxes.size() / 2; }
372
374 int tdim() const { return _tdim; }
375
377 std::string str() const
378 {
379 std::stringstream s;
380 tree_print(s, _bboxes.size() / 2 - 1);
381 return s.str();
382 }
383
391 std::array<std::int32_t, 2> bbox(std::size_t node) const
392 {
393 assert(2 * node + 1 < _bboxes.size());
394 return {_bboxes[2 * node], _bboxes[2 * node + 1]};
395 }
396
397private:
398 // Constructor
399 BoundingBoxTree(std::vector<std::int32_t>&& bboxes,
400 std::vector<T>&& bbox_coords)
401 : _tdim(0), _bboxes(bboxes), _bbox_coordinates(bbox_coords)
402 {
403 // Do nothing
404 }
405
406 // Topological dimension of leaf entities
407 int _tdim;
408
409 // Print out recursively, for debugging
410 void tree_print(std::stringstream& s, std::int32_t i) const
411 {
412 s << "[";
413 for (std::size_t j = 0; j < 2; ++j)
414 {
415 for (std::size_t k = 0; k < 3; ++k)
416 s << _bbox_coordinates[6 * i + j * 3 + k] << " ";
417 if (j == 0)
418 s << "]->"
419 << "[";
420 }
421 s << "]\n";
422
423 if (_bboxes[2 * i] == _bboxes[2 * i + 1])
424 s << "leaf containing entity (" << _bboxes[2 * i + 1] << ")";
425 else
426 {
427 s << "{";
428 tree_print(s, _bboxes[2 * i]);
429 s << ", \n";
430 tree_print(s, _bboxes[2 * i + 1]);
431 s << "}\n";
432 }
433 }
434
435 // List of bounding boxes (parent-child-entity relations)
436 std::vector<std::int32_t> _bboxes;
437
438 // List of bounding box coordinates
439 std::vector<T> _bbox_coordinates;
440};
441} // namespace dolfinx::geometry
Definition BoundingBoxTree.h:206
BoundingBoxTree(std::vector< std::pair< std::array< T, 3 >, std::int32_t > > points)
Definition BoundingBoxTree.h:289
BoundingBoxTree & operator=(const BoundingBoxTree &other)=default
Copy assignment.
BoundingBoxTree & operator=(BoundingBoxTree &&other)=default
Move assignment.
BoundingBoxTree create_global_tree(MPI_Comm comm) const
Definition BoundingBoxTree.h:335
int tdim() const
Topological dimension of leaf entities.
Definition BoundingBoxTree.h:374
BoundingBoxTree(BoundingBoxTree &&tree)=default
Move constructor.
std::int32_t num_bboxes() const
Return number of bounding boxes.
Definition BoundingBoxTree.h:371
std::array< T, 6 > get_bbox(std::size_t node) const
Return bounding box coordinates for a given node in the tree,.
Definition BoundingBoxTree.h:323
std::array< std::int32_t, 2 > bbox(std::size_t node) const
Definition BoundingBoxTree.h:391
BoundingBoxTree(const BoundingBoxTree &tree)=delete
Copy constructor.
BoundingBoxTree(const mesh::Mesh< T > &mesh, int tdim, std::optional< std::span< const std::int32_t > > entities=std::nullopt, double padding=0)
Definition BoundingBoxTree.h:234
~BoundingBoxTree()=default
Destructor.
std::string str() const
Print out for debugging.
Definition BoundingBoxTree.h:377
A Mesh consists of a set of connected and numbered mesh topological entities, and geometry data.
Definition Mesh.h:23
Topology stores the topology of a mesh, consisting of mesh entities and connectivity (incidence relat...
Definition Topology.h:46
std::shared_ptr< const common::IndexMap > index_map(int dim) const
Get the IndexMap that described the parallel distribution of the mesh entities.
Definition Topology.cpp:787
bool create_entities(int dim)
Create entities of given topological dimension.
Definition Topology.cpp:848
Functions supporting mesh operations.
MPI_Datatype mpi_t
Retrieves the MPI data type associated to the provided type.
Definition MPI.h:282
int size(MPI_Comm comm)
Definition MPI.cpp:72
Geometry data structures and algorithms.
Definition BoundingBoxTree.h:22
std::vector< std::int32_t > entities_to_geometry(const Mesh< T > &mesh, int dim, std::span< const std::int32_t > entities, bool permute=false)
Compute the geometry degrees of freedom associated with the closure of a given set of cell entities.
Definition utils.h:640