Note: this is documentation for an old release. View the latest documentation at docs.fenicsproject.org/dolfinx/v0.9.0/cpp/doxygen/d0/d47/mesh_2utils_8h_source.html
DOLFINx 0.7.3
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 <basix/mdspan.hpp>
13#include <concepts>
14#include <dolfinx/graph/AdjacencyList.h>
15#include <dolfinx/graph/ordering.h>
16#include <dolfinx/graph/partition.h>
17#include <functional>
18#include <mpi.h>
19#include <span>
20
23
24namespace dolfinx::fem
25{
26class ElementDofLayout;
27}
28
29namespace dolfinx::mesh
30{
31enum class CellType;
32
34enum class GhostMode : int
35{
36 none,
37 shared_facet,
38 shared_vertex
39};
40
41namespace
42{
44template <typename T>
46 std::span<const std::int32_t> nodemap)
47{
48 // Copy existing data to keep ghost values (not reordered)
49 std::vector<T> data(list.array());
50 std::vector<std::int32_t> offsets(list.offsets().size());
51
52 // Compute new offsets (owned and ghost)
53 offsets[0] = 0;
54 for (std::size_t n = 0; n < nodemap.size(); ++n)
55 offsets[nodemap[n] + 1] = list.num_links(n);
56 for (std::size_t n = nodemap.size(); n < (std::size_t)list.num_nodes(); ++n)
57 offsets[n + 1] = list.num_links(n);
58 std::partial_sum(offsets.begin(), offsets.end(), offsets.begin());
59 graph::AdjacencyList<T> list_new(std::move(data), std::move(offsets));
60
61 for (std::size_t n = 0; n < nodemap.size(); ++n)
62 {
63 auto links_old = list.links(n);
64 auto links_new = list_new.links(nodemap[n]);
65 assert(links_old.size() == links_new.size());
66 std::copy(links_old.begin(), links_old.end(), links_new.begin());
67 }
68
69 return list_new;
70}
71} // namespace
72
73namespace impl
74{
88template <std::floating_point T>
89std::tuple<std::vector<std::int32_t>, std::vector<T>, std::vector<std::int32_t>>
91 std::span<const std::int32_t> facets)
92{
93 auto topology = mesh.topology();
94 assert(topology);
95 const int tdim = topology->dim();
96 if (dim == tdim)
97 {
98 throw std::runtime_error(
99 "Cannot use mesh::locate_entities_boundary (boundary) for cells.");
100 }
101
102 // Build set of vertices on boundary and set of boundary entities
103 mesh.topology_mutable()->create_connectivity(tdim - 1, 0);
104 mesh.topology_mutable()->create_connectivity(tdim - 1, dim);
105 std::vector<std::int32_t> vertices, entities;
106 {
107 auto f_to_v = topology->connectivity(tdim - 1, 0);
108 assert(f_to_v);
109 auto f_to_e = topology->connectivity(tdim - 1, dim);
110 assert(f_to_e);
111 for (auto f : facets)
112 {
113 auto v = f_to_v->links(f);
114 vertices.insert(vertices.end(), v.begin(), v.end());
115 auto e = f_to_e->links(f);
116 entities.insert(entities.end(), e.begin(), e.end());
117 }
118
119 // Build vector of boundary vertices
120 std::sort(vertices.begin(), vertices.end());
121 vertices.erase(std::unique(vertices.begin(), vertices.end()),
122 vertices.end());
123 std::sort(entities.begin(), entities.end());
124 entities.erase(std::unique(entities.begin(), entities.end()),
125 entities.end());
126 }
127
128 // Get geometry data
129 auto x_dofmap = mesh.geometry().dofmap();
130 std::span<const T> x_nodes = mesh.geometry().x();
131
132 // Get all vertex 'node' indices
133 mesh.topology_mutable()->create_connectivity(0, tdim);
134 mesh.topology_mutable()->create_connectivity(tdim, 0);
135 auto v_to_c = topology->connectivity(0, tdim);
136 assert(v_to_c);
137 auto c_to_v = topology->connectivity(tdim, 0);
138 assert(c_to_v);
139 std::vector<T> x_vertices(3 * vertices.size(), -1.0);
140 std::vector<std::int32_t> vertex_to_pos(v_to_c->num_nodes(), -1);
141 for (std::size_t i = 0; i < vertices.size(); ++i)
142 {
143 const std::int32_t v = vertices[i];
144
145 // Get first cell and find position
146 const int c = v_to_c->links(v).front();
147 auto cell_vertices = c_to_v->links(c);
148 auto it = std::find(cell_vertices.begin(), cell_vertices.end(), v);
149 assert(it != cell_vertices.end());
150 const int local_pos = std::distance(cell_vertices.begin(), it);
151
152 auto dofs = MDSPAN_IMPL_STANDARD_NAMESPACE::MDSPAN_IMPL_PROPOSED_NAMESPACE::
153 submdspan(x_dofmap, c, MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent);
154 for (std::size_t j = 0; j < 3; ++j)
155 x_vertices[j * vertices.size() + i] = x_nodes[3 * dofs[local_pos] + j];
156 vertex_to_pos[v] = i;
157 }
158
159 return {std::move(entities), std::move(x_vertices), std::move(vertex_to_pos)};
160}
161
162} // namespace impl
163
175std::vector<std::int32_t> exterior_facet_indices(const Topology& topology);
176
193using CellPartitionFunction = std::function<graph::AdjacencyList<std::int32_t>(
194 MPI_Comm comm, int nparts, int tdim,
196
207extract_topology(const CellType& cell_type, const fem::ElementDofLayout& layout,
209
218template <std::floating_point T>
219std::vector<T> h(const Mesh<T>& mesh, std::span<const std::int32_t> entities,
220 int dim)
221{
222 if (entities.empty())
223 return std::vector<T>();
224 if (dim == 0)
225 return std::vector<T>(entities.size(), 0);
226
227 // Get the geometry dofs for the vertices of each entity
228 const std::vector<std::int32_t> vertex_xdofs
229 = entities_to_geometry(mesh, dim, entities, false);
230 assert(!entities.empty());
231 const std::size_t num_vertices = vertex_xdofs.size() / entities.size();
232
233 // Get the geometry coordinate
234 std::span<const T> x = mesh.geometry().x();
235
236 // Function to compute the length of (p0 - p1)
237 auto delta_norm = [](const auto& p0, const auto& p1)
238 {
239 T norm = 0;
240 for (std::size_t i = 0; i < 3; ++i)
241 norm += (p0[i] - p1[i]) * (p0[i] - p1[i]);
242 return std::sqrt(norm);
243 };
244
245 // Compute greatest distance between any to vertices
246 assert(dim > 0);
247 std::vector<T> h(entities.size(), 0);
248 for (std::size_t e = 0; e < entities.size(); ++e)
249 {
250 // Get geometry 'dof' for each vertex of entity e
251 std::span<const std::int32_t> e_vertices(
252 vertex_xdofs.data() + e * num_vertices, num_vertices);
253
254 // Compute maximum distance between any two vertices
255 for (std::size_t i = 0; i < e_vertices.size(); ++i)
256 {
257 std::span<const T, 3> p0(x.data() + 3 * e_vertices[i], 3);
258 for (std::size_t j = i + 1; j < e_vertices.size(); ++j)
259 {
260 std::span<const T, 3> p1(x.data() + 3 * e_vertices[j], 3);
261 h[e] = std::max(h[e], delta_norm(p0, p1));
262 }
263 }
264 }
265
266 return h;
267}
268
272template <std::floating_point T>
273std::vector<T> cell_normals(const Mesh<T>& mesh, int dim,
274 std::span<const std::int32_t> entities)
275{
276 auto topology = mesh.topology();
277 assert(topology);
278
279 if (entities.empty())
280 return std::vector<T>();
281
282 if (topology->cell_types().size() > 1)
283 throw std::runtime_error("Mixed topology not supported");
284 if (topology->cell_types()[0] == CellType::prism and dim == 2)
285 throw std::runtime_error("More work needed for prism cell");
286
287 const int gdim = mesh.geometry().dim();
288 const CellType type = cell_entity_type(topology->cell_types()[0], dim, 0);
289
290 // Find geometry nodes for topology entities
291 std::span<const T> x = mesh.geometry().x();
292
293 // Orient cells if they are tetrahedron
294 bool orient = false;
295 if (topology->cell_types()[0] == CellType::tetrahedron)
296 orient = true;
297
298 std::vector<std::int32_t> geometry_entities
299 = entities_to_geometry(mesh, dim, entities, orient);
300
301 const std::size_t shape1 = geometry_entities.size() / entities.size();
302 std::vector<T> n(entities.size() * 3);
303 switch (type)
304 {
305 case CellType::interval:
306 {
307 if (gdim > 2)
308 throw std::invalid_argument("Interval cell normal undefined in 3D");
309 for (std::size_t i = 0; i < entities.size(); ++i)
310 {
311 // Get the two vertices as points
312 std::array vertices{geometry_entities[i * shape1],
313 geometry_entities[i * shape1 + 1]};
314 std::array p = {std::span<const T, 3>(x.data() + 3 * vertices[0], 3),
315 std::span<const T, 3>(x.data() + 3 * vertices[1], 3)};
316
317 // Define normal by rotating tangent counter-clockwise
318 std::array<T, 3> t;
319 std::transform(p[1].begin(), p[1].end(), p[0].begin(), t.begin(),
320 [](auto x, auto y) { return x - y; });
321
322 T norm = std::sqrt(t[0] * t[0] + t[1] * t[1]);
323 std::span<T, 3> ni(n.data() + 3 * i, 3);
324 ni[0] = -t[1] / norm;
325 ni[1] = t[0] / norm;
326 ni[2] = 0.0;
327 }
328 return n;
329 }
330 case CellType::triangle:
331 {
332 for (std::size_t i = 0; i < entities.size(); ++i)
333 {
334 // Get the three vertices as points
335 std::array vertices = {geometry_entities[i * shape1 + 0],
336 geometry_entities[i * shape1 + 1],
337 geometry_entities[i * shape1 + 2]};
338 std::array p = {std::span<const T, 3>(x.data() + 3 * vertices[0], 3),
339 std::span<const T, 3>(x.data() + 3 * vertices[1], 3),
340 std::span<const T, 3>(x.data() + 3 * vertices[2], 3)};
341
342 // Compute (p1 - p0) and (p2 - p0)
343 std::array<T, 3> dp1, dp2;
344 std::transform(p[1].begin(), p[1].end(), p[0].begin(), dp1.begin(),
345 [](auto x, auto y) { return x - y; });
346 std::transform(p[2].begin(), p[2].end(), p[0].begin(), dp2.begin(),
347 [](auto x, auto y) { return x - y; });
348
349 // Define cell normal via cross product of first two edges
350 std::array<T, 3> ni = math::cross(dp1, dp2);
351 T norm = std::sqrt(ni[0] * ni[0] + ni[1] * ni[1] + ni[2] * ni[2]);
352 std::transform(ni.begin(), ni.end(), std::next(n.begin(), 3 * i),
353 [norm](auto x) { return x / norm; });
354 }
355
356 return n;
357 }
358 case CellType::quadrilateral:
359 {
360 // TODO: check
361 for (std::size_t i = 0; i < entities.size(); ++i)
362 {
363 // Get the three vertices as points
364 std::array vertices = {geometry_entities[i * shape1 + 0],
365 geometry_entities[i * shape1 + 1],
366 geometry_entities[i * shape1 + 2]};
367 std::array p = {std::span<const T, 3>(x.data() + 3 * vertices[0], 3),
368 std::span<const T, 3>(x.data() + 3 * vertices[1], 3),
369 std::span<const T, 3>(x.data() + 3 * vertices[2], 3)};
370
371 // Compute (p1 - p0) and (p2 - p0)
372 std::array<T, 3> dp1, dp2;
373 std::transform(p[1].begin(), p[1].end(), p[0].begin(), dp1.begin(),
374 [](auto x, auto y) { return x - y; });
375 std::transform(p[2].begin(), p[2].end(), p[0].begin(), dp2.begin(),
376 [](auto x, auto y) { return x - y; });
377
378 // Define cell normal via cross product of first two edges
379 std::array<T, 3> ni = math::cross(dp1, dp2);
380 T norm = std::sqrt(ni[0] * ni[0] + ni[1] * ni[1] + ni[2] * ni[2]);
381 std::transform(ni.begin(), ni.end(), std::next(n.begin(), 3 * i),
382 [norm](auto x) { return x / norm; });
383 }
384
385 return n;
386 }
387 default:
388 throw std::invalid_argument(
389 "cell_normal not supported for this cell type.");
390 }
391}
392
396template <std::floating_point T>
397std::vector<T> compute_midpoints(const Mesh<T>& mesh, int dim,
398 std::span<const std::int32_t> entities)
399{
400 if (entities.empty())
401 return std::vector<T>();
402
403 std::span<const T> x = mesh.geometry().x();
404
405 // Build map from entity -> geometry dof
406 // FIXME: This assumes a linear geometry.
407 const std::vector<std::int32_t> e_to_g
408 = entities_to_geometry(mesh, dim, entities, false);
409 std::size_t shape1 = e_to_g.size() / entities.size();
410
411 std::vector<T> x_mid(entities.size() * 3, 0);
412 for (std::size_t e = 0; e < entities.size(); ++e)
413 {
414 std::span<T, 3> p(x_mid.data() + 3 * e, 3);
415 std::span<const std::int32_t> rows(e_to_g.data() + e * shape1, shape1);
416 for (auto row : rows)
417 {
418 std::span<const T, 3> xg(x.data() + 3 * row, 3);
419 std::transform(p.begin(), p.end(), xg.begin(), p.begin(),
420 [size = rows.size()](auto x, auto y)
421 { return x + y / size; });
422 }
423 }
424
425 return x_mid;
426}
427
428namespace impl
429{
434template <std::floating_point T>
435std::pair<std::vector<T>, std::array<std::size_t, 2>>
437{
438 auto topology = mesh.topology();
439 assert(topology);
440 const int tdim = topology->dim();
441
442 // Create entities and connectivities
443 mesh.topology_mutable()->create_connectivity(tdim, 0);
444
445 // Get all vertex 'node' indices
446 auto x_dofmap = mesh.geometry().dofmap();
447 const std::int32_t num_vertices = topology->index_map(0)->size_local()
448 + topology->index_map(0)->num_ghosts();
449 auto c_to_v = topology->connectivity(tdim, 0);
450 assert(c_to_v);
451 std::vector<std::int32_t> vertex_to_node(num_vertices);
452 for (int c = 0; c < c_to_v->num_nodes(); ++c)
453 {
454 auto x_dofs
455 = MDSPAN_IMPL_STANDARD_NAMESPACE::MDSPAN_IMPL_PROPOSED_NAMESPACE::
456 submdspan(x_dofmap, c, MDSPAN_IMPL_STANDARD_NAMESPACE::full_extent);
457 auto vertices = c_to_v->links(c);
458 for (std::size_t i = 0; i < vertices.size(); ++i)
459 vertex_to_node[vertices[i]] = x_dofs[i];
460 }
461
462 // Pack coordinates of vertices
463 std::span<const T> x_nodes = mesh.geometry().x();
464 std::vector<T> x_vertices(3 * vertex_to_node.size(), 0.0);
465 for (std::size_t i = 0; i < vertex_to_node.size(); ++i)
466 {
467 const int pos = 3 * vertex_to_node[i];
468 for (std::size_t j = 0; j < 3; ++j)
469 x_vertices[j * vertex_to_node.size() + i] = x_nodes[pos + j];
470 }
471
472 return {std::move(x_vertices), {3, vertex_to_node.size()}};
473}
474
475} // namespace impl
476
478template <typename Fn, typename T>
479concept MarkerFn = std::is_invocable_r<
480 std::vector<std::int8_t>, Fn,
481 MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan<
482 const T, MDSPAN_IMPL_STANDARD_NAMESPACE::extents<
483 std::size_t, 3,
484 MDSPAN_IMPL_STANDARD_NAMESPACE::dynamic_extent>>>::value;
485
499template <std::floating_point T, MarkerFn<T> U>
500std::vector<std::int32_t> locate_entities(const Mesh<T>& mesh, int dim,
501 U marker)
502{
503 using cmdspan3x_t = MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan<
504 const T,
505 MDSPAN_IMPL_STANDARD_NAMESPACE::extents<
506 std::size_t, 3, MDSPAN_IMPL_STANDARD_NAMESPACE::dynamic_extent>>;
507
508 // Run marker function on vertex coordinates
509 const auto [xdata, xshape] = impl::compute_vertex_coords(mesh);
510 cmdspan3x_t x(xdata.data(), xshape);
511 const std::vector<std::int8_t> marked = marker(x);
512 if (marked.size() != x.extent(1))
513 throw std::runtime_error("Length of array of markers is wrong.");
514
515 auto topology = mesh.topology();
516 assert(topology);
517 const int tdim = topology->dim();
518
519 mesh.topology_mutable()->create_entities(dim);
520 mesh.topology_mutable()->create_connectivity(tdim, 0);
521 if (dim < tdim)
522 mesh.topology_mutable()->create_connectivity(dim, 0);
523
524 // Iterate over entities of dimension 'dim' to build vector of marked
525 // entities
526 auto e_to_v = topology->connectivity(dim, 0);
527 assert(e_to_v);
528 std::vector<std::int32_t> entities;
529 for (int e = 0; e < e_to_v->num_nodes(); ++e)
530 {
531 // Iterate over entity vertices
532 bool all_vertices_marked = true;
533 for (std::int32_t v : e_to_v->links(e))
534 {
535 if (!marked[v])
536 {
537 all_vertices_marked = false;
538 break;
539 }
540 }
541
542 if (all_vertices_marked)
543 entities.push_back(e);
544 }
545
546 return entities;
547}
548
572template <std::floating_point T, MarkerFn<T> U>
573std::vector<std::int32_t> locate_entities_boundary(const Mesh<T>& mesh, int dim,
574 U marker)
575{
576 auto topology = mesh.topology();
577 assert(topology);
578 const int tdim = topology->dim();
579 if (dim == tdim)
580 {
581 throw std::runtime_error(
582 "Cannot use mesh::locate_entities_boundary (boundary) for cells.");
583 }
584
585 // Compute list of boundary facets
586 mesh.topology_mutable()->create_entities(tdim - 1);
587 mesh.topology_mutable()->create_connectivity(tdim - 1, tdim);
588 const std::vector<std::int32_t> boundary_facets
589 = exterior_facet_indices(*topology);
590
591 using cmdspan3x_t = MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan<
592 const T,
593 MDSPAN_IMPL_STANDARD_NAMESPACE::extents<
594 std::size_t, 3, MDSPAN_IMPL_STANDARD_NAMESPACE::dynamic_extent>>;
595
596 // Run marker function on the vertex coordinates
597 const auto [facet_entities, xdata, vertex_to_pos]
598 = impl::compute_vertex_coords_boundary(mesh, dim, boundary_facets);
599 cmdspan3x_t x(xdata.data(), 3, xdata.size() / 3);
600 const std::vector<std::int8_t> marked = marker(x);
601 if (marked.size() != x.extent(1))
602 throw std::runtime_error("Length of array of markers is wrong.");
603
604 // Loop over entities and check vertex markers
605 mesh.topology_mutable()->create_entities(dim);
606 auto e_to_v = topology->connectivity(dim, 0);
607 assert(e_to_v);
608 std::vector<std::int32_t> entities;
609 for (auto e : facet_entities)
610 {
611 // Iterate over entity vertices
612 bool all_vertices_marked = true;
613 for (auto v : e_to_v->links(e))
614 {
615 const std::int32_t pos = vertex_to_pos[v];
616 if (!marked[pos])
617 {
618 all_vertices_marked = false;
619 break;
620 }
621 }
622
623 // Mark facet with all vertices marked
624 if (all_vertices_marked)
625 entities.push_back(e);
626 }
627
628 return entities;
629}
630
647template <std::floating_point T>
648std::vector<std::int32_t>
649entities_to_geometry(const Mesh<T>& mesh, int dim,
650 std::span<const std::int32_t> entities, bool orient)
651{
652 auto topology = mesh.topology();
653 assert(topology);
654
655 if (topology->cell_types().size() > 1)
656 throw std::runtime_error("Mixed topology not supported");
657 CellType cell_type = topology->cell_types()[0];
658 if (cell_type == CellType::prism and dim == 2)
659 throw std::runtime_error("More work needed for prism cells");
660 if (orient and (cell_type != CellType::tetrahedron or dim != 2))
661 throw std::runtime_error("Can only orient facets of a tetrahedral mesh");
662
663 const Geometry<T>& geometry = mesh.geometry();
664 auto x = geometry.x();
665
666 const int tdim = topology->dim();
667 mesh.topology_mutable()->create_entities(dim);
668 mesh.topology_mutable()->create_connectivity(dim, tdim);
669 mesh.topology_mutable()->create_connectivity(dim, 0);
670 mesh.topology_mutable()->create_connectivity(tdim, 0);
671
672 auto xdofs = geometry.dofmap();
673 auto e_to_c = topology->connectivity(dim, tdim);
674 if (!e_to_c)
675 {
676 throw std::runtime_error(
677 "Entity-to-cell connectivity has not been computed.");
678 }
679
680 auto e_to_v = topology->connectivity(dim, 0);
681 if (!e_to_v)
682 {
683 throw std::runtime_error(
684 "Entity-to-vertex connectivity has not been computed.");
685 }
686
687 auto c_to_v = topology->connectivity(tdim, 0);
688 if (!e_to_v)
689 {
690 throw std::runtime_error(
691 "Cell-to-vertex connectivity has not been computed.");
692 }
693
694 const std::size_t num_vertices
695 = num_cell_vertices(cell_entity_type(cell_type, dim, 0));
696 std::vector<std::int32_t> geometry_idx(entities.size() * num_vertices);
697 for (std::size_t i = 0; i < entities.size(); ++i)
698 {
699 const std::int32_t idx = entities[i];
700 // Always pick the second cell to be consistent with the e_to_v connectivity
701 const std::int32_t cell = e_to_c->links(idx).back();
702 auto ev = e_to_v->links(idx);
703 assert(ev.size() == num_vertices);
704 auto cv = c_to_v->links(cell);
705 std::span<const std::int32_t> xc(
706 xdofs.data_handle() + xdofs.extent(1) * cell, xdofs.extent(1));
707 for (std::size_t j = 0; j < num_vertices; ++j)
708 {
709 int k = std::distance(cv.begin(), std::find(cv.begin(), cv.end(), ev[j]));
710 assert(k < (int)cv.size());
711 geometry_idx[i * num_vertices + j] = xc[k];
712 }
713
714 if (orient)
715 {
716 // Compute cell midpoint
717 std::array<T, 3> midpoint = {0, 0, 0};
718 for (std::int32_t j : xc)
719 for (int k = 0; k < 3; ++k)
720 midpoint[k] += x[3 * j + k];
721 std::transform(midpoint.begin(), midpoint.end(), midpoint.begin(),
722 [size = xc.size()](auto x) { return x / size; });
723
724 // Compute vector triple product of two edges and vector to midpoint
725 std::array<T, 3> p0, p1, p2;
726 std::copy_n(std::next(x.begin(), 3 * geometry_idx[i * num_vertices + 0]),
727 3, p0.begin());
728 std::copy_n(std::next(x.begin(), 3 * geometry_idx[i * num_vertices + 1]),
729 3, p1.begin());
730 std::copy_n(std::next(x.begin(), 3 * geometry_idx[i * num_vertices + 2]),
731 3, p2.begin());
732
733 std::array<T, 9> a;
734 std::transform(midpoint.begin(), midpoint.end(), p0.begin(), a.begin(),
735 [](auto x, auto y) { return x - y; });
736 std::transform(p1.begin(), p1.end(), p0.begin(), std::next(a.begin(), 3),
737 [](auto x, auto y) { return x - y; });
738 std::transform(p2.begin(), p2.end(), p0.begin(), std::next(a.begin(), 6),
739 [](auto x, auto y) { return x - y; });
740
741 // Midpoint direction should be opposite to normal, hence this
742 // should be negative. Switch points if not.
743 if (math::det(a.data(), {3, 3}) > 0.0)
744 {
745 std::swap(geometry_idx[i * num_vertices + 1],
746 geometry_idx[i * num_vertices + 2]);
747 }
748 }
749 }
750
751 return geometry_idx;
752}
753
759 = mesh::GhostMode::none,
760 const graph::partition_fn& partfn
762
770std::vector<std::int32_t>
771compute_incident_entities(const Topology& topology,
772 std::span<const std::int32_t> entities, int d0,
773 int d1);
774
779template <typename U>
781 MPI_Comm comm, const graph::AdjacencyList<std::int64_t>& cells,
782 const std::vector<fem::CoordinateElement<
783 typename std::remove_reference_t<typename U::value_type>>>& elements,
784 const U& x, std::array<std::size_t, 2> xshape,
785 CellPartitionFunction partitioner)
786{
787 const fem::ElementDofLayout dof_layout = elements[0].create_dof_layout();
788
789 // Function top build geometry. Used to scope memory operations.
790 auto build_topology = [](auto comm, auto& elements, auto& dof_layout,
791 auto& cells, auto& partitioner)
792 {
793 // -- Partition topology
794
795 // Note: the function extract_topology (returns an
796 // AdjacencyList<std::int64_t>) extract topology data, e.g. 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. We don't create an
800 // object before calling partitioner to ensure that memory is
801 // freed immediately.
802 //
803 // Note: extract_topology could be skipped for 'P1' elements since
804 // it is just the identity
805
806 // Compute the destination rank for cells on this process via graph
807 // partitioning.
808 const int tdim = cell_dim(elements[0].cell_shape());
809
812 std::vector<std::int64_t> original_cell_index0;
813 std::vector<int> ghost_owners;
814 if (partitioner)
815 {
816 const int size = dolfinx::MPI::size(comm);
817 dest = partitioner(
818 comm, size, tdim,
819 extract_topology(elements[0].cell_shape(), dof_layout, cells));
820
821 // -- Distribute cells (topology, includes higher-order 'nodes')
822
823 // Distribute cells to destination rank
824 std::vector<int> src;
825 std::tie(cell_nodes, src, original_cell_index0, ghost_owners)
826 = graph::build::distribute(comm, cells, dest);
827 }
828 else
829 {
830 int rank = dolfinx::MPI::rank(comm);
832 std::vector<std::int32_t>(cells.num_nodes(), rank), 1);
833 cell_nodes = cells;
834 std::int64_t offset(0), num_owned(cells.num_nodes());
835 MPI_Exscan(&num_owned, &offset, 1, MPI_INT64_T, MPI_SUM, comm);
836 original_cell_index0.resize(cell_nodes.num_nodes());
837 std::iota(original_cell_index0.begin(), original_cell_index0.end(),
838 offset);
839 }
840
841 // -- Extra cell topology
842
843 // Extract cell 'topology', i.e. extract the vertices for each cell
844 // and discard any 'higher-order' nodes
845
847 = extract_topology(elements[0].cell_shape(), dof_layout, cell_nodes);
848
849 // -- Re-order cells
850
851 // Build local dual graph for owned cells to apply re-ordering to
852 const std::int32_t num_owned_cells
853 = cells_extracted.num_nodes() - ghost_owners.size();
854
855 auto [graph, unmatched_facets, max_v, facet_attached_cells]
857 std::span<const std::int64_t>(
858 cells_extracted.array().data(),
859 cells_extracted.offsets()[num_owned_cells]),
860 std::span<const std::int32_t>(cells_extracted.offsets().data(),
861 num_owned_cells + 1),
862 tdim);
863
864 const std::vector<int> remap = graph::reorder_gps(graph);
865
866 // Create re-ordered cell lists (leaves ghosts unchanged)
867 std::vector<std::int64_t> original_cell_index(original_cell_index0.size());
868 for (std::size_t i = 0; i < remap.size(); ++i)
869 original_cell_index[remap[i]] = original_cell_index0[i];
870 std::copy_n(std::next(original_cell_index0.cbegin(), num_owned_cells),
871 ghost_owners.size(),
872 std::next(original_cell_index.begin(), num_owned_cells));
873 cells_extracted = reorder_list(cells_extracted, remap);
874 cell_nodes = reorder_list(cell_nodes, remap);
875
876 // -- Create Topology
877
878 // Boundary vertices are marked as unknown
879 std::vector<std::int64_t> boundary_vertices(unmatched_facets);
880 std::sort(boundary_vertices.begin(), boundary_vertices.end());
881 boundary_vertices.erase(
882 std::unique(boundary_vertices.begin(), boundary_vertices.end()),
883 boundary_vertices.end());
884
885 // Remove -1 if it occurs in boundary vertices (may occur in mixed topology)
886 if (boundary_vertices.size() > 0 and boundary_vertices[0] == -1)
887 boundary_vertices.erase(boundary_vertices.begin());
888
889 // Create cells and vertices with the ghosting requested. Input
890 // topology includes cells shared via facet, but ghosts will be
891 // removed later if not required by ghost_mode.
892
893 std::vector<std::int32_t> cell_group_offsets
894 = {0, std::int32_t(cells_extracted.num_nodes() - ghost_owners.size()),
895 cells_extracted.num_nodes()};
896 std::vector<mesh::CellType> cell_type = {elements[0].cell_shape()};
897 return std::pair{create_topology(comm, cells_extracted, original_cell_index,
898 ghost_owners, cell_type,
899 cell_group_offsets, boundary_vertices),
900 std::move(cell_nodes)};
901 };
902
903 auto [topology, cell_nodes]
904 = build_topology(comm, elements, dof_layout, cells, partitioner);
905
906 // Create connectivity required to compute the Geometry (extra
907 // connectivities for higher-order geometries)
908 int tdim = topology.dim();
909 for (int e = 1; e < tdim; ++e)
910 {
911 if (dof_layout.num_entity_dofs(e) > 0)
912 topology.create_entities(e);
913 }
914
915 if (elements[0].needs_dof_permutations())
916 topology.create_entity_permutations();
917
918 Geometry geometry
919 = create_geometry(comm, topology, elements, cell_nodes, x, xshape[1]);
921 comm, std::make_shared<Topology>(std::move(topology)),
922 std::move(geometry));
923}
924
944template <typename U>
945Mesh<typename std::remove_reference_t<typename U::value_type>>
947 const std::vector<fem::CoordinateElement<
948 std::remove_reference_t<typename U::value_type>>>& elements,
949 const U& x, std::array<std::size_t, 2> xshape, GhostMode ghost_mode)
950{
951 if (dolfinx::MPI::size(comm) == 1)
952 return create_mesh(comm, cells, elements, x, xshape, nullptr);
953 else
954 {
955 return create_mesh(comm, cells, elements, x, xshape,
956 create_cell_partitioner(ghost_mode));
957 }
958}
959
968template <std::floating_point T>
969std::tuple<Mesh<T>, std::vector<std::int32_t>, std::vector<std::int32_t>,
970 std::vector<std::int32_t>>
971create_submesh(const Mesh<T>& mesh, int dim,
972 std::span<const std::int32_t> entities)
973{
974 // Create sub-topology
975 mesh.topology_mutable()->create_connectivity(dim, 0);
976 auto [topology, subentity_to_entity, subvertex_to_vertex]
977 = mesh::create_subtopology(*mesh.topology(), dim, entities);
978
979 // Create sub-geometry
980 const int tdim = mesh.topology()->dim();
981 mesh.topology_mutable()->create_entities(dim);
982 mesh.topology_mutable()->create_connectivity(dim, tdim);
983 mesh.topology_mutable()->create_connectivity(tdim, dim);
984 auto [geometry, subx_to_x_dofmap] = mesh::create_subgeometry(
985 *mesh.topology(), mesh.geometry(), dim, subentity_to_entity);
986
987 return {Mesh<T>(mesh.comm(), std::make_shared<Topology>(std::move(topology)),
988 std::move(geometry)),
989 std::move(subentity_to_entity), std::move(subvertex_to_vertex),
990 std::move(subx_to_x_dofmap)};
991}
992
993} // namespace dolfinx::mesh
A CoordinateElement manages coordinate mappings for isoparametric cells.
Definition CoordinateElement.h:39
The class represents the degree-of-freedom (dofs) for an element. Dofs are associated with a mesh ent...
Definition ElementDofLayout.h:31
int num_entity_dofs(int dim) const
Return the number of dofs for a given entity dimension.
Definition ElementDofLayout.cpp:63
This class provides a static adjacency list data structure. It is commonly used to store directed gra...
Definition AdjacencyList.h:28
int num_links(std::size_t node) const
Number of connections for given node.
Definition AdjacencyList.h:102
std::span< T > links(std::size_t node)
Get the links (edges) for given node.
Definition AdjacencyList.h:112
const std::vector< T > & array() const
Return contiguous array of links for all nodes (const version)
Definition AdjacencyList.h:129
std::int32_t num_nodes() const
Get the number of nodes.
Definition AdjacencyList.h:97
const std::vector< std::int32_t > & offsets() const
Offset for each node in array() (const version)
Definition AdjacencyList.h:135
Geometry stores the geometry imposed on a mesh.
Definition Geometry.h:33
MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan< const std::int32_t, MDSPAN_IMPL_STANDARD_NAMESPACE::dextents< std::size_t, 2 > > dofmap() const
DOF map.
Definition Geometry.h:94
std::span< const value_type > x() const
Access geometry degrees-of-freedom data (const version).
Definition Geometry.h:113
A Mesh consists of a set of connected and numbered mesh topological entities, and geometry data.
Definition Mesh.h:23
std::shared_ptr< Topology > topology_mutable() const
Get mesh topology if one really needs the mutable version.
Definition Mesh.h:72
std::shared_ptr< Topology > topology()
Get mesh topology.
Definition Mesh.h:64
Geometry< T > & geometry()
Get mesh geometry.
Definition Mesh.h:76
MPI_Comm comm() const
Mesh MPI communicator.
Definition Mesh.h:84
Requirements on function for geometry marking.
Definition utils.h:479
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:90
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:436
int size(MPI_Comm comm)
Return size of the group (number of processes) associated with the communicator.
Definition MPI.cpp:72
int rank(MPI_Comm comm)
Return process rank for the communicator.
Definition MPI.cpp:64
Finite element method functionality.
Definition assemble_matrix_impl.h:25
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:36
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
AdjacencyList< typename std::decay_t< U >::value_type > regular_adjacency_list(U &&data, int degree)
Construct a constant degree (valency) adjacency list.
Definition AdjacencyList.h:183
Mesh data structures and algorithms on meshes.
Definition DofMap.h:32
mesh::Geometry< typename std::remove_reference_t< typename U::value_type > > create_geometry(MPI_Comm comm, const Topology &topology, const std::vector< fem::CoordinateElement< std::remove_reference_t< typename U::value_type > > > &elements, const graph::AdjacencyList< std::int64_t > &cell_nodes, 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:179
std::pair< mesh::Geometry< T >, std::vector< int32_t > > create_subgeometry(const Topology &topology, const Geometry< T > &geometry, int dim, std::span< const std::int32_t > subentity_to_entity)
Create a sub-geometry for a subset of entities.
Definition Geometry.h:280
Topology create_topology(MPI_Comm comm, const graph::AdjacencyList< std::int64_t > &cells, std::span< const std::int64_t > original_cell_index, std::span< const int > ghost_owners, const std::vector< CellType > &cell_type, const std::vector< std::int32_t > &cell_group_offsets, std::span< const std::int64_t > boundary_vertices)
Create a distributed mesh topology.
Definition Topology.cpp:892
GhostMode
Enum for different partitioning ghost modes.
Definition utils.h:35
std::function< graph::AdjacencyList< std::int32_t >(MPI_Comm comm, int nparts, int tdim, const graph::AdjacencyList< std::int64_t > &cells)> CellPartitionFunction
Signature for the cell partitioning function. The function should compute the destination rank for ce...
Definition utils.h:195
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:273
int cell_dim(CellType type)
Return topological dimension of cell type.
Definition cell_types.cpp:134
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:1157
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:573
int num_cell_vertices(CellType type)
Number vertices for a cell type.
Definition cell_types.cpp:147
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 std::int64_t > cells, std::span< const std::int32_t > offsets, int tdim)
Compute the local part of the dual graph (cell-cell connections via facets) and facet with only one a...
Definition graphbuild.cpp:391
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:219
Mesh< typename std::remove_reference_t< typename U::value_type > > create_mesh(MPI_Comm comm, const graph::AdjacencyList< std::int64_t > &cells, const std::vector< fem::CoordinateElement< typename std::remove_reference_t< typename U::value_type > > > &elements, const U &x, std::array< std::size_t, 2 > xshape, CellPartitionFunction partitioner)
Create a mesh using a provided mesh partitioning function.
Definition utils.h:780
graph::AdjacencyList< std::int64_t > extract_topology(const CellType &cell_type, const fem::ElementDofLayout &layout, const graph::AdjacencyList< std::int64_t > &cells)
Extract topology from cell data, i.e. extract cell vertices.
Definition utils.cpp:28
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:500
std::vector< std::int32_t > entities_to_geometry(const Mesh< T > &mesh, int dim, std::span< const std::int32_t > entities, bool orient)
Determine the indices in the geometry data for each vertex of the given mesh entities.
Definition utils.h:649
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:55
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:105
CellType
Cell type identifier.
Definition cell_types.h:22
std::vector< T > compute_midpoints(const Mesh< T > &mesh, int dim, std::span< const std::int32_t > entities)
Compute the midpoints for mesh entities of a given dimension.
Definition utils.h:397
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:971
CellPartitionFunction create_cell_partitioner(mesh::GhostMode ghost_mode=mesh::GhostMode::none, const graph::partition_fn &partfn=&graph::partition_graph)
Create a function that computes destination rank for mesh cells in this rank by applying the default ...
Definition utils.cpp:84