DOLFINx 0.12.0.0
DOLFINx C++
Loading...
Searching...
No Matches
utils.h
Go to the documentation of this file.
1// Copyright (C) 2019-2026 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 "EntityMap.h"
10#include "Mesh.h"
11#include "MeshTags.h"
12#include "Topology.h"
13#include "graphbuild.h"
14#include <algorithm>
15#include <basix/mdspan.hpp>
16#include <concepts>
17#include <cstdint>
18#include <dolfinx/graph/AdjacencyList.h>
19#include <dolfinx/graph/ordering.h>
20#include <dolfinx/graph/partition.h>
21#include <format>
22#include <functional>
23#include <mpi.h>
24#include <numeric>
25#include <optional>
26#include <ranges>
27#include <span>
28#include <vector>
29
32
33namespace dolfinx::fem
34{
36}
37
38namespace dolfinx::mesh
39{
40enum class CellType : std::int8_t;
41
43enum class GhostMode : std::uint8_t
44{
45 none,
46 shared_facet
47};
48
49namespace impl
50{
56template <typename T>
57void reorder_list(std::span<T> list, std::span<const std::int32_t> nodemap)
58{
59 if (nodemap.empty())
60 return;
61
62 assert(list.size() % nodemap.size() == 0);
63 std::size_t degree = list.size() / nodemap.size();
64 const std::vector<T> orig(list.begin(), list.end());
65 for (std::size_t n = 0; n < nodemap.size(); ++n)
66 {
67 std::span links_old(orig.data() + n * degree, degree);
68 auto links_new = list.subspan(nodemap[n] * degree, degree);
69 std::ranges::copy(links_old, links_new.begin());
70 }
71}
72
86template <std::floating_point T>
87std::tuple<std::vector<std::int32_t>, std::vector<T>, std::vector<std::int32_t>>
89 std::span<const std::int32_t> facets)
90{
91 auto topology = mesh.topology();
92 assert(topology);
93 const int tdim = topology->dim();
94 if (dim == tdim)
95 {
96 throw std::runtime_error(
97 "Cannot use mesh::locate_entities_boundary (boundary) for cells.");
98 }
99
100 // Build set of vertices on boundary and set of boundary entities
101 mesh.topology_mutable()->create_connectivity(tdim - 1, 0);
102 mesh.topology_mutable()->create_connectivity(tdim - 1, dim);
103 std::vector<std::int32_t> vertices, entities;
104 {
105 auto f_to_v = topology->connectivity(tdim - 1, 0);
106 assert(f_to_v);
107 auto f_to_e = topology->connectivity(tdim - 1, dim);
108 assert(f_to_e);
109 for (auto f : facets)
110 {
111 auto v = f_to_v->links(f);
112 vertices.insert(vertices.end(), v.begin(), v.end());
113 auto e = f_to_e->links(f);
114 entities.insert(entities.end(), e.begin(), e.end());
115 }
116
117 // Build vector of boundary vertices
118 {
119 std::ranges::sort(vertices);
120 auto [unique_end, range_end] = std::ranges::unique(vertices);
121 vertices.erase(unique_end, range_end);
122 }
123
124 {
125 std::ranges::sort(entities);
126 auto [unique_end, range_end] = std::ranges::unique(entities);
127 entities.erase(unique_end, range_end);
128 }
129 }
130
131 // Get geometry data
132 auto x_dofmap = mesh.geometry().dofmaps().front();
133 std::span<const T> x_nodes = mesh.geometry().x();
134
135 // Get all vertex 'node' indices
136 mesh.topology_mutable()->create_connectivity(0, tdim);
137 mesh.topology_mutable()->create_connectivity(tdim, 0);
138 auto v_to_c = topology->connectivity(0, tdim);
139 assert(v_to_c);
140 auto c_to_v = topology->connectivity(tdim, 0);
141 assert(c_to_v);
142 std::vector<T> x_vertices(3 * vertices.size(), -1.0);
143 std::vector<std::int32_t> vertex_to_pos(v_to_c->num_nodes(), -1);
144 for (std::size_t i = 0; i < vertices.size(); ++i)
145 {
146 const std::int32_t v = vertices[i];
147
148 // Get first cell and find position
149 const std::int32_t c = v_to_c->links(v).front();
150 auto cell_vertices = c_to_v->links(c);
151 auto it = std::ranges::find(cell_vertices, v);
152 assert(it != cell_vertices.end());
153 const std::size_t local_pos
154 = std::ranges::distance(cell_vertices.begin(), it);
155
156 auto dofs = md::submdspan(x_dofmap, c, md::full_extent);
157 for (std::size_t j = 0; j < 3; ++j)
158 x_vertices[j * vertices.size() + i] = x_nodes[3 * dofs[local_pos] + j];
159 vertex_to_pos[v] = i;
160 }
161
162 return {std::move(entities), std::move(x_vertices), std::move(vertex_to_pos)};
163}
164
165} // namespace impl
166
180std::vector<std::int32_t> exterior_facet_indices(const Topology& topology,
181 int facet_type_idx);
182
194std::vector<std::int32_t> exterior_facet_indices(const Topology& topology);
195
213using CellPartitionFunction = std::function<graph::AdjacencyList<std::int32_t>(
214 MPI_Comm comm, int nparts, const std::vector<CellType>& cell_types,
215 const std::vector<std::span<const std::int64_t>>& cells)>;
216
221using CellReorderFunction = std::function<std::vector<std::int32_t>(
223
232inline auto
234 std::optional<std::int32_t> max_facet_to_cell_links)
235{
251 return [&, max_facet_to_cell_links](
252 const std::vector<CellType>& celltypes,
253 const std::vector<fem::ElementDofLayout>& doflayouts,
254 const std::vector<std::vector<int>>& ghost_owners,
255 std::vector<std::vector<std::int64_t>>& cells,
256 std::vector<std::vector<std::int64_t>>& cells_v,
257 std::vector<std::vector<std::int64_t>>& original_idx,
258 int num_threads) -> std::vector<std::int64_t>
259 {
260 // Build local dual graph for owned cells to (i) get list of vertices
261 // on the process boundary and (ii) apply re-ordering to cells for
262 // locality
263
264 spdlog::info("Build local dual graphs, re-order cells, and compute process "
265 "boundary vertices.");
266
267 std::vector<std::pair<std::vector<std::int64_t>, int>> facets;
268
269 // Build lists of cells (by cell type) that excludes ghosts
270 std::vector<std::span<const std::int64_t>> cells1_v_local;
271 for (std::size_t i = 0; i < celltypes.size(); ++i)
272 {
273 int num_cell_vertices = mesh::num_cell_vertices(celltypes[i]);
274 std::size_t num_owned_cells
275 = cells_v[i].size() / num_cell_vertices - ghost_owners[i].size();
276 cells1_v_local.emplace_back(cells_v[i].data(),
277 num_owned_cells * num_cell_vertices);
278
279 // Build local dual graph for cell type
280 auto [graph, unmatched_facets, max_v, _facet_attached_cells]
281 = build_local_dual_graph(std::vector{celltypes[i]},
282 std::vector{cells1_v_local.back()},
283 max_facet_to_cell_links, num_threads);
284
285 // Store unmatched_facets for current cell type
286 facets.emplace_back(std::move(unmatched_facets), max_v);
287
288 // Compute re-ordering of graph
289 const std::vector<std::int32_t> remap = reorder_fn(graph);
290
291 // Update 'original' indices
292 const std::vector<std::int64_t>& orig_idx = original_idx[i];
293 std::vector<std::int64_t> _original_idx(orig_idx.size());
294 std::copy_n(orig_idx.rbegin(), ghost_owners[i].size(),
295 _original_idx.rbegin());
296 {
297 for (std::size_t j = 0; j < remap.size(); ++j)
298 _original_idx[remap[j]] = orig_idx[j];
299 }
300 original_idx[i] = _original_idx;
301
302 // Reorder cells
303 impl::reorder_list(
304 std::span(cells_v[i].data(), remap.size() * num_cell_vertices),
305 remap);
306 impl::reorder_list(
307 std::span(cells[i].data(), remap.size() * doflayouts[i].num_dofs()),
308 remap);
309 }
310
311 if (facets.size() == 1) // Optimisation for single cell type
312 {
313 std::vector<std::int64_t>& vertices = facets.front().first;
314
315 // Remove duplicated vertex indices
316 std::ranges::sort(vertices);
317 auto [unique_end, range_end] = std::ranges::unique(vertices);
318 vertices.erase(unique_end, range_end);
319
320 // Remove -1 if it appears as first entity. This can happen in
321 // mixed topology meshes where '-1' is used to pad facet data when
322 // cells facets have differing numbers of vertices.
323 if (!vertices.empty() and vertices.front() == -1)
324 vertices.erase(vertices.begin());
325
326 return vertices;
327 }
328 else
329 {
330 // Pack 'unmatched' facets for all cell types into a single
331 // column-major array (facets0): column j holds vertex j across
332 // all facets, so the multi-column sort_by_perm() overload can
333 // operate directly on contiguous per-column data.
334 std::size_t num_facets = std::accumulate(
335 facets.begin(), facets.end(), std::size_t(0),
336 [](std::size_t x, auto& y)
337 { return x + (y.second > 0 ? y.first.size() / y.second : 0); });
338 int max_v = std::ranges::max_element(facets, [](auto& a, auto& b)
339 { return a.second < b.second; })
340 ->second;
341
342 std::vector<std::int64_t> facets0_b(max_v * num_facets, -1);
343 std::vector<std::span<std::int64_t>> facets0(max_v);
344 for (int j = 0; j < max_v; ++j)
345 facets0[j] = std::span(facets0_b.data() + j * num_facets, num_facets);
346
347 {
348 std::size_t row = 0;
349 for (const auto& [v_data, num_v] : facets)
350 {
351 for (auto it = v_data.begin(); it != v_data.end(); it += num_v, ++row)
352 for (int j = 0; j < num_v; ++j)
353 facets0[j][row] = *std::next(it, j);
354 }
355 }
356
357 // Compute row permutation
358 std::vector<std::span<const std::int64_t>> facets0_view(facets0.begin(),
359 facets0.end());
360 const std::vector<std::int32_t> perm = dolfinx::sort_by_perm(
361 std::span<std::span<const std::int64_t>>(facets0_view));
362
363 // For facets in facets0 that appear only once, store the facet
364 // vertices
365 std::vector<std::int64_t> vertices;
366 // TODO: allocate memory for vertices
367
368 // Number of leading valid (non -1 padding) vertices in row
369 auto trim_len = [&facets0, max_v](std::int32_t row)
370 {
371 int n = max_v;
372 while (n > 0 and facets0[n - 1][row] < 0)
373 --n;
374 return n;
375 };
376
377 auto it = perm.begin();
378 while (it != perm.end())
379 {
380 std::int32_t row0 = *it;
381 int n = trim_len(row0);
382
383 // Find iterator to next facet whose leading n vertices differ
384 // from row0
385 auto it1 = std::find_if_not(it, perm.end(),
386 [&facets0, row0, n](std::int32_t row)
387 {
388 for (int j = 0; j < n; ++j)
389 if (facets0[j][row] != facets0[j][row0])
390 return false;
391 return true;
392 });
393
394 // If no repeated facet found, insert row0 vertices
395 if (std::ranges::distance(it, it1) == 1)
396 {
397 for (int j = 0; j < n; ++j)
398 vertices.push_back(facets0[j][row0]);
399 }
400 else if (std::ranges::distance(it, it1) > 2)
401 throw std::runtime_error("More than two matching facets found.");
402
403 // Advance iterator
404 it = it1;
405 }
406
407 // Remove duplicate indices
408 std::ranges::sort(vertices);
409 auto [unique_end, range_end] = std::ranges::unique(vertices);
410 vertices.erase(unique_end, range_end);
411
412 return vertices;
413 }
414 };
415}
416
426std::vector<std::int64_t> extract_topology(CellType cell_type,
427 const fem::ElementDofLayout& layout,
428 std::span<const std::int64_t> cells);
429
438template <std::floating_point T>
439std::vector<T> h(const Mesh<T>& mesh, std::span<const std::int32_t> entities,
440 int dim)
441{
442 if (entities.empty())
443 return std::vector<T>();
444 if (dim == 0)
445 return std::vector<T>(entities.size(), 0);
446
447 // Get the geometry dofs for the vertices of each entity
448 const auto [vertex_xdofs, xdof_shape]
449 = entities_to_geometry(mesh, dim, entities, false);
450
451 // Get the geometry coordinate
452 std::span<const T> x = mesh.geometry().x();
453
454 // Function to compute the length of (p0 - p1)
455 auto delta_norm = [](auto&& p0, auto&& p1)
456 {
457 T norm = 0;
458 for (std::size_t i = 0; i < 3; ++i)
459 norm += (p0[i] - p1[i]) * (p0[i] - p1[i]);
460 return std::sqrt(norm);
461 };
462
463 // Compute greatest distance between any to vertices
464 assert(dim > 0);
465 std::vector<T> h(entities.size(), 0);
466 for (std::size_t e = 0; e < entities.size(); ++e)
467 {
468 // Get geometry 'dof' for each vertex of entity e
469 std::span<const std::int32_t> e_vertices(
470 vertex_xdofs.data() + e * xdof_shape[1], xdof_shape[1]);
471
472 // Compute maximum distance between any two vertices
473 for (std::size_t i = 0; i < e_vertices.size(); ++i)
474 {
475 std::span<const T, 3> p0(x.data() + 3 * e_vertices[i], 3);
476 for (std::size_t j = i + 1; j < e_vertices.size(); ++j)
477 {
478 std::span<const T, 3> p1(x.data() + 3 * e_vertices[j], 3);
479 h[e] = std::max(h[e], delta_norm(p0, p1));
480 }
481 }
482 }
483
484 return h;
485}
486
490template <std::floating_point T>
491std::vector<T> cell_normals(const Mesh<T>& mesh, int dim,
492 std::span<const std::int32_t> entities)
493{
494 if (entities.empty())
495 return std::vector<T>();
496
497 auto topology = mesh.topology();
498 assert(topology);
499 if (topology->cell_type() == CellType::prism and dim == 2)
500 {
501 throw std::runtime_error(
502 "Cell normal computation for prism cells not yet supported.");
503 }
504
505 const int gdim = mesh.geometry().dim();
506 const CellType type = cell_entity_type(topology->cell_type(), dim, 0);
507
508 // Find geometry nodes for topology entities
509 std::span<const T> x = mesh.geometry().x();
510 const auto [geometry_entities, eshape]
511 = entities_to_geometry(mesh, dim, entities, false);
512
513 std::vector<T> n(entities.size() * 3);
514 switch (type)
515 {
516 case CellType::interval:
517 {
518 if (gdim > 2)
519 throw std::invalid_argument("Interval cell normal undefined in 3D.");
520 for (std::size_t i = 0; i < entities.size(); ++i)
521 {
522 // Get the two vertices as points
523 std::array vertices{geometry_entities[i * eshape[1]],
524 geometry_entities[i * eshape[1] + 1]};
525 std::array p = {std::span<const T, 3>(x.data() + 3 * vertices[0], 3),
526 std::span<const T, 3>(x.data() + 3 * vertices[1], 3)};
527
528 // Define normal by rotating tangent counter-clockwise
529 std::array<T, 3> t;
530 std::ranges::transform(p[1], p[0], t.begin(),
531 [](auto x, auto y) { return x - y; });
532
533 T norm = std::sqrt(t[0] * t[0] + t[1] * t[1]);
534 std::span<T, 3> ni(n.data() + 3 * i, 3);
535 ni[0] = -t[1] / norm;
536 ni[1] = t[0] / norm;
537 ni[2] = 0.0;
538 }
539 return n;
540 }
541 case CellType::triangle:
542 {
543 for (std::size_t i = 0; i < entities.size(); ++i)
544 {
545 // Get the three vertices as points
546 std::array vertices = {geometry_entities[i * eshape[1] + 0],
547 geometry_entities[i * eshape[1] + 1],
548 geometry_entities[i * eshape[1] + 2]};
549 std::array p = {std::span<const T, 3>(x.data() + 3 * vertices[0], 3),
550 std::span<const T, 3>(x.data() + 3 * vertices[1], 3),
551 std::span<const T, 3>(x.data() + 3 * vertices[2], 3)};
552
553 // Compute (p1 - p0) and (p2 - p0)
554 std::array<T, 3> dp1, dp2;
555 std::ranges::transform(p[1], p[0], dp1.begin(),
556 [](auto x, auto y) { return x - y; });
557 std::ranges::transform(p[2], p[0], dp2.begin(),
558 [](auto x, auto y) { return x - y; });
559
560 // Define cell normal via cross product of first two edges
561 std::array<T, 3> ni = math::cross(dp1, dp2);
562 T norm = std::sqrt(ni[0] * ni[0] + ni[1] * ni[1] + ni[2] * ni[2]);
563 std::ranges::transform(ni, std::next(n.begin(), 3 * i),
564 [norm](auto x) { return x / norm; });
565 }
566
567 return n;
568 }
569 case CellType::quadrilateral:
570 {
571 // TODO: check
572 for (std::size_t i = 0; i < entities.size(); ++i)
573 {
574 // Get the three vertices as points
575 std::array vertices = {geometry_entities[i * eshape[1] + 0],
576 geometry_entities[i * eshape[1] + 1],
577 geometry_entities[i * eshape[1] + 2]};
578 std::array p = {std::span<const T, 3>(x.data() + 3 * vertices[0], 3),
579 std::span<const T, 3>(x.data() + 3 * vertices[1], 3),
580 std::span<const T, 3>(x.data() + 3 * vertices[2], 3)};
581
582 // Compute (p1 - p0) and (p2 - p0)
583 std::array<T, 3> dp1, dp2;
584 std::ranges::transform(p[1], p[0], dp1.begin(),
585 [](auto x, auto y) { return x - y; });
586 std::ranges::transform(p[2], p[0], dp2.begin(),
587 [](auto x, auto y) { return x - y; });
588
589 // Define cell normal via cross product of first two edges
590 std::array<T, 3> ni = math::cross(dp1, dp2);
591 T norm = std::sqrt(ni[0] * ni[0] + ni[1] * ni[1] + ni[2] * ni[2]);
592 std::ranges::transform(ni, std::next(n.begin(), 3 * i),
593 [norm](auto x) { return x / norm; });
594 }
595
596 return n;
597 }
598 default:
599 throw std::invalid_argument(
600 "cell_normal not supported for this cell type.");
601 }
602}
603
607template <std::floating_point T>
608std::vector<T> compute_midpoints(const Mesh<T>& mesh, int dim,
609 std::span<const std::int32_t> entities)
610{
611 if (entities.empty())
612 return std::vector<T>();
613
614 std::span<const T> x = mesh.geometry().x();
615
616 // Build map from entity -> geometry dof
617 const auto [e_to_g, eshape]
618 = entities_to_geometry(mesh, dim, entities, false);
619
620 std::vector<T> x_mid(entities.size() * 3, 0);
621 for (std::size_t e = 0; e < entities.size(); ++e)
622 {
623 std::span<T, 3> p(x_mid.data() + 3 * e, 3);
624 std::span<const std::int32_t> rows(e_to_g.data() + e * eshape[1],
625 eshape[1]);
626 for (auto row : rows)
627 {
628 std::span<const T, 3> xg(x.data() + 3 * row, 3);
629 std::ranges::transform(p, xg, p.begin(),
630 [size = rows.size()](auto x, auto y)
631 { return x + y / size; });
632 }
633 }
634
635 return x_mid;
636}
637
638namespace impl
639{
644template <std::floating_point T>
645std::pair<std::vector<T>, std::array<std::size_t, 2>>
647{
648 auto topology = mesh.topology();
649 assert(topology);
650 const int tdim = topology->dim();
651
652 // Create entities and connectivities
653
654 // Get all vertex 'node' indices
655 const std::int32_t num_vertices = topology->index_map(0)->size_local()
656 + topology->index_map(0)->num_ghosts();
657
658 std::vector<std::int32_t> vertex_to_node(num_vertices);
659 for (int cell_type_idx = 0,
660 num_cell_types = topology->entity_types(tdim).size();
661 cell_type_idx < num_cell_types; ++cell_type_idx)
662 {
663 auto x_dofmap = mesh.geometry().dofmaps().at(cell_type_idx);
664 auto c_to_v = topology->connectivity({tdim, cell_type_idx}, {0, 0});
665 assert(c_to_v);
666 for (int c = 0; c < c_to_v->num_nodes(); ++c)
667 {
668 auto x_dofs = md::submdspan(x_dofmap, c, md::full_extent);
669 auto vertices = c_to_v->links(c);
670 for (std::size_t i = 0; i < vertices.size(); ++i)
671 vertex_to_node[vertices[i]] = x_dofs[i];
672 }
673 }
674
675 // Pack coordinates of vertices
676 std::span<const T> x_nodes = mesh.geometry().x();
677 std::vector<T> x_vertices(3 * vertex_to_node.size(), 0.0);
678 for (std::size_t i = 0; i < vertex_to_node.size(); ++i)
679 {
680 std::int32_t pos = 3 * vertex_to_node[i];
681 for (std::size_t j = 0; j < 3; ++j)
682 x_vertices[j * vertex_to_node.size() + i] = x_nodes[pos + j];
683 }
684
685 return {std::move(x_vertices), {3, vertex_to_node.size()}};
686}
687
688} // namespace impl
689
691template <typename Fn, typename T>
692concept MarkerFn = std::is_invocable_r<
693 std::vector<std::int8_t>, Fn,
694 md::mdspan<const T,
695 md::extents<std::size_t, 3, md::dynamic_extent>>>::value;
696
712template <std::floating_point T, MarkerFn<T> U>
713std::vector<std::int32_t> locate_entities(const Mesh<T>& mesh, int dim,
714 U marker, int entity_type_idx)
715{
716
717 using cmdspan3x_t
718 = md::mdspan<const T, md::extents<std::size_t, 3, md::dynamic_extent>>;
719
720 // Run marker function on vertex coordinates
721 const auto [xdata, xshape] = impl::compute_vertex_coords(mesh);
722
723 cmdspan3x_t x(xdata.data(), xshape);
724 const std::vector<std::int8_t> marked = marker(x);
725 if (marked.size() != x.extent(1))
726 throw std::runtime_error("Length of array of markers is wrong.");
727
728 auto topology = mesh.topology();
729 assert(topology);
730 const int tdim = topology->dim();
731
732 mesh.topology_mutable()->create_entities(dim);
733 if (dim < tdim)
734 mesh.topology_mutable()->create_connectivity(dim, 0);
735
736 // Iterate over entities of dimension 'dim' to build vector of marked
737 // entities
738 auto e_to_v = topology->connectivity({dim, entity_type_idx}, {0, 0});
739 assert(e_to_v);
740 std::vector<std::int32_t> entities;
741 for (int e = 0; e < e_to_v->num_nodes(); ++e)
742 {
743 // Iterate over entity vertices
744 bool all_vertices_marked = true;
745 for (std::int32_t v : e_to_v->links(e))
746 {
747 if (!marked[v])
748 {
749 all_vertices_marked = false;
750 break;
751 }
752 }
753
754 if (all_vertices_marked)
755 entities.push_back(e);
756 }
757
758 return entities;
759}
760
774template <std::floating_point T, MarkerFn<T> U>
775std::vector<std::int32_t> locate_entities(const Mesh<T>& mesh, int dim,
776 U marker)
777{
778 const int num_entity_types = mesh.topology()->entity_types(dim).size();
779 if (num_entity_types > 1)
780 {
781 throw std::runtime_error(
782 "Multiple entity types of this dimension. Specify entity type index");
783 }
784 return locate_entities(mesh, dim, marker, 0);
785}
786
810template <std::floating_point T, MarkerFn<T> U>
811std::vector<std::int32_t> locate_entities_boundary(const Mesh<T>& mesh, int dim,
812 U marker)
813{
814 // TODO Rewrite this function, it should be possible to simplify considerably
815 auto topology = mesh.topology();
816 assert(topology);
817 int tdim = topology->dim();
818 if (dim == tdim)
819 {
820 throw std::runtime_error(
821 "Cannot use mesh::locate_entities_boundary (boundary) for cells.");
822 }
823
824 // Compute list of boundary facets
825 mesh.topology_mutable()->create_entities(tdim - 1);
826 mesh.topology_mutable()->create_connectivity(tdim - 1, tdim);
827 std::vector<std::int32_t> boundary_facets = exterior_facet_indices(*topology);
828
829 using cmdspan3x_t
830 = md::mdspan<const T, md::extents<std::size_t, 3, md::dynamic_extent>>;
831
832 // Run marker function on the vertex coordinates
833 auto [facet_entities, xdata, vertex_to_pos]
834 = impl::compute_vertex_coords_boundary(mesh, dim, boundary_facets);
835 cmdspan3x_t x(xdata.data(), 3, xdata.size() / 3);
836 std::vector<std::int8_t> marked = marker(x);
837 if (marked.size() != x.extent(1))
838 throw std::runtime_error("Length of array of markers is wrong.");
839
840 // Loop over entities and check vertex markers
841 mesh.topology_mutable()->create_entities(dim);
842 auto e_to_v = topology->connectivity(dim, 0);
843 assert(e_to_v);
844 std::vector<std::int32_t> entities;
845 for (auto e : facet_entities)
846 {
847 // Iterate over entity vertices
848 bool all_vertices_marked = true;
849 for (auto v : e_to_v->links(e))
850 {
851 const std::int32_t pos = vertex_to_pos[v];
852 if (!marked[pos])
853 {
854 all_vertices_marked = false;
855 break;
856 }
857 }
858
859 // Mark facet with all vertices marked
860 if (all_vertices_marked)
861 entities.push_back(e);
862 }
863
864 return entities;
865}
866
885template <std::floating_point T>
886std::pair<std::vector<std::int32_t>, std::array<std::size_t, 2>>
888 std::span<const std::int32_t> entities,
889 bool permute = false)
890{
891 auto topology = mesh.topology();
892 assert(topology);
893 CellType cell_type = topology->cell_type();
894 if ((cell_type == CellType::prism or cell_type == CellType::pyramid)
895 and dim == 2)
896 {
897 throw std::runtime_error("mesh::entities_to_geometry for prism/pyramid "
898 "cell facets not yet supported.");
899 }
900
901 const int tdim = topology->dim();
902 const Geometry<T>& geometry = mesh.geometry();
903 auto xdofs = geometry.dofmaps().front();
904
905 // Get the DOF layout and the number of DOFs per entity
906 const fem::CoordinateElement<T>& coord_ele = geometry.cmaps().front();
907 const fem::ElementDofLayout layout = coord_ele.create_dof_layout();
908 const std::size_t num_entity_dofs = layout.entity_closure_dofs(dim, 0).size();
909 std::vector<std::int32_t> entity_xdofs;
910 entity_xdofs.reserve(entities.size() * num_entity_dofs);
911 std::array<std::size_t, 2> eshape{entities.size(), num_entity_dofs};
912
913 // Get the element's closure DOFs
914 const std::vector<std::vector<std::vector<int>>>& closure_dofs_all
915 = layout.entity_closure_dofs_all();
916
917 // Special case when dim == tdim (cells)
918 if (dim == tdim)
919 {
920 for (std::int32_t c : entities)
921 {
922 // Extract degrees of freedom
923 auto x_c = md::submdspan(xdofs, c, md::full_extent);
924 for (std::int32_t entity_dof : closure_dofs_all[tdim][0])
925 entity_xdofs.push_back(x_c[entity_dof]);
926 }
927
928 return {std::move(entity_xdofs), eshape};
929 }
930
931 assert(dim != tdim);
932
933 auto e_to_c = topology->connectivity(dim, tdim);
934 if (!e_to_c)
935 {
936 throw std::runtime_error(std::format(
937 "Entity-to-cell connectivity has not been computed. Missing dims "
938 "{}->{}",
939 dim, tdim));
940 }
941
942 auto c_to_e = topology->connectivity(tdim, dim);
943 if (!c_to_e)
944 {
945 throw std::runtime_error(std::format(
946 "Cell-to-entity connectivity has not been computed. Missing dims "
947 "{}->{}",
948 tdim, dim));
949 }
950
951 // Get the cell info, which is needed to permute the closure dofs
952 std::span<const std::uint32_t> cell_info;
953 if (permute)
954 cell_info = std::span(mesh.topology()->get_cell_permutation_info());
955
956 for (std::int32_t e : entities)
957 {
958 // Get a cell connected to the entity
959 assert(!e_to_c->links(e).empty());
960 std::int32_t c = e_to_c->links(e).front();
961
962 // Get the local index of the entity
963 std::span<const std::int32_t> cell_entities = c_to_e->links(c);
964 auto it = std::find(cell_entities.begin(), cell_entities.end(), e);
965 assert(it != cell_entities.end());
966 std::size_t local_entity = std::ranges::distance(cell_entities.begin(), it);
967
968 // Cell sub-entities must be permuted so that their local
969 // orientation agrees with their global orientation
970 std::vector<std::int32_t> closure_dofs(closure_dofs_all[dim][local_entity]);
971 if (permute)
972 {
973 mesh::CellType entity_type
974 = mesh::cell_entity_type(cell_type, dim, local_entity);
975 coord_ele.permute_subentity_closure(closure_dofs, cell_info[c],
976 entity_type, local_entity);
977 }
978
979 // Extract degrees of freedom
980 auto x_c = md::submdspan(xdofs, c, md::full_extent);
981 for (std::int32_t entity_dof : closure_dofs)
982 entity_xdofs.push_back(x_c[entity_dof]);
983 }
984
985 return {std::move(entity_xdofs), eshape};
986}
987
1001 std::optional<std::int32_t> max_facet_to_cell_links);
1002
1014 std::optional<std::int32_t> max_facet_to_cell_links);
1015
1023std::vector<std::int32_t>
1024compute_incident_entities(const Topology& topology,
1025 std::span<const std::int32_t> entities, int d0,
1026 int d1);
1027
1073template <typename U>
1075 MPI_Comm comm, MPI_Comm commt,
1076 std::vector<std::span<const std::int64_t>> cells,
1077 const std::vector<fem::CoordinateElement<
1078 typename std::remove_reference_t<typename U::value_type>>>& elements,
1079 MPI_Comm commg, const U& x, std::array<std::size_t, 2> xshape,
1080 const CellPartitionFunction& partitioner,
1081 std::optional<std::int32_t> max_facet_to_cell_links, int num_threads,
1082 const CellReorderFunction& reorder_fn = graph::reorder_rcm)
1083{
1084 if (cells.size() != elements.size())
1085 throw std::runtime_error("Number of cell arrays and elements must match.");
1086 std::vector<CellType> celltypes;
1087 std::ranges::transform(elements, std::back_inserter(celltypes),
1088 [](auto& e) { return e.cell_shape(); });
1089 std::vector<fem::ElementDofLayout> doflayouts;
1090 std::ranges::transform(elements, std::back_inserter(doflayouts),
1091 [](auto& e) { return e.create_dof_layout(); });
1092
1093 // Note: `extract_topology` extracts topology data, i.e. just the
1094 // vertices. For P1 geometry this should just be the identity
1095 // operator. For other elements the filtered lists may have 'gaps',
1096 // i.e. the indices might not be contiguous.
1097 //
1098 // `extract_topology` could be skipped for 'P1 geometry' elements
1099
1100 std::int32_t num_cell_types = cells.size();
1101
1102 // -- Partition topology across ranks of comm
1103 std::vector<std::vector<std::int64_t>> cells1(num_cell_types);
1104 std::vector<std::vector<std::int64_t>> original_idx1(num_cell_types);
1105 std::vector<std::vector<int>> ghost_owners(num_cell_types);
1106 if (partitioner)
1107 {
1108 spdlog::info("Using partitioner with cell data ({} cell types)",
1109 num_cell_types);
1111 if (commt != MPI_COMM_NULL)
1112 {
1113 int size = dolfinx::MPI::size(comm);
1114 std::vector<std::vector<std::int64_t>> t(num_cell_types);
1115 std::vector<std::span<const std::int64_t>> tspan(num_cell_types);
1116 for (std::int32_t i = 0; i < num_cell_types; ++i)
1117 {
1118 t[i] = extract_topology(celltypes[i], doflayouts[i], cells[i]);
1119 tspan[i] = std::span(t[i]);
1120 }
1121 dest = partitioner(commt, size, celltypes, tspan);
1122 }
1123
1124 std::int32_t cell_offset = 0;
1125 for (std::int32_t i = 0; i < num_cell_types; ++i)
1126 {
1127 std::size_t num_cell_nodes = doflayouts[i].num_dofs();
1128 if (cells[i].size() % num_cell_nodes != 0)
1129 {
1130 throw std::runtime_error("Cell array size is not a multiple of the "
1131 "number of nodes per cell.");
1132 }
1133 std::size_t num_cells = cells[i].size() / num_cell_nodes;
1134
1135 // Extract destination AdjacencyList for this cell type
1136 std::vector<std::int32_t> offsets_i(
1137 std::next(dest.offsets().begin(), cell_offset),
1138 std::next(dest.offsets().begin(), cell_offset + num_cells + 1));
1139 std::vector<std::int32_t> data_i(
1140 std::next(dest.array().begin(), offsets_i.front()),
1141 std::next(dest.array().begin(), offsets_i.back()));
1142 std::int32_t offset_0 = offsets_i.front();
1143 std::ranges::for_each(offsets_i,
1144 [&offset_0](std::int32_t& j) { j -= offset_0; });
1145 graph::AdjacencyList<std::int32_t> dest_i(data_i, offsets_i);
1146 cell_offset += num_cells;
1147
1148 // Distribute cells (topology, includes higher-order 'nodes') to
1149 // destination rank
1150 std::vector<int> src_ranks;
1151 std::tie(cells1[i], src_ranks, original_idx1[i], ghost_owners[i])
1152 = graph::build::distribute(comm, cells[i],
1153 {num_cells, num_cell_nodes}, dest_i);
1154 spdlog::debug("Got {} cells from distribution", cells1[i].size());
1155 }
1156 }
1157 else
1158 {
1159 // No partitioning, construct a global index
1160 std::int64_t num_owned = 0;
1161 for (std::int32_t i = 0; i < num_cell_types; ++i)
1162 {
1163 cells1[i] = std::vector<std::int64_t>(cells[i].begin(), cells[i].end());
1164 std::int32_t num_cell_nodes = doflayouts[i].num_dofs();
1165 if (cells1[i].size() % num_cell_nodes != 0)
1166 {
1167 throw std::runtime_error("Cell array size is not a multiple of the "
1168 "number of nodes per cell.");
1169 }
1170 original_idx1[i].resize(cells1[i].size() / num_cell_nodes);
1171 num_owned += original_idx1[i].size();
1172 }
1173
1174 // Add on global offset
1175 std::int64_t global_offset = 0;
1176 MPI_Exscan(&num_owned, &global_offset, 1, MPI_INT64_T, MPI_SUM, comm);
1177 for (std::int32_t i = 0; i < num_cell_types; ++i)
1178 {
1179 std::iota(original_idx1[i].begin(), original_idx1[i].end(),
1180 global_offset);
1181 global_offset += original_idx1[i].size();
1182 }
1183 }
1184
1185 // Extract cell 'topology', i.e. extract the vertices for each cell
1186 // and discard any 'higher-order' nodes
1187 std::vector<std::vector<std::int64_t>> cells1_v(num_cell_types);
1188 for (std::int32_t i = 0; i < num_cell_types; ++i)
1189 {
1190 cells1_v[i] = extract_topology(celltypes[i], doflayouts[i], cells1[i]);
1191 spdlog::info("Extract basic topology: {}->{}", cells1[i].size(),
1192 cells1_v[i].size());
1193 }
1194
1195 auto boundary_v_fn
1196 = create_boundary_vertices_fn(reorder_fn, max_facet_to_cell_links);
1197 const std::vector<std::int64_t> boundary_v
1198 = boundary_v_fn(celltypes, doflayouts, ghost_owners, cells1, cells1_v,
1199 original_idx1, num_threads);
1200
1201 spdlog::debug("Got {} boundary vertices", boundary_v.size());
1202
1203 // Create Topology
1204 std::vector<std::span<const std::int64_t>> cells1_v_span;
1205 std::ranges::transform(cells1_v, std::back_inserter(cells1_v_span),
1206 [](auto& c) { return std::span(c); });
1207 std::vector<std::span<const std::int64_t>> original_idx1_span;
1208 std::ranges::transform(original_idx1, std::back_inserter(original_idx1_span),
1209 [](auto& c) { return std::span(c); });
1210 std::vector<std::span<const int>> ghost_owners_span;
1211 std::ranges::transform(ghost_owners, std::back_inserter(ghost_owners_span),
1212 [](auto& c) { return std::span(c); });
1213 Topology topology
1214 = create_topology(comm, celltypes, cells1_v_span, original_idx1_span,
1215 ghost_owners_span, boundary_v, num_threads);
1216
1217 // Create connectivities required higher-order geometries for creating
1218 // a Geometry object
1219 for (int i = 0; i < num_cell_types; ++i)
1220 {
1221 const auto& entity_dofs = doflayouts[i].entity_dofs_all();
1222 for (int dim = 1; dim < topology.dim(); ++dim)
1223 {
1224 // Accumulate count of all dofs on this dimension
1225 int dim_sum
1226 = std::accumulate(entity_dofs[dim].begin(), entity_dofs[dim].end(), 0,
1227 [](int c, auto v) { return c + v.size(); });
1228
1229 spdlog::debug("Counting entity dofs, dim={}: {}", dim, dim_sum);
1230 if (dim_sum > 0)
1231 topology.create_entities(dim);
1232 }
1233
1234 if (elements[i].needs_dof_permutations())
1235 topology.create_entity_permutations();
1236 }
1237
1238 // Build list of unique (global) node indices from cells1 and
1239 // distribute coordinate data
1240 std::vector<std::int64_t> nodes1, nodes2;
1241 for (std::vector<std::int64_t>& c : cells1)
1242 nodes1.insert(nodes1.end(), c.begin(), c.end());
1243 for (std::vector<std::int64_t>& c : cells1)
1244 nodes2.insert(nodes2.end(), c.begin(), c.end());
1245
1246 dolfinx::radix_sort(nodes1);
1247 auto [unique_end, range_end] = std::ranges::unique(nodes1);
1248 nodes1.erase(unique_end, range_end);
1249
1250 std::vector coords
1251 = dolfinx::MPI::distribute_data(comm, nodes1, commg, x, xshape[1]);
1252
1253 // Create geometry object
1255 = create_geometry(topology, elements, nodes1, nodes2, coords, xshape[1]);
1256
1257 return Mesh(comm, std::make_shared<Topology>(std::move(topology)),
1258 std::move(geometry));
1259}
1260
1300template <typename U>
1302 MPI_Comm comm, MPI_Comm commt, std::span<const std::int64_t> cells,
1304 typename std::remove_reference_t<typename U::value_type>>& element,
1305 MPI_Comm commg, const U& x, std::array<std::size_t, 2> xshape,
1306 const CellPartitionFunction& partitioner,
1307 std::optional<std::int32_t> max_facet_to_cell_links, int num_threads,
1308 const CellReorderFunction& reorder_fn = graph::reorder_rcm)
1309{
1310 return create_mesh(comm, commt, std::vector{cells}, std::vector{element},
1311 commg, x, xshape, partitioner, max_facet_to_cell_links,
1312 num_threads, reorder_fn);
1313}
1314
1335template <typename U>
1336Mesh<typename std::remove_reference_t<typename U::value_type>>
1337create_mesh(MPI_Comm comm, std::span<const std::int64_t> cells,
1339 std::remove_reference_t<typename U::value_type>>& elements,
1340 const U& x, std::array<std::size_t, 2> xshape, GhostMode ghost_mode,
1341 std::optional<std::int32_t> max_facet_to_cell_links = 2)
1342{
1343 if (dolfinx::MPI::size(comm) == 1)
1344 {
1345 return create_mesh(comm, comm, std::vector{cells}, std::vector{elements},
1346 comm, x, xshape, nullptr, max_facet_to_cell_links, 1);
1347 }
1348 else
1349 {
1350 return create_mesh(
1351 comm, comm, std::vector{cells}, std::vector{elements}, comm, x, xshape,
1352 create_cell_partitioner(ghost_mode, max_facet_to_cell_links),
1353 max_facet_to_cell_links, 1);
1354 }
1355}
1356
1370template <std::floating_point T>
1371std::pair<Geometry<T>, std::vector<int32_t>>
1373 std::span<const std::int32_t> subentity_to_entity)
1374{
1375 const Geometry<T>& geometry = mesh.geometry();
1376
1377 // Get the geometry dofs in the sub-geometry based on the entities in
1378 // sub-geometry
1379 const fem::ElementDofLayout layout
1380 = geometry.cmaps().front().create_dof_layout();
1381
1382 const std::vector<std::int32_t> x_indices
1383 = entities_to_geometry(mesh, dim, subentity_to_entity, true).first;
1384
1385 std::vector<std::int32_t> sub_x_dofs = x_indices;
1386 std::ranges::sort(sub_x_dofs);
1387 auto [unique_end, range_end] = std::ranges::unique(sub_x_dofs);
1388 sub_x_dofs.erase(unique_end, range_end);
1389
1390 // Get the sub-geometry dofs owned by this process
1391 auto x_index_map = geometry.index_map();
1392 assert(x_index_map);
1393
1394 std::shared_ptr<common::IndexMap> sub_x_dof_index_map;
1395 std::vector<std::int32_t> subx_to_x_dofmap;
1396 {
1397 auto [map, new_to_old] = common::create_sub_index_map(
1398 *x_index_map, sub_x_dofs, common::IndexMapOrder::any, true);
1399 sub_x_dof_index_map = std::make_shared<common::IndexMap>(std::move(map));
1400 subx_to_x_dofmap = std::move(new_to_old);
1401 }
1402
1403 // Create sub-geometry coordinates
1404 std::span<const T> x = geometry.x();
1405 std::int32_t sub_num_x_dofs = subx_to_x_dofmap.size();
1406 std::vector<T> sub_x(3 * sub_num_x_dofs);
1407 for (std::int32_t i = 0; i < sub_num_x_dofs; ++i)
1408 {
1409 std::copy_n(std::next(x.begin(), 3 * subx_to_x_dofmap[i]), 3,
1410 std::next(sub_x.begin(), 3 * i));
1411 }
1412
1413 // Create geometry to sub-geometry map
1414 std::vector<std::int32_t> x_to_subx_dof_map(
1415 x_index_map->size_local() + x_index_map->num_ghosts(), -1);
1416 for (std::size_t i = 0; i < subx_to_x_dofmap.size(); ++i)
1417 x_to_subx_dof_map[subx_to_x_dofmap[i]] = i;
1418
1419 // Create sub-geometry dofmap
1420 std::vector<std::int32_t> sub_x_dofmap;
1421 sub_x_dofmap.reserve(x_indices.size());
1422 std::ranges::transform(x_indices, std::back_inserter(sub_x_dofmap),
1423 [&x_to_subx_dof_map](auto x_dof)
1424 {
1425 assert(x_to_subx_dof_map[x_dof] != -1);
1426 return x_to_subx_dof_map[x_dof];
1427 });
1428
1429 // Sub-geometry coordinate element
1430 CellType sub_xcell
1431 = cell_entity_type(geometry.cmaps().front().cell_shape(), dim, 0);
1432
1433 // Special handling of point meshes, as they only support constant
1434 // basis functions
1435 int degree
1436 = (sub_xcell == CellType::point) ? 0 : geometry.cmaps().front().degree();
1437 fem::CoordinateElement<T> sub_cmap(sub_xcell, degree,
1438 geometry.cmaps().front().variant());
1439
1440 // Sub-geometry input_global_indices
1441 const std::vector<std::int64_t>& igi = geometry.input_global_indices();
1442 std::vector<std::int64_t> sub_igi;
1443 sub_igi.reserve(subx_to_x_dofmap.size());
1444 std::ranges::transform(subx_to_x_dofmap, std::back_inserter(sub_igi),
1445 [&igi](auto sub_x_dof) { return igi[sub_x_dof]; });
1446
1447 // Create geometry
1448 return {Geometry(
1449 sub_x_dof_index_map,
1450 std::vector<std::vector<std::int32_t>>{std::move(sub_x_dofmap)},
1451 {sub_cmap}, std::move(sub_x), geometry.dim(), std::move(sub_igi)),
1452 std::move(subx_to_x_dofmap)};
1453}
1454
1464template <std::floating_point T>
1465std::tuple<Mesh<T>, EntityMap, EntityMap, std::vector<std::int32_t>>
1467 std::span<const std::int32_t> entities)
1468{
1469 // Create sub-topology
1470 mesh.topology_mutable()->create_connectivity(dim, 0);
1471 auto [topology, subentity_to_entity, subvertex_to_vertex]
1472 = mesh::create_subtopology(*mesh.topology(), dim, entities);
1473
1474 // Create sub-geometry
1475 const int tdim = mesh.topology()->dim();
1476 mesh.topology_mutable()->create_entities(dim);
1477 mesh.topology_mutable()->create_connectivity(dim, tdim);
1478 mesh.topology_mutable()->create_connectivity(tdim, dim);
1479 mesh.topology_mutable()->create_entity_permutations();
1480 auto [geometry, subx_to_x_dofmap]
1481 = mesh::create_subgeometry(mesh, dim, subentity_to_entity);
1482
1483 Mesh<T> submesh
1484 = Mesh(mesh.comm(), std::make_shared<Topology>(std::move(topology)),
1485 std::move(geometry));
1486 EntityMap entity_map(mesh.topology(), submesh.topology(), dim,
1487 subentity_to_entity);
1488 EntityMap vertex_map(mesh.topology(), submesh.topology(), 0,
1489 subvertex_to_vertex);
1490 return {std::move(submesh), std::move(entity_map), std::move(vertex_map),
1491 std::move(subx_to_x_dofmap)};
1492}
1493
1501template <typename T>
1503 const MeshTags<T>& tags,
1504 std::shared_ptr<const dolfinx::mesh::Topology> submesh_topology,
1505 const EntityMap& vertex_map, const EntityMap& cell_map)
1506{
1507 int tag_dim = tags.dim();
1508 int submesh_tdim = submesh_topology->dim();
1509 auto topology = tags.topology();
1510 if (tag_dim > submesh_tdim)
1511 {
1512 throw std::runtime_error("Tag dimension must be less than or equal to "
1513 "submesh dimension");
1514 }
1515 std::shared_ptr<const dolfinx::common::IndexMap> sub_cell_imap
1516 = submesh_topology->index_map(submesh_tdim);
1517 if (!sub_cell_imap)
1518 {
1519 throw std::runtime_error(
1520 std::format("Entities of dimension {} does not exist in mesh topology.",
1521 submesh_tdim));
1522 }
1523
1524 // Create a map from parent entity to submesh cell
1525 std::int32_t submesh_num_cells
1526 = sub_cell_imap->size_local() + sub_cell_imap->num_ghosts();
1527 auto sub_cells = std::ranges::views::iota(0, submesh_num_cells);
1528 std::vector<std::int32_t> sub_cell_to_parent_entity
1529 = cell_map.sub_topology_to_topology(sub_cells, false);
1530
1531 // Create a full lookup for all cells on the parent mesh, as the tag can have
1532 // entities that are not in the submesh
1533 auto parent_entity_imap = topology->index_map(submesh_tdim);
1534 if (!parent_entity_imap)
1535 {
1536 throw std::runtime_error(std::format(
1537 "Entities of dimension {} does not exist in parent mesh topology.",
1538 submesh_tdim));
1539 }
1540 std::size_t num_parent_entities
1541 = parent_entity_imap->size_local() + parent_entity_imap->num_ghosts();
1542 std::vector<std::int32_t> parent_entity_to_sub_cell(num_parent_entities, -1);
1543 for (std::size_t i = 0; i < sub_cell_to_parent_entity.size(); ++i)
1544 parent_entity_to_sub_cell[sub_cell_to_parent_entity[i]]
1545 = static_cast<std::int32_t>(i);
1546
1547 // Get map from submesh vertex to parent vertex
1548 std::vector<std::int32_t> sub_to_parent_vertex;
1549 {
1550 auto sub_vertex_map = submesh_topology->index_map(0);
1551 std::int32_t num_sub_vertices
1552 = sub_vertex_map->size_local() + sub_vertex_map->num_ghosts();
1553 auto sub_vertices = std::ranges::views::iota(0, num_sub_vertices);
1554
1555 sub_to_parent_vertex
1556 = vertex_map.sub_topology_to_topology(sub_vertices, false);
1557 }
1558 // Access various connectivity maps
1559 auto sub_e_to_v = submesh_topology->connectivity(tag_dim, 0);
1560 auto sub_c_to_e = submesh_topology->connectivity(submesh_tdim, tag_dim);
1561 auto sub_entity_imap = submesh_topology->index_map(tag_dim);
1562 auto e_to_v = topology->connectivity(tag_dim, 0);
1563 std::shared_ptr<const dolfinx::graph::AdjacencyList<std::int32_t>>
1564 e_to_sub_cell = nullptr;
1565 if (tag_dim != submesh_tdim)
1566 {
1567 e_to_sub_cell = topology->connectivity(tag_dim, submesh_tdim);
1568 if (!e_to_sub_cell)
1569 {
1570 throw std::runtime_error(
1571 std::format("Missing connectivity between {} and {} in parent mesh",
1572 tag_dim, submesh_tdim));
1573 }
1574 }
1575
1576 if (!sub_e_to_v)
1577 {
1578 throw std::runtime_error(std::format(
1579 "Missing connectivity between {} and {} in submesh", tag_dim, 0));
1580 }
1581 if (!sub_c_to_e)
1582 {
1583 throw std::runtime_error(
1584 std::format("Missing connectivity between {} and {} in submesh",
1585 submesh_tdim, tag_dim));
1586 }
1587 if (!sub_entity_imap)
1588 {
1589 throw std::runtime_error(std::format(
1590 "Entities of dimension {} does not exist in submesh topology.",
1591 tag_dim));
1592 }
1593 if (!e_to_v)
1594 {
1595 throw std::runtime_error(
1596 std::format("Missing connectivity between {} and 0", tag_dim));
1597 }
1598
1599 // Prepare sub entity to parent map
1600 std::size_t num_sub_entities
1601 = sub_entity_imap->size_local() + sub_entity_imap->num_ghosts();
1602 constexpr T max_val = std::numeric_limits<T>::max();
1603 std::vector<T> submesh_values(num_sub_entities, max_val);
1604 std::vector<std::int32_t> submesh_indices(num_sub_entities);
1605 std::iota(submesh_indices.begin(), submesh_indices.end(), 0);
1606
1607 std::span<const std::int32_t> tagged_entities = tags.indices();
1608 std::span<const T> tagged_values = tags.values();
1609 // For each entity in the tag, find all cells of the submesh connected to this
1610 // entity
1611 for (std::size_t i = 0; i < tagged_entities.size(); ++i)
1612 {
1613 auto find_and_map_sub_entity
1614 = [tag_dim, submesh_tdim, &e_to_v, &parent_entity_to_sub_cell,
1615 &sub_to_parent_vertex, &sub_e_to_v, &sub_c_to_e,
1616 &e_to_sub_cell](std::int32_t entity)
1617 {
1618 // Fast exit if the tag dimension is the same as the submesh dimension,
1619 // as we can directly map the parent entity to the submesh cell
1620 if (tag_dim == submesh_tdim)
1621 return parent_entity_to_sub_cell[entity];
1622
1623 // Given an entity in the parent meshtag, find all submesh-cells that are
1624 // entities in parent mesh that contain this entity.
1625 auto entity_vertices = e_to_v->links(entity);
1626 auto parent_sub_cells = e_to_sub_cell->links(entity);
1627 auto submesh_cells
1628 = parent_sub_cells
1629 | std::views::transform([&parent_entity_to_sub_cell](auto c)
1630 { return parent_entity_to_sub_cell[c]; })
1631 | std::views::filter([](auto sub_cell) { return sub_cell != -1; });
1632 for (auto sub_cell : submesh_cells)
1633 {
1634 for (auto sub_entity : sub_c_to_e->links(sub_cell))
1635 {
1636 // Convert submesh entity vertices to parent vertices
1637 auto parent_vertices
1638 = sub_e_to_v->links(sub_entity)
1639 | std::views::transform([&sub_to_parent_vertex](auto v)
1640 { return sub_to_parent_vertex[v]; });
1641
1642 // Check if all parent vertices of the submesh entity are in the
1643 // parent entity
1644 bool entity_matches = std::ranges::all_of(
1645 parent_vertices,
1646 [&entity_vertices](auto p_v)
1647 {
1648 // With C++23 this can use std::ranges::contains
1649 return std::ranges::find(entity_vertices, p_v)
1650 != std::ranges::end(entity_vertices);
1651 });
1652
1653 // If a match is found, apply values and exit the lambda immediately
1654 if (entity_matches)
1655 return sub_entity;
1656 }
1657 }
1658 return -1;
1659 };
1660
1661 // Execute the search for the current entity
1662 std::int32_t sub_entity = find_and_map_sub_entity(tagged_entities[i]);
1663 if (sub_entity != -1)
1664 submesh_values[sub_entity] = tagged_values[i];
1665 }
1666
1667 // Filter out the entities that were never mapped (values still equal max)
1668 std::vector<std::int32_t> filtered_indices;
1669 std::vector<T> filtered_values;
1670 filtered_indices.reserve(num_sub_entities);
1671 filtered_values.reserve(num_sub_entities);
1672 for (std::size_t i = 0; i < submesh_values.size(); ++i)
1673 {
1674 if (submesh_values[i] != max_val)
1675 {
1676 filtered_indices.push_back(submesh_indices[i]);
1677 filtered_values.push_back(submesh_values[i]);
1678 }
1679 }
1680 filtered_indices.shrink_to_fit();
1681 filtered_values.shrink_to_fit();
1682 MeshTags<T> new_meshtag(submesh_topology, tag_dim, filtered_indices,
1683 filtered_values, tags.name());
1684 return new_meshtag;
1685}
1686
1687} // namespace dolfinx::mesh
Definition CoordinateElement.h:38
ElementDofLayout create_dof_layout() const
Compute and return the dof layout.
Definition CoordinateElement.cpp:79
void permute_subentity_closure(std::span< std::int32_t > d, std::uint32_t cell_info, mesh::CellType entity_type, int entity_index) const
Given the closure DOFs of a cell sub-entity in reference ordering, this function computes the permut...
Definition CoordinateElement.cpp:68
Definition ElementDofLayout.h:31
const std::vector< int > & entity_closure_dofs(int dim, int entity_index) const
Definition ElementDofLayout.cpp:65
const std::vector< std::vector< std::vector< int > > > & entity_closure_dofs_all() const
Definition ElementDofLayout.cpp:77
This class provides a static adjacency list data structure.
Definition AdjacencyList.h:41
const std::vector< LinkData > & array() const
Return contiguous array of links for all nodes (const version).
Definition AdjacencyList.h:188
const std::vector< std::int32_t > & offsets() const
Offset for each node in array() (const version).
Definition AdjacencyList.h:194
A bidirectional map relating entities in one topology to another.
Definition EntityMap.h:22
std::vector< std::int32_t > sub_topology_to_topology(CellRange auto &&entities, bool inverse) const
Map entities between the sub-topology and the parent topology.
Definition EntityMap.h:104
Geometry stores the geometry imposed on a mesh.
Definition Geometry.h:37
MeshTags associate values with mesh topology entities.
Definition MeshTags.h:33
const std::string & name() const
Return name.
Definition MeshTags.h:115
std::span< const std::int32_t > indices() const
Definition MeshTags.h:103
std::span< const T > values() const
Values attached to topology entities.
Definition MeshTags.h:106
int dim() const
Return topological dimension of tagged entities.
Definition MeshTags.h:109
std::shared_ptr< const Topology > topology() const
Return topology.
Definition MeshTags.h:112
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()
Get mesh topology.
Definition Mesh.h:69
Topology stores the topology of a mesh, consisting of mesh entities and connectivity (incidence relat...
Definition Topology.h:49
Requirements on function for geometry marking.
Definition utils.h:692
void reorder_list(std::span< T > list, std::span< const std::int32_t > nodemap)
Re-order the nodes of a fixed-degree adjacency list.
Definition utils.h:57
std::tuple< std::vector< std::int32_t >, std::vector< T >, std::vector< std::int32_t > > compute_vertex_coords_boundary(const mesh::Mesh< T > &mesh, int dim, std::span< const std::int32_t > facets)
Compute the coordinates of 'vertices' for entities of a given dimension that are attached to specifie...
Definition utils.h:88
std::pair< std::vector< T >, std::array< std::size_t, 2 > > compute_vertex_coords(const mesh::Mesh< T > &mesh)
The coordinates for all 'vertices' in the mesh.
Definition utils.h:646
int size(MPI_Comm comm)
Definition MPI.cpp:81
std::vector< std::ranges::range_value_t< U > > distribute_data(MPI_Comm comm0, std::span< const std::int64_t > indices, MPI_Comm comm1, const U &x, int shape1)
Distribute rows of a row-major array to the ranks that require them, via the post office pattern.
Definition MPI.h:734
std::pair< IndexMap, std::vector< std::int32_t > > create_sub_index_map(const IndexMap &imap, std::span< const std::int32_t > indices, IndexMapOrder order=IndexMapOrder::any, bool allow_owner_change=false)
Create a new index map from a subset of indices in an existing index map.
Definition IndexMap.cpp:825
@ any
Allow arbitrary ordering of ghost indices in sub-maps.
Definition IndexMap.h:27
Finite element method functionality.
Definition assemble_expression_impl.h:23
Geometry data structures and algorithms.
Definition BoundingBoxTree.h:24
std::tuple< graph::AdjacencyList< std::int64_t >, std::vector< int >, std::vector< std::int64_t >, std::vector< int > > distribute(MPI_Comm comm, const graph::AdjacencyList< std::int64_t > &list, const graph::AdjacencyList< std::int32_t > &destinations)
Distribute adjacency list nodes to destination ranks.
Definition partition.cpp:40
Graph data structures and algorithms.
Definition AdjacencyList.h:23
std::function< graph::AdjacencyList< std::int32_t >( MPI_Comm, int, const AdjacencyList< std::int64_t > &, bool)> partition_fn
Signature of functions for computing the parallel partitioning of a distributed graph.
Definition partition.h:31
std::vector< std::int32_t > reorder_rcm(const graph::AdjacencyList< std::int32_t > &graph)
Re-order a graph using the Reverse Cuthill-McKee algorithm.
Definition ordering.cpp:149
Mesh data structures and algorithms on meshes.
Definition DofMap.h:32
CellPartitionFunction create_cell_partitioner(mesh::GhostMode ghost_mode, graph::partition_fn partfn, std::optional< std::int32_t > max_facet_to_cell_links)
Create a function that computes destination rank for mesh cells on this rank by applying the default ...
Definition utils.cpp:102
Mesh< typename std::remove_reference_t< typename U::value_type > > create_mesh(MPI_Comm comm, MPI_Comm commt, std::vector< std::span< const std::int64_t > > cells, const std::vector< fem::CoordinateElement< typename std::remove_reference_t< typename U::value_type > > > &elements, MPI_Comm commg, const U &x, std::array< std::size_t, 2 > xshape, const CellPartitionFunction &partitioner, std::optional< std::int32_t > max_facet_to_cell_links, int num_threads, const CellReorderFunction &reorder_fn=graph::reorder_rcm)
Create a distributed mesh::Mesh from mesh data and using the provided graph partitioning function for...
Definition utils.h:1074
std::function< std::vector< std::int32_t >( const graph::AdjacencyList< std::int32_t > &)> CellReorderFunction
Function that reorders (locally) cells that are owned by this process. It takes the local mesh dual g...
Definition utils.h:221
std::tuple< graph::AdjacencyList< std::int32_t >, std::vector< std::int64_t >, int, std::vector< std::int32_t > > build_local_dual_graph(std::span< const CellType > celltypes, const std::vector< std::span< const std::int64_t > > &cells, std::optional< std::int32_t > max_facet_to_cell_links, int num_threads)
Compute the local part of the dual graph (cell-cell connections via facets) and facets with only one ...
Definition graphbuild.cpp:566
MeshTags< T > transfer_meshtags_to_submesh(const MeshTags< T > &tags, std::shared_ptr< const dolfinx::mesh::Topology > submesh_topology, const EntityMap &vertex_map, const EntityMap &cell_map)
Transfer a meshtags object from a parent to a submesh.
Definition utils.h:1502
std::function< graph::AdjacencyList< std::int32_t >( MPI_Comm comm, int nparts, const std::vector< CellType > &cell_types, const std::vector< std::span< const std::int64_t > > &cells)> CellPartitionFunction
Signature for the cell partitioning function. Function that implement this interface compute the dest...
Definition utils.h:213
std::vector< T > cell_normals(const Mesh< T > &mesh, int dim, std::span< const std::int32_t > entities)
Compute normal to given cell (viewed as embedded in 3D).
Definition utils.h:491
std::tuple< Topology, std::vector< int32_t >, std::vector< int32_t > > create_subtopology(const Topology &topology, int dim, std::span< const std::int32_t > entities)
Create a topology for a subset of entities of a given topological dimension.
Definition Topology.cpp:1521
std::tuple< Mesh< T >, EntityMap, EntityMap, std::vector< std::int32_t > > create_submesh(const Mesh< T > &mesh, int dim, std::span< const std::int32_t > entities)
Create a new mesh consisting of a subset of entities in a mesh.
Definition utils.h:1466
std::vector< std::int32_t > exterior_facet_indices(const Topology &topology, int facet_type_idx)
Compute the indices of all exterior facets that are owned by the caller.
Definition utils.cpp:61
std::vector< std::int32_t > locate_entities_boundary(const Mesh< T > &mesh, int dim, U marker)
Compute indices of all mesh entities that are attached to an owned boundary facet and evaluate to tru...
Definition utils.h:811
CellType
Cell type identifier.
Definition cell_types.h:22
int num_cell_vertices(CellType type)
Number vertices for a cell type.
Definition cell_types.cpp:100
std::vector< T > h(const Mesh< T > &mesh, std::span< const std::int32_t > entities, int dim)
Compute greatest distance between any two vertices of the mesh entities (h).
Definition utils.h:439
std::pair< Geometry< T >, std::vector< int32_t > > create_subgeometry(const Mesh< T > &mesh, int dim, std::span< const std::int32_t > subentity_to_entity)
Create a sub-geometry from a mesh and a subset of mesh entities to be included.
Definition utils.h:1372
auto create_boundary_vertices_fn(const CellReorderFunction &reorder_fn, std::optional< std::int32_t > max_facet_to_cell_links)
Creates the default boundary vertices routine for a given reorder function.
Definition utils.h:233
Geometry< typename std::remove_reference_t< typename U::value_type > > create_geometry(const Topology &topology, const std::vector< fem::CoordinateElement< std::remove_reference_t< typename U::value_type > > > &elements, std::span< const std::int64_t > nodes, std::span< const std::int64_t > xdofs, const U &x, int dim, const std::function< std::vector< int >(const graph::AdjacencyList< std::int32_t > &)> &reorder_fn=nullptr)
Build Geometry from input data.
Definition Geometry.h:235
std::pair< std::vector< std::int32_t >, std::array< std::size_t, 2 > > 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:887
std::vector< std::int32_t > compute_incident_entities(const Topology &topology, std::span< const std::int32_t > entities, int d0, int d1)
Compute incident entities.
Definition utils.cpp:134
std::vector< std::int64_t > extract_topology(CellType cell_type, const fem::ElementDofLayout &layout, std::span< const std::int64_t > cells)
Extract topology from cell data, i.e. extract cell vertices.
Definition utils.cpp:32
std::vector< std::int32_t > locate_entities(const Mesh< T > &mesh, int dim, U marker, int entity_type_idx)
Compute indices of all mesh entities that evaluate to true for the provided geometric marking functio...
Definition utils.h:713
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:608
GhostMode
Enum for different partitioning ghost modes.
Definition utils.h:44
Topology create_topology(MPI_Comm comm, const std::vector< CellType > &cell_types, std::vector< std::span< const std::int64_t > > cells, std::vector< std::span< const std::int64_t > > original_cell_index, std::vector< std::span< const int > > ghost_owners, std::span< const std::int64_t > boundary_vertices, int num_threads)
Create a mesh topology.
Definition Topology.cpp:1135
CellType cell_entity_type(CellType type, int d, int index)
Return type of cell for entity of dimension d at given entity index.
Definition cell_types.h:111
constexpr void radix_sort(R &&range, P proj={})
Sort a range with radix sorting algorithm. The bucket size is determined by the number of bits to sor...
Definition sort.h:81
std::vector< std::int32_t > sort_by_perm(std::span< const T > x, std::size_t shape1, std::optional< std::size_t > ncols=std::nullopt)
Compute the permutation array that sorts a 2D array by row.
Definition sort.h:200