9#include "BoundingBoxTree.h"
15#include <dolfinx/graph/AdjacencyList.h>
16#include <dolfinx/mesh/Mesh.h>
34template <std::
floating_po
int T>
36 std::span<const std::int32_t> entities,
37 std::span<const T> points)
39 const int tdim = mesh.
topology()->dim();
41 if (geometry.
cmaps().size() > 1)
42 throw std::runtime_error(
"Mixed topology not supported");
44 std::span<const T> geom_dofs = geometry.
x();
45 auto x_dofmap = geometry.
dofmap();
46 std::vector<T> shortest_vectors(3 * entities.size());
49 for (std::size_t e = 0; e < entities.size(); e++)
53 assert(entities[e] >= 0);
55 = MDSPAN_IMPL_STANDARD_NAMESPACE::MDSPAN_IMPL_PROPOSED_NAMESPACE::
56 submdspan(x_dofmap, entities[e],
57 MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent);
58 std::vector<T> nodes(3 * dofs.size());
59 for (std::size_t i = 0; i < dofs.size(); ++i)
61 const int pos = 3 * dofs[i];
62 for (std::size_t j = 0; j < 3; ++j)
63 nodes[3 * i + j] = geom_dofs[pos + j];
67 = compute_distance_gjk<T>(points.subspan(3 * e, 3), nodes);
68 std::copy(d.begin(), d.end(), std::next(shortest_vectors.begin(), 3 * e));
75 auto e_to_c = mesh.
topology()->connectivity(dim, tdim);
79 for (std::size_t e = 0; e < entities.size(); e++)
81 const std::int32_t index = entities[e];
84 assert(e_to_c->num_links(index) > 0);
85 const std::int32_t c = e_to_c->links(index)[0];
88 auto cell_entities = c_to_e->links(c);
89 auto it0 = std::find(cell_entities.begin(), cell_entities.end(), index);
90 assert(it0 != cell_entities.end());
91 const int local_cell_entity = std::distance(cell_entities.begin(), it0);
94 auto dofs = MDSPAN_IMPL_STANDARD_NAMESPACE::
95 MDSPAN_IMPL_PROPOSED_NAMESPACE::submdspan(
96 x_dofmap, c, MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent);
97 const std::vector<int> entity_dofs
98 = geometry.
cmaps()[0].create_dof_layout().entity_closure_dofs(
99 dim, local_cell_entity);
100 std::vector<T> nodes(3 * entity_dofs.size());
101 for (std::size_t i = 0; i < entity_dofs.size(); i++)
103 const int pos = 3 * dofs[entity_dofs[i]];
104 for (std::size_t j = 0; j < 3; ++j)
105 nodes[3 * i + j] = geom_dofs[pos + j];
109 = compute_distance_gjk<T>(points.subspan(3 * e, 3), nodes);
110 std::copy(d.begin(), d.end(), std::next(shortest_vectors.begin(), 3 * e));
114 return shortest_vectors;
123template <std::
floating_po
int T>
125 std::span<const T, 3> x)
127 assert(b.size() == 6);
128 auto b0 = b.template subspan<0, 3>();
129 auto b1 = b.template subspan<3, 3>();
130 return std::transform_reduce(x.begin(), x.end(), b0.begin(), 0.0,
135 return dx > 0 ? 0 : dx * dx;
137 + std::transform_reduce(x.begin(), x.end(), b1.begin(), 0.0,
142 return dx < 0 ? 0 : dx * dx;
161template <std::
floating_po
int T>
163 std::span<const std::int32_t> entities,
164 std::span<const T> points)
167 std::vector<T> d(v.size() / 3, 0);
168 for (std::size_t i = 0; i < d.size(); ++i)
169 for (std::size_t j = 0; j < 3; ++j)
170 d[i] += v[3 * i + j] * v[3 * i + j];
177constexpr bool is_leaf(std::array<int, 2> bbox)
180 return bbox[0] == bbox[1];
187template <std::
floating_po
int T>
188constexpr bool point_in_bbox(
const std::array<T, 6>& b, std::span<const T, 3> x)
190 assert(b.size() == 6);
191 constexpr T rtol = 1e-14;
193 for (std::size_t i = 0; i < 3; i++)
195 T eps = rtol * (b[i + 3] - b[i]);
196 in &= x[i] >= (b[i] - eps);
197 in &= x[i] <= (b[i + 3] + eps);
206template <std::
floating_po
int T>
207constexpr bool bbox_in_bbox(std::span<const T, 6> a, std::span<const T, 6> b)
209 constexpr T rtol = 1e-14;
210 auto a0 = a.template subspan<0, 3>();
211 auto a1 = a.template subspan<3, 3>();
212 auto b0 = b.template subspan<0, 3>();
213 auto b1 = b.template subspan<3, 3>();
216 for (std::size_t i = 0; i < 3; i++)
218 T eps = rtol * (b1[i] - b0[i]);
219 in &= a1[i] >= (b0[i] - eps);
220 in &= a0[i] <= (b1[i] + eps);
227template <std::
floating_po
int T>
228std::pair<std::int32_t, T>
229_compute_closest_entity(
const geometry::BoundingBoxTree<T>& tree,
230 std::span<const T, 3> point, std::int32_t node,
231 const mesh::Mesh<T>& mesh, std::int32_t closest_entity,
236 const std::array<int, 2> bbox = tree.bbox(node);
241 if (tree.tdim() == 0)
243 std::array<T, 6> diff = tree.get_bbox(node);
244 for (std::size_t k = 0; k < 3; ++k)
246 r2 = diff[0] * diff[0] + diff[1] * diff[1] + diff[2] * diff[2];
250 r2 = compute_squared_distance_bbox<T>(tree.get_bbox(node), point);
256 r2 = squared_distance<T>(mesh, tree.tdim(),
257 std::span(std::next(bbox.begin(), 1), 1),
266 closest_entity = bbox.back();
270 return {closest_entity, R2};
275 r2 = compute_squared_distance_bbox<T>(tree.get_bbox(node), point);
277 return {closest_entity, R2};
282 std::pair<int, T> p0 = _compute_closest_entity(tree, point, bbox.front(),
283 mesh, closest_entity, R2);
284 std::pair<int, T> p1 = _compute_closest_entity(tree, point, bbox.back(),
285 mesh, p0.first, p0.second);
294template <std::
floating_po
int T>
295void _compute_collisions_point(
const geometry::BoundingBoxTree<T>& tree,
296 std::span<const T, 3> p,
297 std::vector<std::int32_t>& entities)
299 std::deque<std::int32_t> stack;
300 std::int32_t next = tree.num_bboxes() - 1;
303 const std::array<int, 2> bbox = tree.bbox(next);
309 entities.push_back(bbox[1]);
314 bool left = point_in_bbox(tree.get_bbox(bbox[0]), p);
315 bool right = point_in_bbox(tree.get_bbox(bbox[1]), p);
321 stack.push_back(bbox[1]);
338 if (next == -1 and !stack.empty())
347template <std::
floating_po
int T>
348void _compute_collisions_tree(
const geometry::BoundingBoxTree<T>& A,
349 const geometry::BoundingBoxTree<T>& B,
350 std::int32_t node_A, std::int32_t node_B,
351 std::vector<std::int32_t>& entities)
354 if (!bbox_in_bbox<T>(A.get_bbox(node_A), B.get_bbox(node_B)))
358 const std::array<std::int32_t, 2> bbox_A = A.bbox(node_A);
359 const std::array<std::int32_t, 2> bbox_B = B.bbox(node_B);
362 const bool is_leaf_A = is_leaf(bbox_A);
363 const bool is_leaf_B = is_leaf(bbox_B);
364 if (is_leaf_A and is_leaf_B)
368 entities.push_back(bbox_A[1]);
369 entities.push_back(bbox_B[1]);
374 _compute_collisions_tree(A, B, node_A, bbox_B[0], entities);
375 _compute_collisions_tree(A, B, node_A, bbox_B[1], entities);
380 _compute_collisions_tree(A, B, bbox_A[0], node_B, entities);
381 _compute_collisions_tree(A, B, bbox_A[1], node_B, entities);
383 else if (node_A > node_B)
389 _compute_collisions_tree(A, B, bbox_A[0], node_B, entities);
390 _compute_collisions_tree(A, B, bbox_A[1], node_B, entities);
394 _compute_collisions_tree(A, B, node_A, bbox_B[0], entities);
395 _compute_collisions_tree(A, B, node_A, bbox_B[1], entities);
410template <std::
floating_po
int T>
412 std::span<const std::int32_t> entities)
414 LOG(INFO) <<
"Building point search tree to accelerate distance queries for "
415 "a given topological dimension and subset of entities.";
417 const std::vector<T> midpoints
419 std::vector<std::pair<std::array<T, 3>, std::int32_t>> points(
421 for (std::size_t i = 0; i < points.size(); ++i)
423 for (std::size_t j = 0; j < 3; ++j)
424 points[i].first[j] = midpoints[3 * i + j];
425 points[i].second = entities[i];
437template <std::
floating_po
int T>
442 std::vector<std::int32_t> entities;
445 impl::_compute_collisions_tree(tree0, tree1, tree0.
num_bboxes() - 1,
462template <std::
floating_po
int T>
468 std::vector<std::int32_t> entities, offsets(points.size() / 3 + 1, 0);
469 entities.reserve(points.size() / 3);
470 for (std::size_t p = 0; p < points.size() / 3; ++p)
472 impl::_compute_collisions_point(
473 tree, std::span<const T, 3>(points.data() + 3 * p, 3), entities);
474 offsets[p + 1] = entities.size();
482 std::vector<std::int32_t>(),
483 std::vector<std::int32_t>(points.size() / 3 + 1, 0));
497template <std::
floating_po
int T>
500 const std::array<T, 3>& point)
503 std::vector<std::int32_t> cell_candidates;
504 impl::_compute_collisions_point<T>(tree, point, cell_candidates);
506 if (mesh.
geometry().cmaps().size() > 1)
508 throw std::runtime_error(
"Mixed topology not supported");
511 if (cell_candidates.empty())
515 constexpr T eps2 = 1e-20;
517 std::span<const T> geom_dofs = geometry.
x();
518 auto x_dofmap = geometry.
dofmap();
519 const std::size_t num_nodes = x_dofmap.extent(1);
520 std::vector<T> coordinate_dofs(num_nodes * 3);
521 for (
auto cell : cell_candidates)
523 auto dofs = MDSPAN_IMPL_STANDARD_NAMESPACE::
524 MDSPAN_IMPL_PROPOSED_NAMESPACE::submdspan(
525 x_dofmap, cell, MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent);
526 for (std::size_t i = 0; i < num_nodes; ++i)
528 std::copy(std::next(geom_dofs.begin(), 3 * dofs[i]),
529 std::next(geom_dofs.begin(), 3 * dofs[i] + 3),
530 std::next(coordinate_dofs.begin(), 3 * i));
533 = compute_distance_gjk<T>(point, coordinate_dofs);
536 [&norm](
auto e) { norm += std::pow(e, 2); });
558template <std::
floating_po
int T>
559std::vector<std::int32_t>
565 return std::vector<std::int32_t>(points.size() / 3, -1);
567 std::vector<std::int32_t> entities;
568 entities.reserve(points.size() / 3);
569 for (std::size_t i = 0; i < points.size() / 3; ++i)
574 std::array<int, 2> leaf0 = midpoint_tree.
bbox(0);
575 assert(impl::is_leaf(leaf0));
576 std::array<T, 6> diff = midpoint_tree.
get_bbox(0);
577 for (std::size_t k = 0; k < 3; ++k)
578 diff[k] -= points[3 * i + k];
579 T R2 = diff[0] * diff[0] + diff[1] * diff[1] + diff[2] * diff[2];
585 const auto [m_index, m_distance2] = impl::_compute_closest_entity(
586 midpoint_tree, std::span<const T, 3>(points.data() + 3 * i, 3),
587 midpoint_tree.
num_bboxes() - 1, mesh, leaf0[0], R2);
594 const auto [index, distance2] = impl::_compute_closest_entity(
595 tree, std::span<const T, 3>(points.data() + 3 * i, 3),
596 tree.
num_bboxes() - 1, mesh, m_index, m_distance2);
598 entities.push_back(index);
615template <std::
floating_po
int T>
619 std::span<const T> points)
621 std::vector<std::int32_t> offsets = {0};
622 offsets.reserve(candidate_cells.
num_nodes() + 1);
623 std::vector<std::int32_t> colliding_cells;
624 constexpr T eps2 = 1e-12;
625 const int tdim = mesh.
topology()->dim();
626 for (std::int32_t i = 0; i < candidate_cells.
num_nodes(); i++)
628 auto cells = candidate_cells.
links(i);
629 std::vector<T> _point(3 * cells.size());
630 for (std::size_t j = 0; j < cells.size(); ++j)
631 for (std::size_t k = 0; k < 3; ++k)
632 _point[3 * j + k] = points[3 * i + k];
634 std::vector distances_sq = squared_distance<T>(mesh, tdim, cells, _point);
635 for (std::size_t j = 0; j < cells.size(); j++)
636 if (distances_sq[j] < eps2)
637 colliding_cells.push_back(cells[j]);
639 offsets.push_back(colliding_cells.size());
663template <std::
floating_po
int T>
664std::tuple<std::vector<std::int32_t>, std::vector<std::int32_t>, std::vector<T>,
665 std::vector<std::int32_t>>
668 MPI_Comm comm = mesh.
comm();
672 constexpr T padding = 1.0e-4;
673 const int tdim = mesh.
topology()->dim();
674 auto cell_map = mesh.
topology()->index_map(tdim);
675 const std::int32_t num_cells = cell_map->size_local();
677 std::vector<std::int32_t> cells(num_cells, 0);
678 std::iota(cells.begin(), cells.end(), 0);
688 std::vector<std::int32_t> out_ranks = collisions.
array();
689 std::sort(out_ranks.begin(), out_ranks.end());
690 out_ranks.erase(std::unique(out_ranks.begin(), out_ranks.end()),
695 std::sort(in_ranks.begin(), in_ranks.end());
698 MPI_Comm forward_comm;
699 MPI_Dist_graph_create_adjacent(
700 comm, in_ranks.size(), in_ranks.data(), MPI_UNWEIGHTED, out_ranks.size(),
701 out_ranks.data(), MPI_UNWEIGHTED, MPI_INFO_NULL,
false, &forward_comm);
705 std::map<std::int32_t, std::int32_t> rank_to_neighbor;
706 for (std::size_t i = 0; i < out_ranks.size(); i++)
707 rank_to_neighbor[out_ranks[i]] = i;
710 std::vector<std::int32_t> send_sizes(out_ranks.size());
711 for (std::size_t i = 0; i < points.size() / 3; ++i)
712 for (
auto p : collisions.
links(i))
713 send_sizes[rank_to_neighbor[p]] += 3;
716 std::vector<std::int32_t> recv_sizes(in_ranks.size());
717 send_sizes.reserve(1);
718 recv_sizes.reserve(1);
719 MPI_Request sizes_request;
720 MPI_Ineighbor_alltoall(send_sizes.data(), 1, MPI_INT, recv_sizes.data(), 1,
721 MPI_INT, forward_comm, &sizes_request);
724 std::vector<std::int32_t> send_offsets(send_sizes.size() + 1, 0);
725 std::partial_sum(send_sizes.begin(), send_sizes.end(),
726 std::next(send_offsets.begin(), 1));
729 std::vector<T> send_data(send_offsets.back());
730 std::vector<std::int32_t> counter(send_sizes.size(), 0);
732 std::vector<std::int32_t> unpack_map(send_offsets.back() / 3);
733 for (std::size_t i = 0; i < points.size(); i += 3)
735 for (
auto p : collisions.
links(i / 3))
737 int neighbor = rank_to_neighbor[p];
738 int pos = send_offsets[neighbor] + counter[neighbor];
739 auto it = std::next(send_data.begin(), pos);
740 std::copy(std::next(points.begin(), i), std::next(points.begin(), i + 3),
742 unpack_map[pos / 3] = i / 3;
743 counter[neighbor] += 3;
747 MPI_Wait(&sizes_request, MPI_STATUS_IGNORE);
748 std::vector<std::int32_t> recv_offsets(in_ranks.size() + 1, 0);
749 std::partial_sum(recv_sizes.begin(), recv_sizes.end(),
750 std::next(recv_offsets.begin(), 1));
752 std::vector<T> received_points((std::size_t)recv_offsets.back());
753 MPI_Neighbor_alltoallv(
754 send_data.data(), send_sizes.data(), send_offsets.data(),
755 dolfinx::MPI::mpi_type<T>(), received_points.data(), recv_sizes.data(),
756 recv_offsets.data(), dolfinx::MPI::mpi_type<T>(), forward_comm);
762 bb, midpoint_tree, mesh,
763 std::span<const T>(received_points.data(), received_points.size()));
765 mesh, tdim, closest_cells,
766 std::span<const T>(received_points.data(), received_points.size()));
770 MPI_Comm reverse_comm;
771 MPI_Dist_graph_create_adjacent(
772 comm, out_ranks.size(), out_ranks.data(), MPI_UNWEIGHTED, in_ranks.size(),
773 in_ranks.data(), MPI_UNWEIGHTED, MPI_INFO_NULL,
false, &reverse_comm);
778 auto rescale = [](
auto& x)
780 std::transform(x.cbegin(), x.cend(), x.begin(),
781 [](
auto e) { return (e / 3); });
784 rescale(recv_offsets);
786 rescale(send_offsets);
789 std::swap(recv_sizes, send_sizes);
790 std::swap(recv_offsets, send_offsets);
794 std::vector<T> recv_distances(recv_offsets.back());
795 MPI_Neighbor_alltoallv(
796 squared_distances.data(), send_sizes.data(), send_offsets.data(),
797 dolfinx::MPI::mpi_type<T>(), recv_distances.data(), recv_sizes.data(),
798 recv_offsets.data(), dolfinx::MPI::mpi_type<T>(), reverse_comm);
800 std::vector<std::int32_t> point_owners(points.size() / 3, -1);
801 std::vector<T> closest_distance(points.size() / 3, -1);
802 for (std::size_t i = 0; i < out_ranks.size(); i++)
804 for (std::int32_t j = recv_offsets[i]; j < recv_offsets[i + 1]; j++)
806 const std::int32_t pos = unpack_map[j];
809 if (
auto d = closest_distance[pos]; d < 0 or d > recv_distances[j])
811 point_owners[pos] = out_ranks[i];
812 closest_distance[pos] = recv_distances[j];
818 std::swap(send_sizes, recv_sizes);
819 std::swap(send_offsets, recv_offsets);
822 std::vector<std::int32_t> send_owners(send_offsets.back());
823 std::fill(counter.begin(), counter.end(), 0);
824 for (std::size_t i = 0; i < points.size() / 3; ++i)
826 for (
auto p : collisions.
links(i))
828 int neighbor = rank_to_neighbor[p];
829 send_owners[send_offsets[neighbor] + counter[neighbor]++]
835 std::vector<std::int32_t> dest_ranks(recv_offsets.back());
836 MPI_Neighbor_alltoallv(
837 send_owners.data(), send_sizes.data(), send_offsets.data(),
838 dolfinx::MPI::mpi_type<std::int32_t>(), dest_ranks.data(),
839 recv_sizes.data(), recv_offsets.data(),
840 dolfinx::MPI::mpi_type<std::int32_t>(), forward_comm);
843 std::vector<std::int32_t> owned_recv_ranks;
844 owned_recv_ranks.reserve(recv_offsets.back());
845 std::vector<T> owned_recv_points;
846 std::vector<std::int32_t> owned_recv_cells;
847 for (std::size_t i = 0; i < in_ranks.size(); i++)
849 for (std::int32_t j = recv_offsets[i]; j < recv_offsets[i + 1]; j++)
851 if (rank == dest_ranks[j])
853 owned_recv_ranks.push_back(in_ranks[i]);
854 owned_recv_points.insert(
855 owned_recv_points.end(), std::next(received_points.cbegin(), 3 * j),
856 std::next(received_points.cbegin(), 3 * (j + 1)));
857 owned_recv_cells.push_back(closest_cells[j]);
862 MPI_Comm_free(&forward_comm);
863 MPI_Comm_free(&reverse_comm);
865 return std::make_tuple(point_owners, owned_recv_ranks, owned_recv_points,
Axis-Aligned bounding box binary tree. It is used to find entities in a collection (often a mesh::Mes...
Definition BoundingBoxTree.h:205
BoundingBoxTree create_global_tree(MPI_Comm comm) const
Compute a global bounding tree (collective on comm) This can be used to find which process a point mi...
Definition BoundingBoxTree.h:325
std::int32_t num_bboxes() const
Return number of bounding boxes.
Definition BoundingBoxTree.h:361
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:313
std::array< std::int32_t, 2 > bbox(std::size_t node) const
Get bounding box child nodes.
Definition BoundingBoxTree.h:381
This class provides a static adjacency list data structure. It is commonly used to store directed gra...
Definition AdjacencyList.h:28
std::span< T > links(std::size_t node)
Get the links (edges) for given node.
Definition AdjacencyList.h:112
const std::vector< T > & array() const
Return contiguous array of links for all nodes (const version)
Definition AdjacencyList.h:129
std::int32_t num_nodes() const
Get the number of nodes.
Definition AdjacencyList.h:97
Geometry stores the geometry imposed on a mesh.
Definition Geometry.h:33
MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< const std::int32_t, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents< std::size_t, 2 > > dofmap() const
DOF map.
Definition Geometry.h:94
std::span< const value_type > x() const
Access geometry degrees-of-freedom data (const version).
Definition Geometry.h:113
const std::vector< fem::CoordinateElement< value_type > > & cmaps() const
The elements that describes the geometry maps.
Definition Geometry.h:125
A Mesh consists of a set of connected and numbered mesh topological entities, and geometry data.
Definition Mesh.h:23
std::shared_ptr< Topology > topology_mutable() const
Get mesh topology if one really needs the mutable version.
Definition Mesh.h:72
std::shared_ptr< Topology > topology()
Get mesh topology.
Definition Mesh.h:64
Geometry< T > & geometry()
Get mesh geometry.
Definition Mesh.h:76
MPI_Comm comm() const
Mesh MPI communicator.
Definition Mesh.h:84
std::vector< int > compute_graph_edges_nbx(MPI_Comm comm, std::span< const int > edges)
Determine incoming graph edges using the NBX consensus algorithm.
Definition MPI.cpp:164
int rank(MPI_Comm comm)
Return process rank for the communicator.
Definition MPI.cpp:64
Geometry data structures and algorithms.
Definition BoundingBoxTree.h:21
std::vector< T > squared_distance(const mesh::Mesh< T > &mesh, int dim, std::span< const std::int32_t > entities, std::span< const T > points)
Compute the squared distance between a point and a mesh entity.
Definition utils.h:162
std::vector< std::int32_t > compute_collisions(const BoundingBoxTree< T > &tree0, const BoundingBoxTree< T > &tree1)
Compute all collisions between two bounding box trees.
Definition utils.h:438
T compute_squared_distance_bbox(std::span< const T, 6 > b, std::span< const T, 3 > x)
Compute squared distance between point and bounding box.
Definition utils.h:124
graph::AdjacencyList< std::int32_t > compute_colliding_cells(const mesh::Mesh< T > &mesh, const graph::AdjacencyList< std::int32_t > &candidate_cells, std::span< const T > points)
Compute which cells collide with a point.
Definition utils.h:616
std::tuple< std::vector< std::int32_t >, std::vector< std::int32_t >, std::vector< T >, std::vector< std::int32_t > > determine_point_ownership(const mesh::Mesh< T > &mesh, std::span< const T > points)
Given a set of points, determine which process is colliding, using the GJK algorithm on cells to dete...
Definition utils.h:666
std::vector< T > shortest_vector(const mesh::Mesh< T > &mesh, int dim, std::span< const std::int32_t > entities, std::span< const T > points)
Compute the shortest vector from a mesh entity to a point.
Definition utils.h:35
BoundingBoxTree< T > create_midpoint_tree(const mesh::Mesh< T > &mesh, int tdim, std::span< const std::int32_t > entities)
Create a bounding box tree for the midpoints of a subset of entities.
Definition utils.h:411
std::int32_t compute_first_colliding_cell(const mesh::Mesh< T > &mesh, const BoundingBoxTree< T > &tree, const std::array< T, 3 > &point)
Compute the cell that collides with a point.
Definition utils.h:498
std::vector< std::int32_t > compute_closest_entity(const BoundingBoxTree< T > &tree, const BoundingBoxTree< T > &midpoint_tree, const mesh::Mesh< T > &mesh, std::span< const T > points)
Compute closest mesh entity to a point.
Definition utils.h:560
std::vector< T > compute_midpoints(const Mesh< T > &mesh, int dim, std::span< const std::int32_t > entities)
Compute the midpoints for mesh entities of a given dimension.
Definition utils.h:397