DOLFINx 0.9.0
DOLFINx C++ interface
Loading...
Searching...
No Matches
utils.h
Go to the documentation of this file.
1// Copyright (C) 2019-2020 Garth N. Wells
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 "Mesh.h"
10#include "Topology.h"
11#include "graphbuild.h"
12#include <algorithm>
13#include <basix/mdspan.hpp>
14#include <concepts>
15#include <dolfinx/graph/AdjacencyList.h>
16#include <dolfinx/graph/ordering.h>
17#include <dolfinx/graph/partition.h>
18#include <functional>
19#include <mpi.h>
20#include <span>
21
24
25namespace dolfinx::fem
26{
27class ElementDofLayout;
28}
29
30namespace dolfinx::mesh
31{
32enum class CellType;
33
35enum class GhostMode : int
36{
37 none,
38 shared_facet,
39 shared_vertex
40};
41
42namespace impl
43{
45template <typename T>
46void reorder_list(std::span<T> list, std::span<const std::int32_t> nodemap)
47{
48 if (nodemap.empty())
49 return;
50
51 assert(list.size() % nodemap.size() == 0);
52 int degree = list.size() / nodemap.size();
53 const std::vector<T> orig(list.begin(), list.end());
54 for (std::size_t n = 0; n < nodemap.size(); ++n)
55 {
56 auto links_old = std::span(orig.data() + n * degree, degree);
57 auto links_new = list.subspan(nodemap[n] * degree, degree);
58 std::ranges::copy(links_old, links_new.begin());
59 }
60}
61
75template <std::floating_point T>
76std::tuple<std::vector<std::int32_t>, std::vector<T>, std::vector<std::int32_t>>
78 std::span<const std::int32_t> facets)
79{
80 auto topology = mesh.topology();
81 assert(topology);
82 const int tdim = topology->dim();
83 if (dim == tdim)
84 {
85 throw std::runtime_error(
86 "Cannot use mesh::locate_entities_boundary (boundary) for cells.");
87 }
88
89 // Build set of vertices on boundary and set of boundary entities
90 mesh.topology_mutable()->create_connectivity(tdim - 1, 0);
91 mesh.topology_mutable()->create_connectivity(tdim - 1, dim);
92 std::vector<std::int32_t> vertices, entities;
93 {
94 auto f_to_v = topology->connectivity(tdim - 1, 0);
95 assert(f_to_v);
96 auto f_to_e = topology->connectivity(tdim - 1, dim);
97 assert(f_to_e);
98 for (auto f : facets)
99 {
100 auto v = f_to_v->links(f);
101 vertices.insert(vertices.end(), v.begin(), v.end());
102 auto e = f_to_e->links(f);
103 entities.insert(entities.end(), e.begin(), e.end());
104 }
105
106 // Build vector of boundary vertices
107 {
108 std::ranges::sort(vertices);
109 auto [unique_end, range_end] = std::ranges::unique(vertices);
110 vertices.erase(unique_end, range_end);
111 }
112
113 {
114 std::ranges::sort(entities);
115 auto [unique_end, range_end] = std::ranges::unique(entities);
116 entities.erase(unique_end, range_end);
117 }
118 }
119
120 // Get geometry data
121 auto x_dofmap = mesh.geometry().dofmap();
122 std::span<const T> x_nodes = mesh.geometry().x();
123
124 // Get all vertex 'node' indices
125 mesh.topology_mutable()->create_connectivity(0, tdim);
126 mesh.topology_mutable()->create_connectivity(tdim, 0);
127 auto v_to_c = topology->connectivity(0, tdim);
128 assert(v_to_c);
129 auto c_to_v = topology->connectivity(tdim, 0);
130 assert(c_to_v);
131 std::vector<T> x_vertices(3 * vertices.size(), -1.0);
132 std::vector<std::int32_t> vertex_to_pos(v_to_c->num_nodes(), -1);
133 for (std::size_t i = 0; i < vertices.size(); ++i)
134 {
135 const std::int32_t v = vertices[i];
136
137 // Get first cell and find position
138 const int c = v_to_c->links(v).front();
139 auto cell_vertices = c_to_v->links(c);
140 auto it = std::find(cell_vertices.begin(), cell_vertices.end(), v);
141 assert(it != cell_vertices.end());
142 const int local_pos = std::distance(cell_vertices.begin(), it);
143
144 auto dofs = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan(
145 x_dofmap, c, MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent);
146 for (std::size_t j = 0; j < 3; ++j)
147 x_vertices[j * vertices.size() + i] = x_nodes[3 * dofs[local_pos] + j];
148 vertex_to_pos[v] = i;
149 }
150
151 return {std::move(entities), std::move(x_vertices), std::move(vertex_to_pos)};
152}
153
154} // namespace impl
155
167std::vector<std::int32_t> exterior_facet_indices(const Topology& topology);
168
185using CellPartitionFunction = std::function<graph::AdjacencyList<std::int32_t>(
186 MPI_Comm comm, int nparts, const std::vector<CellType>& cell_types,
187 const std::vector<std::span<const std::int64_t>>& cells)>;
188
198std::vector<std::int64_t> extract_topology(CellType cell_type,
199 const fem::ElementDofLayout& layout,
200 std::span<const std::int64_t> cells);
201
210template <std::floating_point T>
211std::vector<T> h(const Mesh<T>& mesh, std::span<const std::int32_t> entities,
212 int dim)
213{
214 if (entities.empty())
215 return std::vector<T>();
216 if (dim == 0)
217 return std::vector<T>(entities.size(), 0);
218
219 // Get the geometry dofs for the vertices of each entity
220 const std::vector<std::int32_t> vertex_xdofs
221 = entities_to_geometry(mesh, dim, entities, false);
222 assert(!entities.empty());
223 const std::size_t num_vertices = vertex_xdofs.size() / entities.size();
224
225 // Get the geometry coordinate
226 std::span<const T> x = mesh.geometry().x();
227
228 // Function to compute the length of (p0 - p1)
229 auto delta_norm = [](auto&& p0, auto&& p1)
230 {
231 T norm = 0;
232 for (std::size_t i = 0; i < 3; ++i)
233 norm += (p0[i] - p1[i]) * (p0[i] - p1[i]);
234 return std::sqrt(norm);
235 };
236
237 // Compute greatest distance between any to vertices
238 assert(dim > 0);
239 std::vector<T> h(entities.size(), 0);
240 for (std::size_t e = 0; e < entities.size(); ++e)
241 {
242 // Get geometry 'dof' for each vertex of entity e
243 std::span<const std::int32_t> e_vertices(
244 vertex_xdofs.data() + e * num_vertices, num_vertices);
245
246 // Compute maximum distance between any two vertices
247 for (std::size_t i = 0; i < e_vertices.size(); ++i)
248 {
249 std::span<const T, 3> p0(x.data() + 3 * e_vertices[i], 3);
250 for (std::size_t j = i + 1; j < e_vertices.size(); ++j)
251 {
252 std::span<const T, 3> p1(x.data() + 3 * e_vertices[j], 3);
253 h[e] = std::max(h[e], delta_norm(p0, p1));
254 }
255 }
256 }
257
258 return h;
259}
260
264template <std::floating_point T>
265std::vector<T> cell_normals(const Mesh<T>& mesh, int dim,
266 std::span<const std::int32_t> entities)
267{
268 auto topology = mesh.topology();
269 assert(topology);
270
271 if (entities.empty())
272 return std::vector<T>();
273
274 if (topology->cell_type() == CellType::prism and dim == 2)
275 throw std::runtime_error("More work needed for prism cell");
276
277 const int gdim = mesh.geometry().dim();
278 const CellType type = cell_entity_type(topology->cell_type(), dim, 0);
279
280 // Find geometry nodes for topology entities
281 std::span<const T> x = mesh.geometry().x();
282 std::vector<std::int32_t> geometry_entities
283 = entities_to_geometry(mesh, dim, entities, false);
284
285 const std::size_t shape1 = geometry_entities.size() / entities.size();
286 std::vector<T> n(entities.size() * 3);
287 switch (type)
288 {
289 case CellType::interval:
290 {
291 if (gdim > 2)
292 throw std::invalid_argument("Interval cell normal undefined in 3D");
293 for (std::size_t i = 0; i < entities.size(); ++i)
294 {
295 // Get the two vertices as points
296 std::array vertices{geometry_entities[i * shape1],
297 geometry_entities[i * shape1 + 1]};
298 std::array p = {std::span<const T, 3>(x.data() + 3 * vertices[0], 3),
299 std::span<const T, 3>(x.data() + 3 * vertices[1], 3)};
300
301 // Define normal by rotating tangent counter-clockwise
302 std::array<T, 3> t;
303 std::ranges::transform(p[1], p[0], t.begin(),
304 [](auto x, auto y) { return x - y; });
305
306 T norm = std::sqrt(t[0] * t[0] + t[1] * t[1]);
307 std::span<T, 3> ni(n.data() + 3 * i, 3);
308 ni[0] = -t[1] / norm;
309 ni[1] = t[0] / norm;
310 ni[2] = 0.0;
311 }
312 return n;
313 }
314 case CellType::triangle:
315 {
316 for (std::size_t i = 0; i < entities.size(); ++i)
317 {
318 // Get the three vertices as points
319 std::array vertices = {geometry_entities[i * shape1 + 0],
320 geometry_entities[i * shape1 + 1],
321 geometry_entities[i * shape1 + 2]};
322 std::array p = {std::span<const T, 3>(x.data() + 3 * vertices[0], 3),
323 std::span<const T, 3>(x.data() + 3 * vertices[1], 3),
324 std::span<const T, 3>(x.data() + 3 * vertices[2], 3)};
325
326 // Compute (p1 - p0) and (p2 - p0)
327 std::array<T, 3> dp1, dp2;
328 std::ranges::transform(p[1], p[0], dp1.begin(),
329 [](auto x, auto y) { return x - y; });
330 std::ranges::transform(p[2], p[0], dp2.begin(),
331 [](auto x, auto y) { return x - y; });
332
333 // Define cell normal via cross product of first two edges
334 std::array<T, 3> ni = math::cross(dp1, dp2);
335 T norm = std::sqrt(ni[0] * ni[0] + ni[1] * ni[1] + ni[2] * ni[2]);
336 std::ranges::transform(ni, std::next(n.begin(), 3 * i),
337 [norm](auto x) { return x / norm; });
338 }
339
340 return n;
341 }
342 case CellType::quadrilateral:
343 {
344 // TODO: check
345 for (std::size_t i = 0; i < entities.size(); ++i)
346 {
347 // Get the three vertices as points
348 std::array vertices = {geometry_entities[i * shape1 + 0],
349 geometry_entities[i * shape1 + 1],
350 geometry_entities[i * shape1 + 2]};
351 std::array p = {std::span<const T, 3>(x.data() + 3 * vertices[0], 3),
352 std::span<const T, 3>(x.data() + 3 * vertices[1], 3),
353 std::span<const T, 3>(x.data() + 3 * vertices[2], 3)};
354
355 // Compute (p1 - p0) and (p2 - p0)
356 std::array<T, 3> dp1, dp2;
357 std::ranges::transform(p[1], p[0], dp1.begin(),
358 [](auto x, auto y) { return x - y; });
359 std::ranges::transform(p[2], p[0], dp2.begin(),
360 [](auto x, auto y) { return x - y; });
361
362 // Define cell normal via cross product of first two edges
363 std::array<T, 3> ni = math::cross(dp1, dp2);
364 T norm = std::sqrt(ni[0] * ni[0] + ni[1] * ni[1] + ni[2] * ni[2]);
365 std::ranges::transform(ni, std::next(n.begin(), 3 * i),
366 [norm](auto x) { return x / norm; });
367 }
368
369 return n;
370 }
371 default:
372 throw std::invalid_argument(
373 "cell_normal not supported for this cell type.");
374 }
375}
376
380template <std::floating_point T>
381std::vector<T> compute_midpoints(const Mesh<T>& mesh, int dim,
382 std::span<const std::int32_t> entities)
383{
384 if (entities.empty())
385 return std::vector<T>();
386
387 std::span<const T> x = mesh.geometry().x();
388
389 // Build map from entity -> geometry dof
390 const std::vector<std::int32_t> e_to_g
391 = entities_to_geometry(mesh, dim, entities, false);
392 std::size_t shape1 = e_to_g.size() / entities.size();
393
394 std::vector<T> x_mid(entities.size() * 3, 0);
395 for (std::size_t e = 0; e < entities.size(); ++e)
396 {
397 std::span<T, 3> p(x_mid.data() + 3 * e, 3);
398 std::span<const std::int32_t> rows(e_to_g.data() + e * shape1, shape1);
399 for (auto row : rows)
400 {
401 std::span<const T, 3> xg(x.data() + 3 * row, 3);
402 std::ranges::transform(p, xg, p.begin(),
403 [size = rows.size()](auto x, auto y)
404 { return x + y / size; });
405 }
406 }
407
408 return x_mid;
409}
410
411namespace impl
412{
417template <std::floating_point T>
418std::pair<std::vector<T>, std::array<std::size_t, 2>>
420{
421 auto topology = mesh.topology();
422 assert(topology);
423 const int tdim = topology->dim();
424
425 // Create entities and connectivities
426 mesh.topology_mutable()->create_connectivity(tdim, 0);
427
428 // Get all vertex 'node' indices
429 auto x_dofmap = mesh.geometry().dofmap();
430 const std::int32_t num_vertices = topology->index_map(0)->size_local()
431 + topology->index_map(0)->num_ghosts();
432 auto c_to_v = topology->connectivity(tdim, 0);
433 assert(c_to_v);
434 std::vector<std::int32_t> vertex_to_node(num_vertices);
435 for (int c = 0; c < c_to_v->num_nodes(); ++c)
436 {
437 auto x_dofs = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan(
438 x_dofmap, c, MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent);
439 auto vertices = c_to_v->links(c);
440 for (std::size_t i = 0; i < vertices.size(); ++i)
441 vertex_to_node[vertices[i]] = x_dofs[i];
442 }
443
444 // Pack coordinates of vertices
445 std::span<const T> x_nodes = mesh.geometry().x();
446 std::vector<T> x_vertices(3 * vertex_to_node.size(), 0.0);
447 for (std::size_t i = 0; i < vertex_to_node.size(); ++i)
448 {
449 const int pos = 3 * vertex_to_node[i];
450 for (std::size_t j = 0; j < 3; ++j)
451 x_vertices[j * vertex_to_node.size() + i] = x_nodes[pos + j];
452 }
453
454 return {std::move(x_vertices), {3, vertex_to_node.size()}};
455}
456
457} // namespace impl
458
460template <typename Fn, typename T>
461concept MarkerFn = std::is_invocable_r<
462 std::vector<std::int8_t>, Fn,
463 MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan<
464 const T, MDSPAN_IMPL_STANDARD_NAMESPACE::extents<
465 std::size_t, 3,
466 MDSPAN_IMPL_STANDARD_NAMESPACE::dynamic_extent>>>::value;
467
481template <std::floating_point T, MarkerFn<T> U>
482std::vector<std::int32_t> locate_entities(const Mesh<T>& mesh, int dim,
483 U marker)
484{
485 using cmdspan3x_t = MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan<
486 const T,
487 MDSPAN_IMPL_STANDARD_NAMESPACE::extents<
488 std::size_t, 3, MDSPAN_IMPL_STANDARD_NAMESPACE::dynamic_extent>>;
489
490 // Run marker function on vertex coordinates
491 const auto [xdata, xshape] = impl::compute_vertex_coords(mesh);
492 cmdspan3x_t x(xdata.data(), xshape);
493 const std::vector<std::int8_t> marked = marker(x);
494 if (marked.size() != x.extent(1))
495 throw std::runtime_error("Length of array of markers is wrong.");
496
497 auto topology = mesh.topology();
498 assert(topology);
499 const int tdim = topology->dim();
500
501 mesh.topology_mutable()->create_entities(dim);
502 mesh.topology_mutable()->create_connectivity(tdim, 0);
503 if (dim < tdim)
504 mesh.topology_mutable()->create_connectivity(dim, 0);
505
506 // Iterate over entities of dimension 'dim' to build vector of marked
507 // entities
508 auto e_to_v = topology->connectivity(dim, 0);
509 assert(e_to_v);
510 std::vector<std::int32_t> entities;
511 for (int e = 0; e < e_to_v->num_nodes(); ++e)
512 {
513 // Iterate over entity vertices
514 bool all_vertices_marked = true;
515 for (std::int32_t v : e_to_v->links(e))
516 {
517 if (!marked[v])
518 {
519 all_vertices_marked = false;
520 break;
521 }
522 }
523
524 if (all_vertices_marked)
525 entities.push_back(e);
526 }
527
528 return entities;
529}
530
554template <std::floating_point T, MarkerFn<T> U>
555std::vector<std::int32_t> locate_entities_boundary(const Mesh<T>& mesh, int dim,
556 U marker)
557{
558 auto topology = mesh.topology();
559 assert(topology);
560 const int tdim = topology->dim();
561 if (dim == tdim)
562 {
563 throw std::runtime_error(
564 "Cannot use mesh::locate_entities_boundary (boundary) for cells.");
565 }
566
567 // Compute list of boundary facets
568 mesh.topology_mutable()->create_entities(tdim - 1);
569 mesh.topology_mutable()->create_connectivity(tdim - 1, tdim);
570 const std::vector<std::int32_t> boundary_facets
571 = exterior_facet_indices(*topology);
572
573 using cmdspan3x_t = MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan<
574 const T,
575 MDSPAN_IMPL_STANDARD_NAMESPACE::extents<
576 std::size_t, 3, MDSPAN_IMPL_STANDARD_NAMESPACE::dynamic_extent>>;
577
578 // Run marker function on the vertex coordinates
579 const auto [facet_entities, xdata, vertex_to_pos]
580 = impl::compute_vertex_coords_boundary(mesh, dim, boundary_facets);
581 cmdspan3x_t x(xdata.data(), 3, xdata.size() / 3);
582 const std::vector<std::int8_t> marked = marker(x);
583 if (marked.size() != x.extent(1))
584 throw std::runtime_error("Length of array of markers is wrong.");
585
586 // Loop over entities and check vertex markers
587 mesh.topology_mutable()->create_entities(dim);
588 auto e_to_v = topology->connectivity(dim, 0);
589 assert(e_to_v);
590 std::vector<std::int32_t> entities;
591 for (auto e : facet_entities)
592 {
593 // Iterate over entity vertices
594 bool all_vertices_marked = true;
595 for (auto v : e_to_v->links(e))
596 {
597 const std::int32_t pos = vertex_to_pos[v];
598 if (!marked[pos])
599 {
600 all_vertices_marked = false;
601 break;
602 }
603 }
604
605 // Mark facet with all vertices marked
606 if (all_vertices_marked)
607 entities.push_back(e);
608 }
609
610 return entities;
611}
612
631template <std::floating_point T>
632std::vector<std::int32_t>
633entities_to_geometry(const Mesh<T>& mesh, int dim,
634 std::span<const std::int32_t> entities,
635 bool permute = false)
636{
637 auto topology = mesh.topology();
638 assert(topology);
639 CellType cell_type = topology->cell_type();
640 if (cell_type == CellType::prism and dim == 2)
641 throw std::runtime_error("More work needed for prism cells");
642
643 const int tdim = topology->dim();
644 const Geometry<T>& geometry = mesh.geometry();
645 auto xdofs = geometry.dofmap();
646
647 // Get the DOF layout and the number of DOFs per entity
648 const fem::CoordinateElement<T>& coord_ele = geometry.cmap();
649 const fem::ElementDofLayout layout = coord_ele.create_dof_layout();
650 const std::size_t num_entity_dofs = layout.num_entity_closure_dofs(dim);
651 std::vector<std::int32_t> entity_xdofs;
652 entity_xdofs.reserve(entities.size() * num_entity_dofs);
653
654 // Get the element's closure DOFs
655 const std::vector<std::vector<std::vector<int>>>& closure_dofs_all
656 = layout.entity_closure_dofs_all();
657
658 // Special case when dim == tdim (cells)
659 if (dim == tdim)
660 {
661 for (std::size_t i = 0; i < entities.size(); ++i)
662 {
663 const std::int32_t c = entities[i];
664 // Extract degrees of freedom
665 auto x_c = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan(
666 xdofs, c, MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent);
667 for (std::int32_t entity_dof : closure_dofs_all[tdim][0])
668 entity_xdofs.push_back(x_c[entity_dof]);
669 }
670 return entity_xdofs;
671 }
672
673 assert(dim != tdim);
674
675 auto e_to_c = topology->connectivity(dim, tdim);
676 if (!e_to_c)
677 {
678 throw std::runtime_error(
679 "Entity-to-cell connectivity has not been computed. Missing dims "
680 + std::to_string(dim) + "->" + std::to_string(tdim));
681 }
682
683 auto c_to_e = topology->connectivity(tdim, dim);
684 if (!c_to_e)
685 {
686 throw std::runtime_error(
687 "Cell-to-entity connectivity has not been computed. Missing dims "
688 + std::to_string(tdim) + "->" + std::to_string(dim));
689 }
690
691 // Get the cell info, which is needed to permute the closure dofs
692 std::span<const std::uint32_t> cell_info;
693 if (permute)
694 cell_info = std::span(mesh.topology()->get_cell_permutation_info());
695
696 for (std::size_t i = 0; i < entities.size(); ++i)
697 {
698 const std::int32_t e = entities[i];
699
700 // Get a cell connected to the entity
701 assert(!e_to_c->links(e).empty());
702 std::int32_t c = e_to_c->links(e).front();
703
704 // Get the local index of the entity
705 std::span<const std::int32_t> cell_entities = c_to_e->links(c);
706 auto it = std::find(cell_entities.begin(), cell_entities.end(), e);
707 assert(it != cell_entities.end());
708 std::size_t local_entity = std::distance(cell_entities.begin(), it);
709
710 std::vector<std::int32_t> closure_dofs(closure_dofs_all[dim][local_entity]);
711
712 // Cell sub-entities must be permuted so that their local orientation agrees
713 // with their global orientation
714 if (permute)
715 {
716 mesh::CellType entity_type
717 = mesh::cell_entity_type(cell_type, dim, local_entity);
718 coord_ele.permute_subentity_closure(closure_dofs, cell_info[c],
719 entity_type, local_entity);
720 }
721
722 // Extract degrees of freedom
723 auto x_c = MDSPAN_IMPL_STANDARD_NAMESPACE::submdspan(
724 xdofs, c, MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent);
725 for (std::int32_t entity_dof : closure_dofs)
726 entity_xdofs.push_back(x_c[entity_dof]);
727 }
728
729 return entity_xdofs;
730}
731
737 = mesh::GhostMode::none,
738 const graph::partition_fn& partfn
740
748std::vector<std::int32_t>
749compute_incident_entities(const Topology& topology,
750 std::span<const std::int32_t> entities, int d0,
751 int d1);
752
782template <typename U>
784 MPI_Comm comm, MPI_Comm commt, std::span<const std::int64_t> cells,
786 typename std::remove_reference_t<typename U::value_type>>& element,
787 MPI_Comm commg, const U& x, std::array<std::size_t, 2> xshape,
788 const CellPartitionFunction& partitioner)
789{
790 CellType celltype = element.cell_shape();
791 const fem::ElementDofLayout doflayout = element.create_dof_layout();
792
793 const int num_cell_vertices = mesh::num_cell_vertices(element.cell_shape());
794 std::size_t num_cell_nodes = doflayout.num_dofs();
795
796 // Note: `extract_topology` extracts topology data, i.e. just the
797 // vertices. For P1 geometry this should just be the identity
798 // operator. For other elements the filtered lists may have 'gaps',
799 // i.e. the indices might not be contiguous.
800 //
801 // `extract_topology` could be skipped for 'P1 geometry' elements
802
803 // -- Partition topology across ranks of comm
804 std::vector<std::int64_t> cells1;
805 std::vector<std::int64_t> original_idx1;
806 std::vector<int> ghost_owners;
807 if (partitioner)
808 {
809 spdlog::info("Using partitioner with {} cell data", cells.size());
811 if (commt != MPI_COMM_NULL)
812 {
813 int size = dolfinx::MPI::size(comm);
814 auto t = extract_topology(element.cell_shape(), doflayout, cells);
815 dest = partitioner(commt, size, {celltype}, {t});
816 }
817
818 // Distribute cells (topology, includes higher-order 'nodes') to
819 // destination rank
820 assert(cells.size() % num_cell_nodes == 0);
821 std::size_t num_cells = cells.size() / num_cell_nodes;
822 std::vector<int> src_ranks;
823 std::tie(cells1, src_ranks, original_idx1, ghost_owners)
824 = graph::build::distribute(comm, cells, {num_cells, num_cell_nodes},
825 dest);
826 spdlog::debug("Got {} cells from distribution", cells1.size());
827 }
828 else
829 {
830 cells1 = std::vector<std::int64_t>(cells.begin(), cells.end());
831 assert(cells1.size() % num_cell_nodes == 0);
832 std::int64_t offset = 0;
833 std::int64_t num_owned = cells1.size() / num_cell_nodes;
834 MPI_Exscan(&num_owned, &offset, 1, MPI_INT64_T, MPI_SUM, comm);
835 original_idx1.resize(num_owned);
836 std::iota(original_idx1.begin(), original_idx1.end(), offset);
837 }
838
839 // Extract cell 'topology', i.e. extract the vertices for each cell
840 // and discard any 'higher-order' nodes
841 std::vector<std::int64_t> cells1_v
842 = extract_topology(celltype, doflayout, cells1);
843 spdlog::info("Extract basic topology: {}->{}", cells1.size(),
844 cells1_v.size());
845
846 // Build local dual graph for owned cells to (i) get list of vertices
847 // on the process boundary and (ii) apply re-ordering to cells for
848 // locality
849 std::vector<std::int64_t> boundary_v;
850 {
851 std::int32_t num_owned_cells
852 = cells1_v.size() / num_cell_vertices - ghost_owners.size();
853 std::vector<std::int32_t> cell_offsets(num_owned_cells + 1, 0);
854 for (std::size_t i = 1; i < cell_offsets.size(); ++i)
855 cell_offsets[i] = cell_offsets[i - 1] + num_cell_vertices;
856 spdlog::info("Build local dual graph");
857 auto [graph, unmatched_facets, max_v, facet_attached_cells]
859 std::vector{celltype},
860 {std::span(cells1_v.data(), num_owned_cells * num_cell_vertices)});
861 const std::vector<int> remap = graph::reorder_gps(graph);
862
863 // Create re-ordered cell lists (leaves ghosts unchanged)
864 std::vector<std::int64_t> _original_idx(original_idx1.size());
865 for (std::size_t i = 0; i < remap.size(); ++i)
866 _original_idx[remap[i]] = original_idx1[i];
867 std::copy_n(std::next(original_idx1.cbegin(), num_owned_cells),
868 ghost_owners.size(),
869 std::next(_original_idx.begin(), num_owned_cells));
870 impl::reorder_list(
871 std::span(cells1_v.data(), remap.size() * num_cell_vertices), remap);
872 impl::reorder_list(std::span(cells1.data(), remap.size() * num_cell_nodes),
873 remap);
874 original_idx1 = _original_idx;
875
876 // Boundary vertices are marked as 'unknown'
877 boundary_v = unmatched_facets;
878 std::ranges::sort(boundary_v);
879 auto [unique_end, range_end] = std::ranges::unique(boundary_v);
880 boundary_v.erase(unique_end, range_end);
881
882 // Remove -1 if it occurs in boundary vertices (may occur in mixed
883 // topology)
884 if (!boundary_v.empty() > 0 and boundary_v[0] == -1)
885 boundary_v.erase(boundary_v.begin());
886 }
887
888 // Create Topology
889 Topology topology = create_topology(comm, cells1_v, original_idx1,
890 ghost_owners, celltype, boundary_v);
891
892 // Create connectivities required higher-order geometries for creating
893 // a Geometry object
894 for (int e = 1; e < topology.dim(); ++e)
895 if (doflayout.num_entity_dofs(e) > 0)
896 topology.create_entities(e);
897 if (element.needs_dof_permutations())
899
900 // Build list of unique (global) node indices from cells1 and
901 // distribute coordinate data
902 std::vector<std::int64_t> nodes1 = cells1;
903 dolfinx::radix_sort(nodes1);
904 auto [unique_end, range_end] = std::ranges::unique(nodes1);
905 nodes1.erase(unique_end, range_end);
906
907 std::vector coords
908 = dolfinx::MPI::distribute_data(comm, nodes1, commg, x, xshape[1]);
909
910 // Create geometry object
911 Geometry geometry
912 = create_geometry(topology, element, nodes1, cells1, coords, xshape[1]);
913
914 return Mesh(comm, std::make_shared<Topology>(std::move(topology)),
915 std::move(geometry));
916}
917
952template <typename U>
954 MPI_Comm comm, MPI_Comm commt,
955 const std::vector<std::span<const std::int64_t>>& cells,
956 const std::vector<fem::CoordinateElement<
957 typename std::remove_reference_t<typename U::value_type>>>& elements,
958 MPI_Comm commg, const U& x, std::array<std::size_t, 2> xshape,
959 const CellPartitionFunction& partitioner)
960{
961 assert(cells.size() == elements.size());
962 std::int32_t num_cell_types = cells.size();
963 std::vector<CellType> celltypes;
964 std::ranges::transform(elements, std::back_inserter(celltypes),
965 [](auto e) { return e.cell_shape(); });
966 std::vector<fem::ElementDofLayout> doflayouts;
967 std::ranges::transform(elements, std::back_inserter(doflayouts),
968 [](auto e) { return e.create_dof_layout(); });
969
970 // Note: `extract_topology` extracts topology data, i.e. just the
971 // vertices. For P1 geometry this should just be the identity
972 // operator. For other elements the filtered lists may have 'gaps',
973 // i.e. the indices might not be contiguous.
974 //
975 // `extract_topology` could be skipped for 'P1 geometry' elements
976
977 // -- Partition topology across ranks of comm
978 std::vector<std::vector<std::int64_t>> cells1(num_cell_types);
979 std::vector<std::vector<std::int64_t>> original_idx1(num_cell_types);
980 std::vector<std::vector<int>> ghost_owners(num_cell_types);
981 if (partitioner)
982 {
983 spdlog::info("Using partitioner with cell data ({} cell types)",
984 num_cell_types);
986 if (commt != MPI_COMM_NULL)
987 {
988 int size = dolfinx::MPI::size(comm);
989 std::vector<std::vector<std::int64_t>> t(num_cell_types);
990 std::vector<std::span<const std::int64_t>> tspan(num_cell_types);
991 for (std::int32_t i = 0; i < num_cell_types; ++i)
992 {
993 t[i] = extract_topology(celltypes[i], doflayouts[i], cells[i]);
994 tspan[i] = std::span(t[i]);
995 }
996 dest = partitioner(commt, size, celltypes, tspan);
997 }
998
999 std::int32_t cell_offset = 0;
1000 for (std::int32_t i = 0; i < num_cell_types; ++i)
1001 {
1002 std::size_t num_cell_nodes = doflayouts[i].num_dofs();
1003 assert(cells[i].size() % num_cell_nodes == 0);
1004 std::size_t num_cells = cells[i].size() / num_cell_nodes;
1005
1006 // Extract destination AdjacencyList for this cell type
1007 std::vector<std::int32_t> offsets_i(
1008 std::next(dest.offsets().begin(), cell_offset),
1009 std::next(dest.offsets().begin(), cell_offset + num_cells + 1));
1010 std::vector<std::int32_t> data_i(
1011 std::next(dest.array().begin(), offsets_i.front()),
1012 std::next(dest.array().begin(), offsets_i.back()));
1013 std::int32_t offset_0 = offsets_i.front();
1014 std::ranges::for_each(offsets_i,
1015 [&offset_0](std::int32_t& j) { j -= offset_0; });
1016 graph::AdjacencyList<std::int32_t> dest_i(data_i, offsets_i);
1017 cell_offset += num_cells;
1018
1019 // Distribute cells (topology, includes higher-order 'nodes') to
1020 // destination rank
1021 std::vector<int> src_ranks;
1022 std::tie(cells1[i], src_ranks, original_idx1[i], ghost_owners[i])
1023 = graph::build::distribute(comm, cells[i],
1024 {num_cells, num_cell_nodes}, dest_i);
1025 spdlog::debug("Got {} cells from distribution", cells1[i].size());
1026 }
1027 }
1028 else
1029 {
1030 // No partitioning, construct a global index
1031 std::int64_t num_owned = 0;
1032 for (std::int32_t i = 0; i < num_cell_types; ++i)
1033 {
1034 cells1[i] = std::vector<std::int64_t>(cells[i].begin(), cells[i].end());
1035 std::int32_t num_cell_nodes = doflayouts[i].num_dofs();
1036 assert(cells1[i].size() % num_cell_nodes == 0);
1037 original_idx1[i].resize(cells1[i].size() / num_cell_nodes);
1038 num_owned += original_idx1[i].size();
1039 }
1040 // Add on global offset
1041 std::int64_t global_offset = 0;
1042 MPI_Exscan(&num_owned, &global_offset, 1, MPI_INT64_T, MPI_SUM, comm);
1043 for (std::int32_t i = 0; i < num_cell_types; ++i)
1044 {
1045 std::iota(original_idx1[i].begin(), original_idx1[i].end(),
1046 global_offset);
1047 global_offset += original_idx1[i].size();
1048 }
1049 }
1050
1051 // Extract cell 'topology', i.e. extract the vertices for each cell
1052 // and discard any 'higher-order' nodes
1053 std::vector<std::vector<std::int64_t>> cells1_v(num_cell_types);
1054 for (std::int32_t i = 0; i < num_cell_types; ++i)
1055 {
1056 cells1_v[i] = extract_topology(celltypes[i], doflayouts[i], cells1[i]);
1057 spdlog::info("Extract basic topology: {}->{}", cells1[i].size(),
1058 cells1_v[i].size());
1059 }
1060
1061 // Build local dual graph for owned cells to (i) get list of vertices
1062 // on the process boundary and (ii) apply re-ordering to cells for
1063 // locality
1064 std::vector<std::int64_t> boundary_v;
1065 {
1066 std::vector<std::span<const std::int64_t>> cells1_v_local_cells;
1067
1068 for (std::int32_t i = 0; i < num_cell_types; ++i)
1069 {
1070 std::int32_t num_cell_vertices = mesh::num_cell_vertices(celltypes[i]);
1071 std::int32_t num_owned_cells
1072 = cells1_v[i].size() / num_cell_vertices - ghost_owners[i].size();
1073 cells1_v_local_cells.push_back(
1074 std::span(cells1_v[i].data(), num_owned_cells * num_cell_vertices));
1075 }
1076 spdlog::info("Build local dual graph");
1077 auto [graph, unmatched_facets, max_v, facet_attached_cells]
1078 = build_local_dual_graph(celltypes, cells1_v_local_cells);
1079
1080 // TODO: in the original create_mesh(), cell reordering is done here.
1081 // This needs reworking for mixed cell topology
1082
1083 // Boundary vertices are marked as 'unknown'
1084 boundary_v = unmatched_facets;
1085 std::ranges::sort(boundary_v);
1086 auto [unique_end, range_end] = std::ranges::unique(boundary_v);
1087 boundary_v.erase(unique_end, range_end);
1088
1089 // Remove -1 if it occurs in boundary vertices (may occur in mixed
1090 // topology)
1091 if (!boundary_v.empty() > 0 and boundary_v[0] == -1)
1092 boundary_v.erase(boundary_v.begin());
1093 }
1094
1095 spdlog::debug("Got {} boundary vertices", boundary_v.size());
1096
1097 // Create Topology
1098
1099 std::vector<std::span<const std::int64_t>> cells1_v_span;
1100 std::ranges::transform(cells1_v, std::back_inserter(cells1_v_span),
1101 [](auto& c) { return std::span(c); });
1102 std::vector<std::span<const std::int64_t>> original_idx1_span;
1103 std::ranges::transform(original_idx1, std::back_inserter(original_idx1_span),
1104 [](auto& c) { return std::span(c); });
1105 std::vector<std::span<const int>> ghost_owners_span;
1106 std::ranges::transform(ghost_owners, std::back_inserter(ghost_owners_span),
1107 [](auto& c) { return std::span(c); });
1108
1109 Topology topology
1110 = create_topology(comm, celltypes, cells1_v_span, original_idx1_span,
1111 ghost_owners_span, boundary_v);
1112
1113 // Create connectivities required higher-order geometries for creating
1114 // a Geometry object
1115 for (int i = 0; i < num_cell_types; ++i)
1116 {
1117 for (int e = 1; e < topology.dim(); ++e)
1118 if (doflayouts[i].num_entity_dofs(e) > 0)
1119 topology.create_entities(e);
1120 if (elements[i].needs_dof_permutations())
1121 topology.create_entity_permutations();
1122 }
1123
1124 // Build list of unique (global) node indices from cells1 and
1125 // distribute coordinate data
1126 std::vector<std::int64_t> nodes1, nodes2;
1127 for (std::vector<std::int64_t>& c : cells1)
1128 nodes1.insert(nodes1.end(), c.begin(), c.end());
1129 for (std::vector<std::int64_t>& c : cells1)
1130 nodes2.insert(nodes2.end(), c.begin(), c.end());
1131
1132 dolfinx::radix_sort(nodes1);
1133 auto [unique_end, range_end] = std::ranges::unique(nodes1);
1134 nodes1.erase(unique_end, range_end);
1135
1136 std::vector coords
1137 = dolfinx::MPI::distribute_data(comm, nodes1, commg, x, xshape[1]);
1138
1139 // Create geometry object
1140 Geometry geometry
1141 = create_geometry(topology, elements, nodes1, nodes2, coords, xshape[1]);
1142
1143 return Mesh(comm, std::make_shared<Topology>(std::move(topology)),
1144 std::move(geometry));
1145}
1146
1165template <typename U>
1166Mesh<typename std::remove_reference_t<typename U::value_type>>
1167create_mesh(MPI_Comm comm, std::span<const std::int64_t> cells,
1169 std::remove_reference_t<typename U::value_type>>& elements,
1170 const U& x, std::array<std::size_t, 2> xshape, GhostMode ghost_mode)
1171{
1172 if (dolfinx::MPI::size(comm) == 1)
1173 return create_mesh(comm, comm, cells, elements, comm, x, xshape, nullptr);
1174 else
1175 {
1176 return create_mesh(comm, comm, cells, elements, comm, x, xshape,
1177 create_cell_partitioner(ghost_mode));
1178 }
1179}
1180
1192template <std::floating_point T>
1193std::pair<Geometry<T>, std::vector<int32_t>>
1194create_subgeometry(const Mesh<T>& mesh, int dim,
1195 std::span<const std::int32_t> subentity_to_entity)
1196{
1197 const Geometry<T>& geometry = mesh.geometry();
1198
1199 // Get the geometry dofs in the sub-geometry based on the entities in
1200 // sub-geometry
1201 const fem::ElementDofLayout layout = geometry.cmap().create_dof_layout();
1202
1203 std::vector<std::int32_t> x_indices
1204 = entities_to_geometry(mesh, dim, subentity_to_entity, true);
1205
1206 std::vector<std::int32_t> sub_x_dofs = x_indices;
1207 std::ranges::sort(sub_x_dofs);
1208 auto [unique_end, range_end] = std::ranges::unique(sub_x_dofs);
1209 sub_x_dofs.erase(unique_end, range_end);
1210
1211 // Get the sub-geometry dofs owned by this process
1212 auto x_index_map = geometry.index_map();
1213 assert(x_index_map);
1214
1215 std::shared_ptr<common::IndexMap> sub_x_dof_index_map;
1216 std::vector<std::int32_t> subx_to_x_dofmap;
1217 {
1218 auto [map, new_to_old] = common::create_sub_index_map(
1219 *x_index_map, sub_x_dofs, common::IndexMapOrder::any, true);
1220 sub_x_dof_index_map = std::make_shared<common::IndexMap>(std::move(map));
1221 subx_to_x_dofmap = std::move(new_to_old);
1222 }
1223
1224 // Create sub-geometry coordinates
1225 std::span<const T> x = geometry.x();
1226 std::int32_t sub_num_x_dofs = subx_to_x_dofmap.size();
1227 std::vector<T> sub_x(3 * sub_num_x_dofs);
1228 for (int i = 0; i < sub_num_x_dofs; ++i)
1229 {
1230 std::copy_n(std::next(x.begin(), 3 * subx_to_x_dofmap[i]), 3,
1231 std::next(sub_x.begin(), 3 * i));
1232 }
1233
1234 // Create geometry to sub-geometry map
1235 std::vector<std::int32_t> x_to_subx_dof_map(
1236 x_index_map->size_local() + x_index_map->num_ghosts(), -1);
1237 for (std::size_t i = 0; i < subx_to_x_dofmap.size(); ++i)
1238 x_to_subx_dof_map[subx_to_x_dofmap[i]] = i;
1239
1240 // Create sub-geometry dofmap
1241 std::vector<std::int32_t> sub_x_dofmap;
1242 sub_x_dofmap.reserve(x_indices.size());
1243 std::ranges::transform(x_indices, std::back_inserter(sub_x_dofmap),
1244 [&x_to_subx_dof_map](auto x_dof)
1245 {
1246 assert(x_to_subx_dof_map[x_dof] != -1);
1247 return x_to_subx_dof_map[x_dof];
1248 });
1249
1250 // Create sub-geometry coordinate element
1251 CellType sub_coord_cell
1252 = cell_entity_type(geometry.cmap().cell_shape(), dim, 0);
1253 // Special handling if point meshes, as they only support constant basis
1254 // functions
1255 int degree = geometry.cmap().degree();
1256 if (sub_coord_cell == CellType::point)
1257 degree = 0;
1258 fem::CoordinateElement<T> sub_cmap(sub_coord_cell, degree,
1259 geometry.cmap().variant());
1260
1261 // Sub-geometry input_global_indices
1262 const std::vector<std::int64_t>& igi = geometry.input_global_indices();
1263 std::vector<std::int64_t> sub_igi;
1264 sub_igi.reserve(subx_to_x_dofmap.size());
1265 std::ranges::transform(subx_to_x_dofmap, std::back_inserter(sub_igi),
1266 [&igi](auto sub_x_dof) { return igi[sub_x_dof]; });
1267
1268 // Create geometry
1269 return {Geometry(sub_x_dof_index_map, std::move(sub_x_dofmap), {sub_cmap},
1270 std::move(sub_x), geometry.dim(), std::move(sub_igi)),
1271 std::move(subx_to_x_dofmap)};
1272}
1273
1282template <std::floating_point T>
1283std::tuple<Mesh<T>, std::vector<std::int32_t>, std::vector<std::int32_t>,
1284 std::vector<std::int32_t>>
1285create_submesh(const Mesh<T>& mesh, int dim,
1286 std::span<const std::int32_t> entities)
1287{
1288 // Create sub-topology
1289 mesh.topology_mutable()->create_connectivity(dim, 0);
1290 auto [topology, subentity_to_entity, subvertex_to_vertex]
1291 = mesh::create_subtopology(*mesh.topology(), dim, entities);
1292
1293 // Create sub-geometry
1294 const int tdim = mesh.topology()->dim();
1295 mesh.topology_mutable()->create_entities(dim);
1296 mesh.topology_mutable()->create_connectivity(dim, tdim);
1297 mesh.topology_mutable()->create_connectivity(tdim, dim);
1298 mesh.topology_mutable()->create_entity_permutations();
1299 auto [geometry, subx_to_x_dofmap]
1300 = mesh::create_subgeometry(mesh, dim, subentity_to_entity);
1301
1302 return {Mesh(mesh.comm(), std::make_shared<Topology>(std::move(topology)),
1303 std::move(geometry)),
1304 std::move(subentity_to_entity), std::move(subvertex_to_vertex),
1305 std::move(subx_to_x_dofmap)};
1306}
1307
1308} // namespace dolfinx::mesh
Definition XDMFFile.h:27
ElementDofLayout create_dof_layout() const
Compute and return the dof layout.
Definition CoordinateElement.cpp:75
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:64
Definition ElementDofLayout.h:30
int num_entity_dofs(int dim) const
Definition ElementDofLayout.cpp:63
const std::vector< std::vector< std::vector< int > > > & entity_closure_dofs_all() const
Definition ElementDofLayout.cpp:92
int num_entity_closure_dofs(int dim) const
Definition ElementDofLayout.cpp:68
int num_dofs() const
Definition ElementDofLayout.cpp:61
Definition topologycomputation.h:24
const std::vector< T > & array() const
Return contiguous array of links for all nodes (const version)
Definition AdjacencyList.h:128
const std::vector< std::int32_t > & offsets() const
Offset for each node in array() (const version)
Definition AdjacencyList.h:134
Geometry stores the geometry imposed on a mesh.
Definition Geometry.h:34
std::shared_ptr< const common::IndexMap > index_map() const
Index map.
Definition Geometry.h:160
const fem::CoordinateElement< value_type > & cmap() const
The element that describes the geometry map.
Definition Geometry.h:181
int dim() const
Return dimension of the Euclidean coordinate system.
Definition Geometry.h:117
MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< const std::int32_t, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents< std::size_t, 2 > > dofmap() const
DofMap for the geometry.
Definition Geometry.h:124
const std::vector< std::int64_t > & input_global_indices() const
Global user indices.
Definition Geometry.h:202
std::span< const value_type > x() const
Access geometry degrees-of-freedom data (const version).
Definition Geometry.h:169
A Mesh consists of a set of connected and numbered mesh topological entities, and geometry data.
Definition Mesh.h:23
Topology stores the topology of a mesh, consisting of mesh entities and connectivity (incidence relat...
Definition Topology.h:44
void create_entity_permutations()
Compute entity permutations and reflections.
Definition Topology.cpp:910
std::int32_t create_entities(int dim)
Create entities of given topological dimension.
Definition Topology.cpp:831
int dim() const noexcept
Return the topological dimension of the mesh.
Definition Topology.cpp:794
Requirements on function for geometry marking.
Definition utils.h:461
void reorder_list(std::span< T > list, std::span< const std::int32_t > nodemap)
Re-order an adjacency list of fixed degree.
Definition utils.h:46
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)
The coordinates of 'vertices' for for entities of a give dimension that are attached to specified fac...
Definition utils.h:77
std::pair< std::vector< T >, std::array< std::size_t, 2 > > compute_vertex_coords(const mesh::Mesh< T > &mesh)
Definition utils.h:419
std::vector< typename std::remove_reference_t< typename U::value_type > > distribute_data(MPI_Comm comm0, std::span< const std::int64_t > indices, MPI_Comm comm1, const U &x, int shape1)
Distribute rows of a rectangular data array to ranks where they are required (scalable version).
Definition MPI.h:676
int size(MPI_Comm comm)
Definition MPI.cpp:72
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:814
@ any
Allow arbitrary ordering of ghost indices in sub-maps.
Finite element method functionality.
Definition assemble_matrix_impl.h:26
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:38
std::vector< std::int32_t > reorder_gps(const graph::AdjacencyList< std::int32_t > &graph)
Re-order a graph using the Gibbs-Poole-Stockmeyer algorithm.
Definition ordering.cpp:360
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
AdjacencyList< std::int32_t > partition_graph(MPI_Comm comm, int nparts, const AdjacencyList< std::int64_t > &local_graph, bool ghosting)
Partition graph across processes using the default graph partitioner.
Definition partition.cpp:21
Mesh data structures and algorithms on meshes.
Definition DofMap.h:32
Topology create_topology(MPI_Comm comm, std::span< const std::int64_t > cells, std::span< const std::int64_t > original_cell_index, std::span< const int > ghost_owners, CellType cell_type, std::span< const std::int64_t > boundary_vertices)
Create a mesh topology.
Definition Topology.cpp:1319
GhostMode
Enum for different partitioning ghost modes.
Definition utils.h:36
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, std::function< std::vector< int >(const graph::AdjacencyList< std::int32_t > &)> reorder_fn=nullptr)
Build Geometry from input data.
Definition Geometry.h:264
Mesh< typename std::remove_reference_t< typename U::value_type > > create_mesh(MPI_Comm comm, MPI_Comm commt, std::span< const std::int64_t > cells, const fem::CoordinateElement< typename std::remove_reference_t< typename U::value_type > > &element, MPI_Comm commg, const U &x, std::array< std::size_t, 2 > xshape, const CellPartitionFunction &partitioner)
Create a distributed mesh from mesh data using a provided graph partitioning function for determining...
Definition utils.h:783
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:265
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.cpp:64
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:1331
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:555
int num_cell_vertices(CellType type)
Definition cell_types.cpp:147
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:211
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. A sub-geometry is sim...
Definition utils.h:1194
std::vector< std::int32_t > entities_to_geometry(const Mesh< T > &mesh, int dim, std::span< const std::int32_t > entities, bool permute=false)
Compute the geometry degrees of freedom associated with the closure of a given set of cell entities.
Definition utils.h:633
std::tuple< graph::AdjacencyList< std::int32_t >, std::vector< std::int64_t >, std::size_t, std::vector< std::int32_t > > build_local_dual_graph(std::span< const CellType > celltypes, const std::vector< std::span< const std::int64_t > > &cells)
Compute the local part of the dual graph (cell-cell connections via facets) and facets with only one ...
Definition graphbuild.cpp:354
std::vector< std::int32_t > locate_entities(const Mesh< T > &mesh, int dim, U marker)
Compute indices of all mesh entities that evaluate to true for the provided geometric marking functio...
Definition utils.h:482
std::vector< std::int32_t > exterior_facet_indices(const Topology &topology)
Compute the indices of all exterior facets that are owned by the caller.
Definition utils.cpp:58
std::vector< std::int32_t > compute_incident_entities(const Topology &topology, std::span< const std::int32_t > entities, int d0, int d1)
Compute incident indices.
Definition utils.cpp:108
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:29
CellType
Cell type identifier.
Definition cell_types.h:22
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. The function should compute the destination rank for ce...
Definition utils.h:185
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:381
std::tuple< Mesh< T >, std::vector< std::int32_t >, std::vector< std::int32_t >, 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:1285
CellPartitionFunction create_cell_partitioner(mesh::GhostMode ghost_mode=mesh::GhostMode::none, const graph::partition_fn &partfn=&graph::partition_graph)
Definition utils.cpp:85
constexpr __radix_sort radix_sort
Radix sort.
Definition sort.h:124