Note: this is documentation for an old release. View the latest documentation at docs.fenicsproject.org/dolfinx/v0.9.0/cpp/doxygen/dd/dd0/geometry_2utils_8h_source.html
DOLFINx 0.7.1
DOLFINx C++ interface
Loading...
Searching...
No Matches
utils.h
1// Copyright (C) 2019-2021 Garth N. Wells and Jørgen S. Dokken
2//
3// This file is part of DOLFINx (https://www.fenicsproject.org)
4//
5// SPDX-License-Identifier: LGPL-3.0-or-later
6
7#pragma once
8
9#include "BoundingBoxTree.h"
10#include "gjk.h"
11#include <array>
12#include <concepts>
13#include <cstdint>
14#include <deque>
15#include <dolfinx/graph/AdjacencyList.h>
16#include <dolfinx/mesh/Mesh.h>
17#include <map>
18#include <span>
19#include <vector>
20
21namespace dolfinx::geometry
22{
23
34template <std::floating_point T>
35std::vector<T> shortest_vector(const mesh::Mesh<T>& mesh, int dim,
36 std::span<const std::int32_t> entities,
37 std::span<const T> points)
38{
39 const int tdim = mesh.topology()->dim();
40 const mesh::Geometry<T>& geometry = mesh.geometry();
41 if (geometry.cmaps().size() > 1)
42 throw std::runtime_error("Mixed topology not supported");
43
44 std::span<const T> geom_dofs = geometry.x();
45 auto x_dofmap = geometry.dofmap();
46 std::vector<T> shortest_vectors(3 * entities.size());
47 if (dim == tdim)
48 {
49 for (std::size_t e = 0; e < entities.size(); e++)
50 {
51 // Check that we have sent in valid entities, i.e. that they exist in the
52 // local dofmap. One gets a cryptical memory segfault if entities is -1
53 assert(entities[e] >= 0);
54 auto dofs
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)
60 {
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];
64 }
65
66 std::array<T, 3> d
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));
69 }
70 }
71 else
72 {
73 mesh.topology_mutable()->create_connectivity(dim, tdim);
74 mesh.topology_mutable()->create_connectivity(tdim, dim);
75 auto e_to_c = mesh.topology()->connectivity(dim, tdim);
76 assert(e_to_c);
77 auto c_to_e = mesh.topology_mutable()->connectivity(tdim, dim);
78 assert(c_to_e);
79 for (std::size_t e = 0; e < entities.size(); e++)
80 {
81 const std::int32_t index = entities[e];
82
83 // Find attached cell
84 assert(e_to_c->num_links(index) > 0);
85 const std::int32_t c = e_to_c->links(index)[0];
86
87 // Find local number of entity wrt cell
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);
92
93 // Tabulate geometry dofs for the entity
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++)
102 {
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];
106 }
107
108 std::array<T, 3> d
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));
111 }
112 }
113
114 return shortest_vectors;
115}
116
123template <std::floating_point T>
124T compute_squared_distance_bbox(std::span<const T, 6> b,
125 std::span<const T, 3> x)
126{
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,
131 std::plus<>{},
132 [](auto x, auto b)
133 {
134 auto dx = x - b;
135 return dx > 0 ? 0 : dx * dx;
136 })
137 + std::transform_reduce(x.begin(), x.end(), b1.begin(), 0.0,
138 std::plus<>{},
139 [](auto x, auto b)
140 {
141 auto dx = x - b;
142 return dx < 0 ? 0 : dx * dx;
143 });
144}
145
161template <std::floating_point T>
162std::vector<T> squared_distance(const mesh::Mesh<T>& mesh, int dim,
163 std::span<const std::int32_t> entities,
164 std::span<const T> points)
165{
166 std::vector<T> v = shortest_vector(mesh, dim, entities, 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];
171 return d;
172}
173
174namespace impl
175{
177constexpr bool is_leaf(std::array<int, 2> bbox)
178{
179 // Leaf nodes are marked by setting child_0 equal to child_1
180 return bbox[0] == bbox[1];
181}
182
187template <std::floating_point T>
188constexpr bool point_in_bbox(const std::array<T, 6>& b, std::span<const T, 3> x)
189{
190 assert(b.size() == 6);
191 constexpr T rtol = 1e-14;
192 bool in = true;
193 for (std::size_t i = 0; i < 3; i++)
194 {
195 T eps = rtol * (b[i + 3] - b[i]);
196 in &= x[i] >= (b[i] - eps);
197 in &= x[i] <= (b[i + 3] + eps);
198 }
199
200 return in;
201}
202
206template <std::floating_point T>
207constexpr bool bbox_in_bbox(std::span<const T, 6> a, std::span<const T, 6> b)
208{
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>();
214
215 bool in = true;
216 for (std::size_t i = 0; i < 3; i++)
217 {
218 T eps = rtol * (b1[i] - b0[i]);
219 in &= a1[i] >= (b0[i] - eps);
220 in &= a0[i] <= (b1[i] + eps);
221 }
222
223 return in;
224}
225
227template <std::floating_point 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,
232 T R2)
233{
234 // Get children of current bounding box node (child_1 denotes entity
235 // index for leaves)
236 const std::array<int, 2> bbox = tree.bbox(node);
237 T r2;
238 if (is_leaf(bbox))
239 {
240 // If point cloud tree the exact distance is easy to compute
241 if (tree.tdim() == 0)
242 {
243 std::array<T, 6> diff = tree.get_bbox(node);
244 for (std::size_t k = 0; k < 3; ++k)
245 diff[k] -= point[k];
246 r2 = diff[0] * diff[0] + diff[1] * diff[1] + diff[2] * diff[2];
247 }
248 else
249 {
250 r2 = compute_squared_distance_bbox<T>(tree.get_bbox(node), point);
251
252 // If bounding box closer than previous closest entity, use gjk to
253 // obtain exact distance to the convex hull of the entity
254 if (r2 <= R2)
255 {
256 r2 = squared_distance<T>(mesh, tree.tdim(),
257 std::span(std::next(bbox.begin(), 1), 1),
258 point)
259 .front();
260 }
261 }
262
263 // If entity is closer than best result so far, return it
264 if (r2 <= R2)
265 {
266 closest_entity = bbox.back();
267 R2 = r2;
268 }
269
270 return {closest_entity, R2};
271 }
272 else
273 {
274 // If bounding box is outside radius, then don't search further
275 r2 = compute_squared_distance_bbox<T>(tree.get_bbox(node), point);
276 if (r2 > R2)
277 return {closest_entity, R2};
278
279 // Check both children
280 // We use R2 (as opposed to r2), as a bounding box can be closer
281 // than the actual entity
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);
286 return p1;
287 }
288}
289
294template <std::floating_point T>
295void _compute_collisions_point(const geometry::BoundingBoxTree<T>& tree,
296 std::span<const T, 3> p,
297 std::vector<std::int32_t>& entities)
298{
299 std::deque<std::int32_t> stack;
300 std::int32_t next = tree.num_bboxes() - 1;
301 while (next != -1)
302 {
303 const std::array<int, 2> bbox = tree.bbox(next);
304 next = -1;
305
306 if (is_leaf(bbox))
307 {
308 // If box is a leaf node then add it to the list of colliding entities
309 entities.push_back(bbox[1]);
310 }
311 else
312 {
313 // Check whether the point collides with child nodes (left and right)
314 bool left = point_in_bbox(tree.get_bbox(bbox[0]), p);
315 bool right = point_in_bbox(tree.get_bbox(bbox[1]), p);
316 if (left && right)
317 {
318 // If the point collides with both child nodes, add the right node to
319 // the stack (for later visiting) and continue the tree traversal with
320 // the left subtree
321 stack.push_back(bbox[1]);
322 next = bbox[0];
323 }
324 else if (left)
325 {
326 // Traverse the current node's left subtree
327 next = bbox[0];
328 }
329 else if (right)
330 {
331 // Traverse the current node's right subtree
332 next = bbox[1];
333 }
334 }
335
336 // If tree traversal reaches a dead end (box is a leaf node or no
337 // collision detected), check the stack for deferred subtrees.
338 if (next == -1 and !stack.empty())
339 {
340 next = stack.back();
341 stack.pop_back();
342 }
343 }
344}
345
346// Compute collisions with tree (recursive)
347template <std::floating_point 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)
352{
353 // If bounding boxes don't collide, then don't search further
354 if (!bbox_in_bbox<T>(A.get_bbox(node_A), B.get_bbox(node_B)))
355 return;
356
357 // Get bounding boxes for current nodes
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);
360
361 // Check whether we've reached a leaf in A or 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)
365 {
366 // If both boxes are leaves (which we know collide), then add them
367 // child_1 denotes entity for leaves
368 entities.push_back(bbox_A[1]);
369 entities.push_back(bbox_B[1]);
370 }
371 else if (is_leaf_A)
372 {
373 // If we reached the leaf in A, then descend B
374 _compute_collisions_tree(A, B, node_A, bbox_B[0], entities);
375 _compute_collisions_tree(A, B, node_A, bbox_B[1], entities);
376 }
377 else if (is_leaf_B)
378 {
379 // If we reached the leaf in B, then descend A
380 _compute_collisions_tree(A, B, bbox_A[0], node_B, entities);
381 _compute_collisions_tree(A, B, bbox_A[1], node_B, entities);
382 }
383 else if (node_A > node_B)
384 {
385 // At this point, we know neither is a leaf so descend the largest
386 // tree first. Note that nodes are added in reverse order with the
387 // top bounding box at the end so the largest tree (the one with the
388 // the most boxes left to traverse) has the largest node number.
389 _compute_collisions_tree(A, B, bbox_A[0], node_B, entities);
390 _compute_collisions_tree(A, B, bbox_A[1], node_B, entities);
391 }
392 else
393 {
394 _compute_collisions_tree(A, B, node_A, bbox_B[0], entities);
395 _compute_collisions_tree(A, B, node_A, bbox_B[1], entities);
396 }
397
398 // Note that cases above can be collected in fewer cases but this way
399 // the logic is easier to follow.
400}
401
402} // namespace impl
403
410template <std::floating_point T>
412 std::span<const std::int32_t> entities)
413{
414 LOG(INFO) << "Building point search tree to accelerate distance queries for "
415 "a given topological dimension and subset of entities.";
416
417 const std::vector<T> midpoints
418 = mesh::compute_midpoints(mesh, tdim, entities);
419 std::vector<std::pair<std::array<T, 3>, std::int32_t>> points(
420 entities.size());
421 for (std::size_t i = 0; i < points.size(); ++i)
422 {
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];
426 }
427
428 // Build tree
429 return BoundingBoxTree(points);
430}
431
437template <std::floating_point T>
438std::vector<std::int32_t> compute_collisions(const BoundingBoxTree<T>& tree0,
439 const BoundingBoxTree<T>& tree1)
440{
441 // Call recursive find function
442 std::vector<std::int32_t> entities;
443 if (tree0.num_bboxes() > 0 and tree1.num_bboxes() > 0)
444 {
445 impl::_compute_collisions_tree(tree0, tree1, tree0.num_bboxes() - 1,
446 tree1.num_bboxes() - 1, entities);
447 }
448
449 return entities;
450}
451
462template <std::floating_point T>
464compute_collisions(const BoundingBoxTree<T>& tree, std::span<const T> points)
465{
466 if (tree.num_bboxes() > 0)
467 {
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)
471 {
472 impl::_compute_collisions_point(
473 tree, std::span<const T, 3>(points.data() + 3 * p, 3), entities);
474 offsets[p + 1] = entities.size();
475 }
476
477 return graph::AdjacencyList(std::move(entities), std::move(offsets));
478 }
479 else
480 {
482 std::vector<std::int32_t>(),
483 std::vector<std::int32_t>(points.size() / 3 + 1, 0));
484 }
485}
486
497template <std::floating_point T>
499 const BoundingBoxTree<T>& tree,
500 const std::array<T, 3>& point)
501{
502 // Compute colliding bounding boxes(cell candidates)
503 std::vector<std::int32_t> cell_candidates;
504 impl::_compute_collisions_point<T>(tree, point, cell_candidates);
505
506 if (mesh.geometry().cmaps().size() > 1)
507 {
508 throw std::runtime_error("Mixed topology not supported");
509 }
510
511 if (cell_candidates.empty())
512 return -1;
513 else
514 {
515 constexpr T eps2 = 1e-20;
516 const mesh::Geometry<T>& geometry = mesh.geometry();
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)
522 {
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)
527 {
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));
531 }
532 std::array<T, 3> shortest_vector
533 = compute_distance_gjk<T>(point, coordinate_dofs);
534 T norm = 0;
535 std::for_each(shortest_vector.cbegin(), shortest_vector.cend(),
536 [&norm](auto e) { norm += std::pow(e, 2); });
537
538 if (norm < eps2)
539 return cell;
540 }
541
542 return -1;
543 }
544}
545
558template <std::floating_point T>
559std::vector<std::int32_t>
561 const BoundingBoxTree<T>& midpoint_tree,
562 const mesh::Mesh<T>& mesh, std::span<const T> points)
563{
564 if (tree.num_bboxes() == 0)
565 return std::vector<std::int32_t>(points.size() / 3, -1);
566
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)
570 {
571 // Use midpoint tree to find initial closest entity to the point.
572 // Start by using a leaf node as the initial guess for the input
573 // entity
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];
580
581 // Use a recursive search through the bounding box tree
582 // to find determine the entity with the closest midpoint.
583 // As the midpoint tree only consist of points, the distance
584 // queries are lightweight.
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);
588
589 // Use a recursives search through the bounding box tree to
590 // determine which entity is actually closest.
591 // Uses the entity with the closest midpoint as initial guess, and
592 // the distance from the midpoint to the point of interest as the
593 // initial search radius.
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);
597
598 entities.push_back(index);
599 }
600
601 return entities;
602}
603
615template <std::floating_point T>
617 const mesh::Mesh<T>& mesh,
618 const graph::AdjacencyList<std::int32_t>& candidate_cells,
619 std::span<const T> points)
620{
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++)
627 {
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];
633
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]);
638
639 offsets.push_back(colliding_cells.size());
640 }
641
642 return graph::AdjacencyList(std::move(colliding_cells), std::move(offsets));
643}
644
663template <std::floating_point T>
664std::tuple<std::vector<std::int32_t>, std::vector<std::int32_t>, std::vector<T>,
665 std::vector<std::int32_t>>
666determine_point_ownership(const mesh::Mesh<T>& mesh, std::span<const T> points)
667{
668 MPI_Comm comm = mesh.comm();
669
670 // Create a global bounding-box tree to find candidate processes with
671 // cells that could collide with the points
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();
676 // NOTE: Should we send the cells in as input?
677 std::vector<std::int32_t> cells(num_cells, 0);
678 std::iota(cells.begin(), cells.end(), 0);
679 BoundingBoxTree bb(mesh, tdim, cells, padding);
680 BoundingBoxTree midpoint_tree = create_midpoint_tree(mesh, tdim, cells);
681 BoundingBoxTree global_bbtree = bb.create_global_tree(comm);
682
683 // Compute collisions:
684 // For each point in `x` get the processes it should be sent to
685 graph::AdjacencyList collisions = compute_collisions(global_bbtree, points);
686
687 // Get unique list of outgoing ranks
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()),
691 out_ranks.end());
692
693 // Compute incoming edges (source processes)
694 std::vector in_ranks = dolfinx::MPI::compute_graph_edges_nbx(comm, out_ranks);
695 std::sort(in_ranks.begin(), in_ranks.end());
696
697 // Create neighborhood communicator in forward direction
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);
702
703 // Compute map from global mpi rank to neighbor rank, "collisions" uses
704 // global rank
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;
708
709 // Count the number of points to send per neighbor process
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;
714
715 // Compute receive sizes
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);
722
723 // Compute sending offsets
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));
727
728 // Pack data to send and store unpack map
729 std::vector<T> send_data(send_offsets.back());
730 std::vector<std::int32_t> counter(send_sizes.size(), 0);
731 // unpack map: [index in adj list][pos in x]
732 std::vector<std::int32_t> unpack_map(send_offsets.back() / 3);
733 for (std::size_t i = 0; i < points.size(); i += 3)
734 {
735 for (auto p : collisions.links(i / 3))
736 {
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),
741 it);
742 unpack_map[pos / 3] = i / 3;
743 counter[neighbor] += 3;
744 }
745 }
746
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));
751
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);
757
758 // Each process checks which local cell is closest and computes the squared
759 // distance to the cell
760 const int rank = dolfinx::MPI::rank(comm);
761 const std::vector<std::int32_t> closest_cells = compute_closest_entity(
762 bb, midpoint_tree, mesh,
763 std::span<const T>(received_points.data(), received_points.size()));
764 const std::vector<T> squared_distances = squared_distance(
765 mesh, tdim, closest_cells,
766 std::span<const T>(received_points.data(), received_points.size()));
767
768 // Create neighborhood communicator in the reverse direction: send
769 // back col to requesting processes
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);
774
775 // Reuse sizes and offsets from first communication set
776 // but divide by three
777 {
778 auto rescale = [](auto& x)
779 {
780 std::transform(x.cbegin(), x.cend(), x.begin(),
781 [](auto e) { return (e / 3); });
782 };
783 rescale(recv_sizes);
784 rescale(recv_offsets);
785 rescale(send_sizes);
786 rescale(send_offsets);
787
788 // The communication is reversed, so swap recv to send offsets
789 std::swap(recv_sizes, send_sizes);
790 std::swap(recv_offsets, send_offsets);
791 }
792
793 // Get distances from closest entity of points that were on the other process
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);
799
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++)
803 {
804 for (std::int32_t j = recv_offsets[i]; j < recv_offsets[i + 1]; j++)
805 {
806 const std::int32_t pos = unpack_map[j];
807 // If point has not been found yet distance is negative
808 // If new received distance smaller than current distance choose owner
809 if (auto d = closest_distance[pos]; d < 0 or d > recv_distances[j])
810 {
811 point_owners[pos] = out_ranks[i];
812 closest_distance[pos] = recv_distances[j];
813 }
814 }
815 }
816
817 // Communication is reversed again to send dest ranks to all processes
818 std::swap(send_sizes, recv_sizes);
819 std::swap(send_offsets, recv_offsets);
820
821 // Pack ownership data
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)
825 {
826 for (auto p : collisions.links(i))
827 {
828 int neighbor = rank_to_neighbor[p];
829 send_owners[send_offsets[neighbor] + counter[neighbor]++]
830 = point_owners[i];
831 }
832 }
833
834 // Send ownership info
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);
841
842 // Unpack dest ranks if point owner is this rank
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++)
848 {
849 for (std::int32_t j = recv_offsets[i]; j < recv_offsets[i + 1]; j++)
850 {
851 if (rank == dest_ranks[j])
852 {
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]);
858 }
859 }
860 }
861
862 MPI_Comm_free(&forward_comm);
863 MPI_Comm_free(&reverse_comm);
864
865 return std::make_tuple(point_owners, owned_recv_ranks, owned_recv_points,
866 owned_recv_cells);
867}
868
869} // namespace dolfinx::geometry
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