deal.II version GIT relicensing-6834-g5b78e6bcdf 2026-10-01 11:20:01+00:00
\(\newcommand{\dealvcentcolon}{\mathrel{\mathop{:}}}\) \(\newcommand{\dealcoloneq}{\dealvcentcolon\mathrel{\mkern-1.2mu}=}\) \(\newcommand{\jump}[1]{\left[\!\left[ #1 \right]\!\right]}\) \(\newcommand{\average}[1]{\left\{\!\left\{ #1 \right\}\!\right\}}\)
Loading...
Searching...
No Matches
grid_tools_topology.cc
Go to the documentation of this file.
1// -----------------------------------------------------------------------------
2//
3// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception OR LGPL-2.1-or-later
4// Copyright (C) 2023 - 2026 by the deal.II authors
5//
6// This file is part of the deal.II library.
7//
8// Detailed license information governing the source code and contributions
9// can be found in LICENSE.md and CONTRIBUTING.md at the top level directory.
10//
11// -----------------------------------------------------------------------------
12
17
20
21#include <boost/container/small_vector.hpp>
22
23#include <algorithm>
24#include <map>
25#include <numeric>
26#include <set>
27#include <vector>
28
30
31namespace GridTools
32{
33 namespace internal
34 {
35 // Lexical comparison for sorting CellData objects.
36 template <int structdim>
38 {
39 bool
41 const CellData<structdim> &b) const
42 {
43 // Check vertices:
44 if (std::lexicographical_compare(std::begin(a.vertices),
45 std::end(a.vertices),
46 std::begin(b.vertices),
47 std::end(b.vertices)))
48 return true;
49 // it should never be necessary to check the material or manifold
50 // ids as a 'tiebreaker' (since they must be equal if the vertex
51 // indices are equal). Assert it anyway:
52 if constexpr (running_in_debug_mode())
53 {
54 if (std::equal(std::begin(a.vertices),
55 std::end(a.vertices),
56 std::begin(b.vertices)))
57 {
58 Assert(a.material_id == b.material_id &&
59 a.manifold_id == b.manifold_id,
61 "Two CellData objects with equal vertices must "
62 "have the same material/boundary ids and manifold "
63 "ids."));
64 }
65 }
66 return false;
67 }
68 };
69
70
80 template <int dim>
82 {
83 public:
87 template <typename FaceIteratorType>
88 void
89 insert_face_data(const FaceIteratorType &face)
90 {
91 CellData<dim - 1> face_cell_data(face->n_vertices());
92 for (unsigned int vertex_n = 0; vertex_n < face->n_vertices();
93 ++vertex_n)
94 face_cell_data.vertices[vertex_n] = face->vertex_index(vertex_n);
95 face_cell_data.boundary_id = face->boundary_id();
96 face_cell_data.manifold_id = face->manifold_id();
97
98 face_data.insert(std::move(face_cell_data));
99 }
100
106 {
107 SubCellData subcell_data;
108
109 for (const CellData<dim - 1> &face_cell_data : face_data)
110 {
111 if constexpr (dim - 1 == 0)
112 (void)face_data;
113 if constexpr (dim - 1 == 1)
114 subcell_data.boundary_lines.push_back(face_cell_data);
115 else if constexpr (dim - 1 == 2)
116 subcell_data.boundary_quads.push_back(face_cell_data);
117 else
119 }
120 return subcell_data;
121 }
122
123
124 private:
125 std::set<CellData<dim - 1>, internal::CellDataComparator<dim - 1>>
127 };
128
129
130 // Do nothing for dim=1:
131 template <>
133 {
134 public:
135 template <typename FaceIteratorType>
136 void
137 insert_face_data(const FaceIteratorType &)
138 {}
139
142 {
143 return SubCellData();
144 }
145 };
146 } // namespace internal
147
148
149
150 template <int dim, int spacedim>
151 std::
152 tuple<std::vector<Point<spacedim>>, std::vector<CellData<dim>>, SubCellData>
154 {
155 Assert(tria.n_levels() >= 1,
156 ExcMessage("The input triangulation must be non-empty."));
157
158 std::vector<Point<spacedim>> vertices = tria.get_vertices();
159 std::vector<CellData<dim>> cells;
160
162 std::set<CellData<1>, internal::CellDataComparator<1>>
163 line_data; // only used in 3d
164
165 for (const auto &cell : tria.cell_iterators_on_level(0))
166 {
167 // Save cell data
168 CellData<dim> cell_data(cell->n_vertices());
169 for (const unsigned int cell_vertex_n : cell->vertex_indices())
170 {
171 Assert(cell->vertex_index(cell_vertex_n) < vertices.size(),
173 cell_data.vertices[cell_vertex_n] =
174 cell->vertex_index(cell_vertex_n);
175 }
176 cell_data.material_id = cell->material_id();
177 cell_data.manifold_id = cell->manifold_id();
178 cells.emplace_back(std::move(cell_data));
179
180 // Save face data
181 if (dim > 1)
182 {
183 for (const unsigned int face_n : cell->face_indices())
184 // We don't need to insert anything if we have default values
185 {
186 const auto face = cell->face(face_n);
187 if (face->boundary_id() != numbers::internal_face_boundary_id ||
188 face->manifold_id() != numbers::flat_manifold_id)
189 face_data.insert_face_data(face);
190 }
191 }
192 // Save line data
193 if (dim == 3)
194 {
195 for (unsigned int line_n = 0; line_n < cell->n_lines(); ++line_n)
196 {
197 const auto line = cell->line(line_n);
198 // We don't need to insert anything if we have default values
199 if (line->boundary_id() != numbers::internal_face_boundary_id ||
200 line->manifold_id() != numbers::flat_manifold_id)
201 {
202 CellData<1> line_cell_data(line->n_vertices());
203 for (const unsigned int vertex_n : line->vertex_indices())
204 line_cell_data.vertices[vertex_n] =
205 line->vertex_index(vertex_n);
206 line_cell_data.boundary_id = line->boundary_id();
207 line_cell_data.manifold_id = line->manifold_id();
208 line_data.insert(std::move(line_cell_data));
209 }
210 }
211 }
212 }
213
214 SubCellData subcell_data = face_data.get();
215
216 if (dim == 3)
217 for (const CellData<1> &face_line_data : line_data)
218 subcell_data.boundary_lines.push_back(face_line_data);
219
220 // We end up with a 'vertices' array that uses some of the entries,
221 // but not all -- specifically, all vertices referenced by level-0
222 // cells. We can compress the array:
223 GridTools::delete_unused_vertices(vertices, cells, subcell_data);
224
225 return std::tuple<std::vector<Point<spacedim>>,
226 std::vector<CellData<dim>>,
227 SubCellData>(std::move(vertices),
228 std::move(cells),
229 std::move(subcell_data));
230 }
231
232
233
234 template <int dim, int spacedim>
235 void
237 std::vector<CellData<dim>> &cells,
238 SubCellData &subcelldata)
239 {
240 Assert(
241 subcelldata.check_consistency(dim),
243 "Invalid SubCellData supplied according to ::check_consistency(). "
244 "This is caused by data containing objects for the wrong dimension."));
245
246 // first check which vertices are actually used
247 std::vector<bool> vertex_used(vertices.size(), false);
248 for (unsigned int c = 0; c < cells.size(); ++c)
249 for (unsigned int v = 0; v < cells[c].vertices.size(); ++v)
250 {
251 Assert(cells[c].vertices[v] < vertices.size(),
252 ExcMessage("Invalid vertex index encountered! cells[" +
253 Utilities::int_to_string(c) + "].vertices[" +
254 Utilities::int_to_string(v) + "]=" +
255 Utilities::int_to_string(cells[c].vertices[v]) +
256 " is invalid, because only " +
257 Utilities::int_to_string(vertices.size()) +
258 " vertices were supplied."));
259 vertex_used[cells[c].vertices[v]] = true;
260 }
261
262
263 // then renumber the vertices that are actually used in the same order as
264 // they were beforehand
265 const unsigned int invalid_vertex = numbers::invalid_unsigned_int;
266 std::vector<unsigned int> new_vertex_numbers(vertices.size(),
267 invalid_vertex);
268 unsigned int next_free_number = 0;
269 for (unsigned int i = 0; i < vertices.size(); ++i)
270 if (vertex_used[i] == true)
271 {
272 new_vertex_numbers[i] = next_free_number;
273 ++next_free_number;
274 }
275
276 // next replace old vertex numbers by the new ones
277 for (unsigned int c = 0; c < cells.size(); ++c)
278 for (auto &v : cells[c].vertices)
279 v = new_vertex_numbers[v];
280
281 // same for boundary data
282 for (unsigned int c = 0; c < subcelldata.boundary_lines.size(); // NOLINT
283 ++c)
284 for (unsigned int v = 0;
285 v < subcelldata.boundary_lines[c].vertices.size();
286 ++v)
287 {
288 Assert(subcelldata.boundary_lines[c].vertices[v] <
289 new_vertex_numbers.size(),
291 "Invalid vertex index in subcelldata.boundary_lines. "
292 "subcelldata.boundary_lines[" +
293 Utilities::int_to_string(c) + "].vertices[" +
294 Utilities::int_to_string(v) + "]=" +
296 subcelldata.boundary_lines[c].vertices[v]) +
297 " is invalid, because only " +
298 Utilities::int_to_string(vertices.size()) +
299 " vertices were supplied."));
300 subcelldata.boundary_lines[c].vertices[v] =
301 new_vertex_numbers[subcelldata.boundary_lines[c].vertices[v]];
302 }
303
304 for (unsigned int c = 0; c < subcelldata.boundary_quads.size(); // NOLINT
305 ++c)
306 for (unsigned int v = 0;
307 v < subcelldata.boundary_quads[c].vertices.size();
308 ++v)
309 {
310 Assert(subcelldata.boundary_quads[c].vertices[v] <
311 new_vertex_numbers.size(),
313 "Invalid vertex index in subcelldata.boundary_quads. "
314 "subcelldata.boundary_quads[" +
315 Utilities::int_to_string(c) + "].vertices[" +
316 Utilities::int_to_string(v) + "]=" +
318 subcelldata.boundary_quads[c].vertices[v]) +
319 " is invalid, because only " +
320 Utilities::int_to_string(vertices.size()) +
321 " vertices were supplied."));
322
323 subcelldata.boundary_quads[c].vertices[v] =
324 new_vertex_numbers[subcelldata.boundary_quads[c].vertices[v]];
325 }
326
327 // finally copy over the vertices which we really need to a new array and
328 // replace the old one by the new one
329 std::vector<Point<spacedim>> tmp;
330 tmp.reserve(std::count(vertex_used.begin(), vertex_used.end(), true));
331 for (unsigned int v = 0; v < vertices.size(); ++v)
332 if (vertex_used[v] == true)
333 tmp.push_back(vertices[v]);
334 swap(vertices, tmp);
335 }
336
337
338
339 template <int dim, int spacedim>
340 void
342 std::vector<CellData<dim>> &cells,
343 SubCellData &subcelldata,
344 std::vector<unsigned int> &considered_vertices,
345 const double tol)
346 {
347 if (tol == 0.0)
348 return; // nothing to do per definition
349
350 AssertIndexRange(2, vertices.size());
351 std::vector<unsigned int> new_vertex_numbers(vertices.size());
352 std::iota(new_vertex_numbers.begin(), new_vertex_numbers.end(), 0);
353
354 // if the considered_vertices vector is empty, consider all vertices
355 if (considered_vertices.empty())
356 considered_vertices = new_vertex_numbers;
357 Assert(considered_vertices.size() <= vertices.size(), ExcInternalError());
358
359 // The algorithm below improves upon the naive O(n^2) algorithm by first
360 // sorting vertices by their value in one component and then only
361 // comparing vertices for equality which are nearly equal in that
362 // component. For example, if @p vertices form a cube, then we will only
363 // compare points that have the same x coordinate when we try to find
364 // duplicated vertices.
365
366 // Start by finding the longest coordinate direction. This minimizes the
367 // number of points that need to be compared against each-other in a
368 // single set for typical geometries.
369 const BoundingBox<spacedim> bbox(vertices);
370
371 unsigned int longest_coordinate_direction = 0;
372 double longest_coordinate_length = bbox.side_length(0);
373 for (unsigned int d = 1; d < spacedim; ++d)
374 {
375 const double coordinate_length = bbox.side_length(d);
376 if (longest_coordinate_length < coordinate_length)
377 {
378 longest_coordinate_length = coordinate_length;
379 longest_coordinate_direction = d;
380 }
381 }
382
383 // Sort vertices (while preserving their vertex numbers) along that
384 // coordinate direction:
385 std::vector<std::pair<unsigned int, Point<spacedim>>> sorted_vertices;
386 sorted_vertices.reserve(vertices.size());
387 for (const unsigned int vertex_n : considered_vertices)
388 {
389 AssertIndexRange(vertex_n, vertices.size());
390 sorted_vertices.emplace_back(vertex_n, vertices[vertex_n]);
391 }
392 std::sort(sorted_vertices.begin(),
393 sorted_vertices.end(),
394 [&](const std::pair<unsigned int, Point<spacedim>> &a,
395 const std::pair<unsigned int, Point<spacedim>> &b) {
396 return a.second[longest_coordinate_direction] <
397 b.second[longest_coordinate_direction];
398 });
399
400 auto within_tolerance = [=](const Point<spacedim> &a,
401 const Point<spacedim> &b) {
402 for (unsigned int d = 0; d < spacedim; ++d)
403 if (std::abs(a[d] - b[d]) > tol)
404 return false;
405 return true;
406 };
407
408 // Find a range of numbers that have the same component in the longest
409 // coordinate direction:
410 auto range_start = sorted_vertices.begin();
411 while (range_start != sorted_vertices.end())
412 {
413 auto range_end = range_start + 1;
414 while (range_end != sorted_vertices.end() &&
415 std::abs(range_end->second[longest_coordinate_direction] -
416 range_start->second[longest_coordinate_direction]) <
417 tol)
418 ++range_end;
419
420 // preserve behavior with older versions of this function by replacing
421 // higher vertex numbers by lower vertex numbers
422 std::sort(range_start,
423 range_end,
424 [](const std::pair<unsigned int, Point<spacedim>> &a,
425 const std::pair<unsigned int, Point<spacedim>> &b) {
426 return a.first < b.first;
427 });
428
429 // Now de-duplicate [range_start, range_end)
430 //
431 // We have identified all points that are within a strip of width 'tol'
432 // in one coordinate direction. Now we need to figure out which of these
433 // are also close in other coordinate directions. If two are close, we
434 // can mark the second one for deletion.
435 for (auto reference = range_start; reference != range_end; ++reference)
436 {
438 for (auto it = reference + 1; it != range_end; ++it)
439 {
440 if (within_tolerance(reference->second, it->second))
441 {
442 new_vertex_numbers[it->first] = reference->first;
443 // skip the replaced vertex in the future
445 }
446 }
447 }
448 range_start = range_end;
449 }
450
451 // now we got a renumbering list. simply renumber all vertices
452 // (non-duplicate vertices get renumbered to themselves, so nothing bad
453 // happens). after that, the duplicate vertices will be unused, so call
454 // delete_unused_vertices() to do that part of the job.
455 for (auto &cell : cells)
456 for (auto &vertex_index : cell.vertices)
457 vertex_index = new_vertex_numbers[vertex_index];
458 for (auto &quad : subcelldata.boundary_quads)
459 for (auto &vertex_index : quad.vertices)
460 vertex_index = new_vertex_numbers[vertex_index];
461 for (auto &line : subcelldata.boundary_lines)
462 for (auto &vertex_index : line.vertices)
463 vertex_index = new_vertex_numbers[vertex_index];
464
465 delete_unused_vertices(vertices, cells, subcelldata);
466 }
467
468
469
470 template <int dim>
471 void
473 const double tol)
474 {
475 if (vertices.empty())
476 return;
477
478 // 1) map point to local vertex index
479 std::map<Point<dim>, unsigned int, FloatingPointComparator<double>>
480 map_point_to_local_vertex_index{FloatingPointComparator<double>(tol)};
481
482 // 2) initialize map with existing points uniquely
483 for (unsigned int i = 0; i < vertices.size(); ++i)
484 map_point_to_local_vertex_index[vertices[i]] = i;
485
486 // no duplicate points are found
487 if (map_point_to_local_vertex_index.size() == vertices.size())
488 return;
489
490 // 3) remove duplicate entries from vertices
491 vertices.resize(map_point_to_local_vertex_index.size());
492 {
493 unsigned int j = 0;
494 for (const auto &p : map_point_to_local_vertex_index)
495 vertices[j++] = p.first;
496 }
497 }
498
499
500
501 template <int dim, int spacedim>
502 std::size_t
504 const std::vector<Point<spacedim>> &all_vertices,
505 std::vector<CellData<dim>> &cells)
506 {
507 // This function is presently only implemented for volumetric (codimension
508 // 0) elements.
509
510 if (dim == 1)
511 return 0;
512 if (dim == 2 && spacedim == 3)
514
515 std::size_t n_negative_cells = 0;
516 std::size_t cell_no = 0;
517 for (auto &cell : cells)
518 {
519 const ArrayView<const unsigned int> vertices(cell.vertices);
520 // Some pathologically twisted cells can have exactly zero measure but
521 // we can still fix them
522 if (GridTools::cell_measure(all_vertices, vertices) <= 0)
523 {
524 ++n_negative_cells;
525 const auto reference_cell =
526 ReferenceCells::n_vertices_to_reference_cell<dim>(
527 vertices.size());
528
529 if (reference_cell.is_hyper_cube())
530 {
531 if (dim == 2)
532 {
533 // flip the cell across the y = x line in 2d
534 std::swap(cell.vertices[1], cell.vertices[2]);
535 }
536 else if (dim == 3)
537 {
538 // swap the front and back faces in 3d
539 std::swap(cell.vertices[0], cell.vertices[2]);
540 std::swap(cell.vertices[1], cell.vertices[3]);
541 std::swap(cell.vertices[4], cell.vertices[6]);
542 std::swap(cell.vertices[5], cell.vertices[7]);
543 }
544 }
545 else if (reference_cell.is_simplex())
546 {
547 // By basic rules for computing determinants we can just swap
548 // two vertices to fix a negative volume. Arbitrarily pick the
549 // last two.
550 std::swap(cell.vertices[cell.vertices.size() - 2],
551 cell.vertices[cell.vertices.size() - 1]);
552 }
553 else if (reference_cell == ReferenceCells::Wedge)
554 {
555 // swap the two triangular faces
556 std::swap(cell.vertices[0], cell.vertices[3]);
557 std::swap(cell.vertices[1], cell.vertices[4]);
558 std::swap(cell.vertices[2], cell.vertices[5]);
559 }
560 else if (reference_cell == ReferenceCells::Pyramid)
561 {
562 // Try swapping two vertices in the base - perhaps things were
563 // read in the UCD (counter-clockwise) order instead of lexical
564 std::swap(cell.vertices[2], cell.vertices[3]);
565 }
566 else
567 {
569 }
570 // Check whether the resulting cell is now ok.
571 // If not, then the grid is seriously broken and
572 // we just give up.
573 AssertThrow(GridTools::cell_measure(all_vertices, vertices) > 0,
574 ExcGridHasInvalidCell(cell_no));
575 }
576 ++cell_no;
577 }
578 return n_negative_cells;
579 }
580
581
582 template <int dim, int spacedim>
583 void
585 const std::vector<Point<spacedim>> &all_vertices,
586 std::vector<CellData<dim>> &cells)
587 {
588 const std::size_t n_negative_cells =
589 invert_cells_with_negative_measure(all_vertices, cells);
590
591 // We assume that all cells of a grid have
592 // either positive or negative volumes but
593 // not both mixed. Although above reordering
594 // might work also on single cells, grids
595 // with both kind of cells are very likely to
596 // be broken. Check for this here.
597 AssertThrow(n_negative_cells == 0 || n_negative_cells == cells.size(),
599 std::string(
600 "This function assumes that either all cells have positive "
601 "volume, or that all cells have been specified in an "
602 "inverted vertex order so that their volume is negative. "
603 "(In the latter case, this class automatically inverts "
604 "every cell.) However, the mesh you have specified "
605 "appears to have both cells with positive and cells with "
606 "negative volume. You need to check your mesh which "
607 "cells these are and how they got there.\n"
608 "As a hint, of the total ") +
609 std::to_string(cells.size()) + " cells in the mesh, " +
610 std::to_string(n_negative_cells) +
611 " appear to have a negative volume."));
612 }
613
614
615
616 // Functions and classes for consistently_order_cells
617 namespace
618 {
624 struct CheapEdge
625 {
629 CheapEdge(const unsigned int v0, const unsigned int v1)
630 : v0(v0)
631 , v1(v1)
632 {}
633
638 bool
639 operator<(const CheapEdge &e) const
640 {
641 return ((v0 < e.v0) || ((v0 == e.v0) && (v1 < e.v1)));
642 }
643
644 private:
648 const unsigned int v0, v1;
649 };
650
651
660 template <int dim>
661 bool
662 is_consistent(const std::vector<CellData<dim>> &cells)
663 {
664 std::set<CheapEdge> edges;
665
666 for (typename std::vector<CellData<dim>>::const_iterator c =
667 cells.begin();
668 c != cells.end();
669 ++c)
670 {
671 // construct the edges in reverse order. for each of them,
672 // ensure that the reverse edge is not yet in the list of
673 // edges (return false if the reverse edge already *is* in
674 // the list) and then add the actual edge to it; std::set
675 // eliminates duplicates automatically
676 for (unsigned int l = 0; l < GeometryInfo<dim>::lines_per_cell; ++l)
677 {
678 const CheapEdge reverse_edge(
681 if (edges.find(reverse_edge) != edges.end())
682 return false;
683
684
685 // ok, not. insert edge in correct order
686 const CheapEdge correct_edge(
689 edges.insert(correct_edge);
690 }
691 }
692
693 // no conflicts found, so return true
694 return true;
695 }
696
697
704 template <int dim>
705 struct ParallelEdges
706 {
712 static const unsigned int starter_edges[dim];
713
718 static const unsigned int n_other_parallel_edges = (1 << (dim - 1)) - 1;
719 static const unsigned int
722 };
723
724 template <>
725 const unsigned int ParallelEdges<2>::starter_edges[2] = {0, 2};
726
727 template <>
728 const unsigned int ParallelEdges<2>::parallel_edges[4][1] = {{1},
729 {0},
730 {3},
731 {2}};
732
733 template <>
734 const unsigned int ParallelEdges<3>::starter_edges[3] = {0, 2, 8};
735
736 template <>
737 const unsigned int ParallelEdges<3>::parallel_edges[12][3] = {
738 {1, 4, 5}, // line 0
739 {0, 4, 5}, // line 1
740 {3, 6, 7}, // line 2
741 {2, 6, 7}, // line 3
742 {0, 1, 5}, // line 4
743 {0, 1, 4}, // line 5
744 {2, 3, 7}, // line 6
745 {2, 3, 6}, // line 7
746 {9, 10, 11}, // line 8
747 {8, 10, 11}, // line 9
748 {8, 9, 11}, // line 10
749 {8, 9, 10} // line 11
750 };
751
752
757 struct AdjacentCell
758 {
762 AdjacentCell()
765 {}
766
770 AdjacentCell(const unsigned int cell_index,
771 const unsigned int edge_within_cell)
774 {}
775
776
777 unsigned int cell_index;
778 unsigned int edge_within_cell;
779 };
780
781
782
783 template <int dim>
784 class AdjacentCells;
785
791 template <>
792 class AdjacentCells<2>
793 {
794 public:
799 using const_iterator = const AdjacentCell *;
800
809 void
810 push_back(const AdjacentCell &adjacent_cell)
811 {
813 adjacent_cells[0] = adjacent_cell;
814 else
815 {
819 adjacent_cells[1] = adjacent_cell;
820 }
821 }
822
823
829 begin() const
830 {
831 return adjacent_cells;
832 }
833
834
841 end() const
842 {
843 // check whether the current object stores zero, one, or two
844 // adjacent cells, and use this to point to the element past the
845 // last valid one
847 return adjacent_cells;
849 return adjacent_cells + 1;
850 else
851 return adjacent_cells + 2;
852 }
853
854 private:
861 AdjacentCell adjacent_cells[2];
862 };
863
864
865
873 template <>
874 class AdjacentCells<3> : public std::vector<AdjacentCell>
875 {};
876
877
887 template <int dim>
888 class Edge
889 {
890 public:
896 Edge(const CellData<dim> &cell, const unsigned int edge_number)
897 : orientation_status(not_oriented)
898 {
901
902 // copy vertices for this particular line
903 vertex_indices[0] =
904 cell
906 vertex_indices[1] =
907 cell
909
910 // bring them into standard orientation
911 if (vertex_indices[0] > vertex_indices[1])
912 std::swap(vertex_indices[0], vertex_indices[1]);
913 }
914
919 bool
920 operator<(const Edge<dim> &e) const
921 {
922 return ((vertex_indices[0] < e.vertex_indices[0]) ||
923 ((vertex_indices[0] == e.vertex_indices[0]) &&
924 (vertex_indices[1] < e.vertex_indices[1])));
925 }
926
930 bool
931 operator==(const Edge<dim> &e) const
932 {
933 return ((vertex_indices[0] == e.vertex_indices[0]) &&
934 (vertex_indices[1] == e.vertex_indices[1]));
935 }
936
941 unsigned int vertex_indices[2];
942
947 enum OrientationStatus
948 {
949 not_oriented,
950 forward,
951 backward
952 };
953
954 OrientationStatus orientation_status;
955
960 AdjacentCells<dim> adjacent_cells;
961 };
962
963
964
969 template <int dim>
970 struct Cell
971 {
977 Cell(const CellData<dim> &c, const std::vector<Edge<dim>> &edge_list)
978 {
979 for (const unsigned int i : GeometryInfo<dim>::vertex_indices())
980 vertex_indices[i] = c.vertices[i];
981
982 // now for each of the edges of this cell, find the location inside the
983 // given edge_list array and store than index
984 for (unsigned int l = 0; l < GeometryInfo<dim>::lines_per_cell; ++l)
985 {
986 const Edge<dim> e(c, l);
987 edge_indices[l] =
988 (std::lower_bound(edge_list.begin(), edge_list.end(), e) -
989 edge_list.begin());
990 Assert(edge_indices[l] < edge_list.size(), ExcInternalError());
991 Assert(edge_list[edge_indices[l]] == e, ExcInternalError());
992 }
993 }
994
999
1005 };
1006
1007
1008
1009 template <int dim>
1010 class EdgeDeltaSet;
1011
1021 template <>
1022 class EdgeDeltaSet<2>
1023 {
1024 public:
1028 using const_iterator = const unsigned int *;
1029
1034 EdgeDeltaSet()
1035 {
1037 }
1038
1039
1043 void
1044 clear()
1045 {
1047 }
1048
1053 void
1054 insert(const unsigned int edge_index)
1055 {
1057 edge_indices[0] = edge_index;
1058 else
1059 {
1062 edge_indices[1] = edge_index;
1063 }
1064 }
1065
1066
1071 begin() const
1072 {
1073 return edge_indices;
1074 }
1075
1076
1081 end() const
1082 {
1083 // check whether the current object stores zero, one, or two
1084 // indices, and use this to point to the element past the
1085 // last valid one
1087 return edge_indices;
1089 return edge_indices + 1;
1090 else
1091 return edge_indices + 2;
1092 }
1093
1094 private:
1098 unsigned int edge_indices[2];
1099 };
1100
1101
1102
1114 template <>
1115 class EdgeDeltaSet<3> : public std::set<unsigned int>
1116 {};
1117
1118
1119
1124 template <int dim>
1125 std::vector<Edge<dim>>
1126 build_edges(const std::vector<CellData<dim>> &cells)
1127 {
1128 // build the edge list for all cells. because each cell has
1129 // GeometryInfo<dim>::lines_per_cell edges, the total number
1130 // of edges is this many times the number of cells. of course
1131 // some of them will be duplicates, and we throw them out below
1132 std::vector<Edge<dim>> edge_list;
1133 edge_list.reserve(cells.size() * GeometryInfo<dim>::lines_per_cell);
1134 for (unsigned int i = 0; i < cells.size(); ++i)
1135 for (unsigned int l = 0; l < GeometryInfo<dim>::lines_per_cell; ++l)
1136 edge_list.emplace_back(cells[i], l);
1137
1138 // next sort the edge list and then remove duplicates
1139 std::sort(edge_list.begin(), edge_list.end());
1140 edge_list.erase(std::unique(edge_list.begin(), edge_list.end()),
1141 edge_list.end());
1142
1143 return edge_list;
1144 }
1145
1146
1147
1152 template <int dim>
1153 std::vector<Cell<dim>>
1154 build_cells_and_connect_edges(const std::vector<CellData<dim>> &cells,
1155 std::vector<Edge<dim>> &edges)
1156 {
1157 std::vector<Cell<dim>> cell_list;
1158 cell_list.reserve(cells.size());
1159 for (unsigned int i = 0; i < cells.size(); ++i)
1160 {
1161 // create our own data structure for the cells and let it
1162 // connect to the edges array
1163 cell_list.emplace_back(cells[i], edges);
1164
1165 // then also inform the edges that they are adjacent
1166 // to the current cell, and where within this cell
1167 for (unsigned int l = 0; l < GeometryInfo<dim>::lines_per_cell; ++l)
1168 edges[cell_list.back().edge_indices[l]].adjacent_cells.push_back(
1169 AdjacentCell(i, l));
1170 }
1171 Assert(cell_list.size() == cells.size(), ExcInternalError());
1172
1173 return cell_list;
1174 }
1175
1176
1177
1182 template <int dim>
1183 unsigned int
1184 get_next_unoriented_cell(const std::vector<Cell<dim>> &cells,
1185 const std::vector<Edge<dim>> &edges,
1186 const unsigned int current_cell)
1187 {
1188 for (unsigned int c = current_cell; c < cells.size(); ++c)
1189 for (unsigned int l = 0; l < GeometryInfo<dim>::lines_per_cell; ++l)
1190 if (edges[cells[c].edge_indices[l]].orientation_status ==
1191 Edge<dim>::not_oriented)
1192 return c;
1193
1195 }
1196
1197
1198
1204 template <int dim>
1205 void
1206 orient_one_set_of_parallel_edges(const std::vector<Cell<dim>> &cells,
1207 std::vector<Edge<dim>> &edges,
1208 const unsigned int cell,
1209 const unsigned int local_edge)
1210 {
1211 // choose the direction of the first edge. we have free choice
1212 // here and could simply choose "forward" if that's what pleases
1213 // us. however, for backward compatibility with the previous
1214 // implementation used till 2016, let us just choose the
1215 // direction so that it matches what we have in the given cell.
1216 //
1217 // in fact, in what can only be assumed to be a bug in the
1218 // original implementation, after orienting all edges, the code
1219 // that rotates the cells so that they match edge orientations
1220 // (see the rotate_cell() function below) rotated the cell two
1221 // more times by 90 degrees. this is ok -- it simply flips all
1222 // edge orientations, which leaves them valid. rather than do
1223 // the same in the current implementation, we can achieve the
1224 // same effect by modifying the rule above to choose the
1225 // direction of the starting edge of this parallel set
1226 // *opposite* to what it looks like in the current cell
1227 //
1228 // this bug only existed in the 2d implementation since there
1229 // were different implementations for 2d and 3d. consequently,
1230 // only replicate it for the 2d case and be "intuitive" in 3d.
1231 if (edges[cells[cell].edge_indices[local_edge]].vertex_indices[0] ==
1233 local_edge, 0)])
1234 // orient initial edge *opposite* to the way it is in the cell
1235 // (see above for the reason)
1236 edges[cells[cell].edge_indices[local_edge]].orientation_status =
1237 (dim == 2 ? Edge<dim>::backward : Edge<dim>::forward);
1238 else
1239 {
1240 Assert(
1241 edges[cells[cell].edge_indices[local_edge]].vertex_indices[0] ==
1242 cells[cell].vertex_indices
1245 Assert(
1246 edges[cells[cell].edge_indices[local_edge]].vertex_indices[1] ==
1247 cells[cell].vertex_indices
1250
1251 // orient initial edge *opposite* to the way it is in the cell
1252 // (see above for the reason)
1253 edges[cells[cell].edge_indices[local_edge]].orientation_status =
1254 (dim == 2 ? Edge<dim>::forward : Edge<dim>::backward);
1255 }
1256
1257 // walk outward from the given edge as described in
1258 // the algorithm in the paper that documents all of
1259 // this
1260 //
1261 // note that in 2d, each of the Deltas can at most
1262 // contain two elements, whereas in 3d it can be arbitrarily many
1263 EdgeDeltaSet<dim> Delta_k;
1264 EdgeDeltaSet<dim> Delta_k_minus_1;
1265 Delta_k_minus_1.insert(cells[cell].edge_indices[local_edge]);
1266
1267 while (Delta_k_minus_1.begin() !=
1268 Delta_k_minus_1.end()) // while set is not empty
1269 {
1270 Delta_k.clear();
1271
1272 for (typename EdgeDeltaSet<dim>::const_iterator delta =
1273 Delta_k_minus_1.begin();
1274 delta != Delta_k_minus_1.end();
1275 ++delta)
1276 {
1277 Assert(edges[*delta].orientation_status !=
1278 Edge<dim>::not_oriented,
1280
1281 // now go through the cells adjacent to this edge
1282 for (typename AdjacentCells<dim>::const_iterator adjacent_cell =
1283 edges[*delta].adjacent_cells.begin();
1284 adjacent_cell != edges[*delta].adjacent_cells.end();
1285 ++adjacent_cell)
1286 {
1287 const unsigned int K = adjacent_cell->cell_index;
1288 const unsigned int delta_is_edge_in_K =
1289 adjacent_cell->edge_within_cell;
1290
1291 // figure out the direction of delta with respect to the cell
1292 // K (in the orientation in which the user has given it to us)
1293 const unsigned int first_edge_vertex =
1294 (edges[*delta].orientation_status == Edge<dim>::forward ?
1295 edges[*delta].vertex_indices[0] :
1296 edges[*delta].vertex_indices[1]);
1297 const unsigned int first_edge_vertex_in_K =
1298 cells[K]
1300 delta_is_edge_in_K, 0)];
1301 Assert(
1302 first_edge_vertex == first_edge_vertex_in_K ||
1303 first_edge_vertex ==
1305 dim>::line_to_cell_vertices(delta_is_edge_in_K, 1)],
1307
1308 // now figure out which direction the each of the "opposite"
1309 // edges needs to be oriented into.
1310 for (unsigned int o_e = 0;
1311 o_e < ParallelEdges<dim>::n_other_parallel_edges;
1312 ++o_e)
1313 {
1314 // get the index of the opposite edge and select which its
1315 // first vertex needs to be based on how the current edge
1316 // is oriented in the current cell
1317 const unsigned int opposite_edge =
1318 cells[K].edge_indices[ParallelEdges<
1319 dim>::parallel_edges[delta_is_edge_in_K][o_e]];
1320 const unsigned int first_opposite_edge_vertex =
1321 cells[K].vertex_indices
1323 ParallelEdges<
1324 dim>::parallel_edges[delta_is_edge_in_K][o_e],
1325 (first_edge_vertex == first_edge_vertex_in_K ? 0 :
1326 1))];
1327
1328 // then determine the orientation of the edge based on
1329 // whether the vertex we want to be the edge's first
1330 // vertex is already the first vertex of the edge, or
1331 // whether it points in the opposite direction
1332 const typename Edge<dim>::OrientationStatus
1333 opposite_edge_orientation =
1334 (edges[opposite_edge].vertex_indices[0] ==
1335 first_opposite_edge_vertex ?
1336 Edge<dim>::forward :
1337 Edge<dim>::backward);
1338
1339 // see if the opposite edge (there is only one in 2d) has
1340 // already been oriented.
1341 if (edges[opposite_edge].orientation_status ==
1342 Edge<dim>::not_oriented)
1343 {
1344 // the opposite edge is not yet oriented. do orient it
1345 // and add it to Delta_k
1346 edges[opposite_edge].orientation_status =
1347 opposite_edge_orientation;
1348 Delta_k.insert(opposite_edge);
1349 }
1350 else
1351 {
1352 // this opposite edge has already been oriented. it
1353 // should be consistent with the current one in 2d,
1354 // while in 3d it may in fact be mis-oriented, and in
1355 // that case the mesh will not be orientable. indicate
1356 // this by throwing an exception that we can catch
1357 // further up; this has the advantage that we can
1358 // propagate through a couple of functions without
1359 // having to do error checking and without modifying
1360 // the 'cells' array that the user gave us
1361 if (dim == 2)
1362 {
1363 Assert(edges[opposite_edge].orientation_status ==
1364 opposite_edge_orientation,
1366 }
1367 else if (dim == 3)
1368 {
1369 if (edges[opposite_edge].orientation_status !=
1370 opposite_edge_orientation)
1371 throw ExcMeshNotOrientable();
1372 }
1373 else
1375 }
1376 }
1377 }
1378 }
1379
1380 // finally copy the new set to the previous one
1381 // (corresponding to increasing 'k' by one in the
1382 // algorithm)
1383 Delta_k_minus_1 = Delta_k;
1384 }
1385 }
1386
1387
1395 template <int dim>
1396 void
1397 rotate_cell(const std::vector<Cell<dim>> &cell_list,
1398 const std::vector<Edge<dim>> &edge_list,
1399 const unsigned int cell_index,
1400 std::vector<CellData<dim>> &raw_cells)
1401 {
1402 // find the first vertex of the cell. this is the vertex where dim edges
1403 // originate, so for each of the edges record which the starting vertex is
1404 unsigned int starting_vertex_of_edge[GeometryInfo<dim>::lines_per_cell];
1405 for (unsigned int e = 0; e < GeometryInfo<dim>::lines_per_cell; ++e)
1406 {
1407 Assert(edge_list[cell_list[cell_index].edge_indices[e]]
1408 .orientation_status != Edge<dim>::not_oriented,
1410 if (edge_list[cell_list[cell_index].edge_indices[e]]
1411 .orientation_status == Edge<dim>::forward)
1412 starting_vertex_of_edge[e] =
1413 edge_list[cell_list[cell_index].edge_indices[e]]
1414 .vertex_indices[0];
1415 else
1416 starting_vertex_of_edge[e] =
1417 edge_list[cell_list[cell_index].edge_indices[e]]
1418 .vertex_indices[1];
1419 }
1420
1421 // find the vertex number that appears dim times. this will then be
1422 // the vertex at which we want to locate the origin of the cell's
1423 // coordinate system (i.e., vertex 0)
1424 unsigned int origin_vertex_of_cell = numbers::invalid_unsigned_int;
1425 switch (dim)
1426 {
1427 case 2:
1428 {
1429 // in 2d, we can simply enumerate the possibilities where the
1430 // origin may be located because edges zero and one don't share
1431 // any vertices, and the same for edges two and three
1432 if ((starting_vertex_of_edge[0] == starting_vertex_of_edge[2]) ||
1433 (starting_vertex_of_edge[0] == starting_vertex_of_edge[3]))
1434 origin_vertex_of_cell = starting_vertex_of_edge[0];
1435 else if ((starting_vertex_of_edge[1] ==
1436 starting_vertex_of_edge[2]) ||
1437 (starting_vertex_of_edge[1] ==
1438 starting_vertex_of_edge[3]))
1439 origin_vertex_of_cell = starting_vertex_of_edge[1];
1440 else
1442
1443 break;
1444 }
1445
1446 case 3:
1447 {
1448 // one could probably do something similar in 3d, but that seems
1449 // more complicated than one wants to write down. just go
1450 // through the list of possible starting vertices and check
1451 for (origin_vertex_of_cell = 0;
1452 origin_vertex_of_cell < GeometryInfo<dim>::vertices_per_cell;
1453 ++origin_vertex_of_cell)
1454 if (std::count(starting_vertex_of_edge,
1455 starting_vertex_of_edge +
1457 cell_list[cell_index]
1458 .vertex_indices[origin_vertex_of_cell]) == dim)
1459 break;
1460 Assert(origin_vertex_of_cell <
1463
1464 break;
1465 }
1466
1467 default:
1469 }
1470
1471 // now rotate raw_cells[cell_index] in such a way that its orientation
1472 // matches that of cell_list[cell_index]
1473 switch (dim)
1474 {
1475 case 2:
1476 {
1477 // in 2d, we can literally rotate the cell until its origin
1478 // matches the one that we have determined above should be
1479 // the origin vertex
1480 //
1481 // when doing a rotation, take into account the ordering of
1482 // vertices (not in clockwise or counter-clockwise sense)
1483 while (raw_cells[cell_index].vertices[0] != origin_vertex_of_cell)
1484 {
1485 const unsigned int tmp = raw_cells[cell_index].vertices[0];
1486 raw_cells[cell_index].vertices[0] =
1487 raw_cells[cell_index].vertices[1];
1488 raw_cells[cell_index].vertices[1] =
1489 raw_cells[cell_index].vertices[3];
1490 raw_cells[cell_index].vertices[3] =
1491 raw_cells[cell_index].vertices[2];
1492 raw_cells[cell_index].vertices[2] = tmp;
1493 }
1494 break;
1495 }
1496
1497 case 3:
1498 {
1499 // in 3d, the situation is a bit more complicated. from above, we
1500 // now know which vertex is at the origin (because 3 edges
1501 // originate from it), but that still leaves 3 possible rotations
1502 // of the cube. the important realization is that we can choose
1503 // any of them: in all 3 rotations, all edges originate from the
1504 // one vertex, and that fixes the directions of all 12 edges in
1505 // the cube because these 3 cover all 3 equivalence classes!
1506 // consequently, we can select an arbitrary one among the
1507 // permutations -- for example the following ones:
1508 static const unsigned int cube_permutations[8][8] = {
1509 {0, 1, 2, 3, 4, 5, 6, 7},
1510 {1, 5, 3, 7, 0, 4, 2, 6},
1511 {2, 6, 0, 4, 3, 7, 1, 5},
1512 {3, 2, 1, 0, 7, 6, 5, 4},
1513 {4, 0, 6, 2, 5, 1, 7, 3},
1514 {5, 4, 7, 6, 1, 0, 3, 2},
1515 {6, 7, 4, 5, 2, 3, 0, 1},
1516 {7, 3, 5, 1, 6, 2, 4, 0}};
1517
1518 unsigned int
1519 temp_vertex_indices[GeometryInfo<dim>::vertices_per_cell];
1520 for (const unsigned int v : GeometryInfo<dim>::vertex_indices())
1521 temp_vertex_indices[v] =
1522 raw_cells[cell_index]
1523 .vertices[cube_permutations[origin_vertex_of_cell][v]];
1524 for (const unsigned int v : GeometryInfo<dim>::vertex_indices())
1525 raw_cells[cell_index].vertices[v] = temp_vertex_indices[v];
1526
1527 break;
1528 }
1529
1530 default:
1531 {
1533 }
1534 }
1535 }
1536
1537
1543 template <int dim>
1544 void
1545 reorient(std::vector<CellData<dim>> &cells)
1546 {
1547 // first build the arrays that connect cells to edges and the other
1548 // way around
1549 std::vector<Edge<dim>> edge_list = build_edges(cells);
1550 std::vector<Cell<dim>> cell_list =
1551 build_cells_and_connect_edges(cells, edge_list);
1552
1553 // then loop over all cells and start orienting parallel edge sets
1554 // of cells that still have non-oriented edges
1555 unsigned int next_cell_with_unoriented_edge = 0;
1556 while ((next_cell_with_unoriented_edge = get_next_unoriented_cell(
1557 cell_list, edge_list, next_cell_with_unoriented_edge)) !=
1559 {
1560 // see which edge sets are still not oriented
1561 //
1562 // we do not need to look at each edge because if we orient edge
1563 // 0, we will end up with edge 1 also oriented (in 2d; in 3d, there
1564 // will be 3 other edges that are also oriented). there are only
1565 // dim independent sets of edges, so loop over these.
1566 //
1567 // we need to check whether each one of these starter edges may
1568 // already be oriented because the line (sheet) that connects
1569 // globally parallel edges may be self-intersecting in the
1570 // current cell
1571 for (unsigned int l = 0; l < dim; ++l)
1572 if (edge_list[cell_list[next_cell_with_unoriented_edge]
1573 .edge_indices[ParallelEdges<dim>::starter_edges[l]]]
1574 .orientation_status == Edge<dim>::not_oriented)
1575 orient_one_set_of_parallel_edges(
1576 cell_list,
1577 edge_list,
1578 next_cell_with_unoriented_edge,
1579 ParallelEdges<dim>::starter_edges[l]);
1580
1581 // ensure that we have really oriented all edges now, not just
1582 // the starter edges
1583 for (unsigned int l = 0; l < GeometryInfo<dim>::lines_per_cell; ++l)
1584 Assert(edge_list[cell_list[next_cell_with_unoriented_edge]
1585 .edge_indices[l]]
1586 .orientation_status != Edge<dim>::not_oriented,
1588 }
1589
1590 // now that we have oriented all edges, we need to rotate cells
1591 // so that the edges point in the right direction with the now
1592 // rotated coordinate system
1593 for (unsigned int c = 0; c < cells.size(); ++c)
1594 rotate_cell(cell_list, edge_list, c, cells);
1595 }
1596
1597
1598 // overload of the function above for 1d -- there is nothing
1599 // to orient in that case
1600 void
1601 reorient(std::vector<CellData<1>> &)
1602 {}
1603 } // namespace
1604
1605
1606
1607 template <int dim>
1608 void
1610 {
1611 Assert(cells.size() != 0,
1612 ExcMessage(
1613 "List of elements to orient must have at least one cell"));
1614
1615 // there is nothing for us to do in 1d
1616 if (dim == 1)
1617 return;
1618
1619 // check if grids are already consistent. if so, do
1620 // nothing. if not, then do the reordering
1621 if (!is_consistent(cells))
1622 try
1623 {
1624 reorient(cells);
1625 }
1626 catch (const ExcMeshNotOrientable &)
1627 {
1628 // the mesh is not orientable. this is acceptable if we are in 3d,
1629 // as class Triangulation knows how to handle this, but it is
1630 // not in 2d; in that case, re-throw the exception
1631 if (dim < 3)
1632 throw;
1633 }
1634 }
1635
1636
1637
1638 template <int dim, int spacedim>
1639 std::map<unsigned int, Point<spacedim>>
1641 {
1642 std::map<unsigned int, Point<spacedim>> vertex_map;
1644 cell = tria.begin_active(),
1645 endc = tria.end();
1646 for (; cell != endc; ++cell)
1647 {
1648 for (const unsigned int i : cell->face_indices())
1649 {
1650 const typename Triangulation<dim, spacedim>::face_iterator &face =
1651 cell->face(i);
1652 if (face->at_boundary())
1653 {
1654 for (unsigned j = 0; j < face->n_vertices(); ++j)
1655 {
1656 const Point<spacedim> &vertex = face->vertex(j);
1657 const unsigned int vertex_index = face->vertex_index(j);
1658 vertex_map[vertex_index] = vertex;
1659 }
1660 }
1661 }
1662 }
1663 return vertex_map;
1664 }
1665
1666
1667
1668 template <int dim, int spacedim>
1669 std::vector<std::vector<std::pair<unsigned int, Point<spacedim>>>>
1671 const Mapping<dim, spacedim> &mapping)
1672 {
1673 Assert(dim == 2, ExcMessage("Only implemented for 2D triangulations"));
1674 // This map holds the two vertex indices of each face.
1675 // Counterclockwise first vertex index on first position,
1676 // counterclockwise second vertex index on second position.
1677 std::map<unsigned int, unsigned int> face_vertex_indices;
1678 std::map<unsigned int, Point<spacedim>> vertex_to_point;
1679
1680 // Iterate over all active cells at the boundary
1681 for (const auto &cell : tria.active_cell_iterators())
1682 {
1683 for (const unsigned int f : cell->face_indices())
1684 {
1685 if (cell->face(f)->at_boundary())
1686 {
1687 // get mapped vertices of the cell
1688 const auto v_mapped = mapping.get_vertices(cell);
1689 const unsigned int v0 = cell->face(f)->vertex_index(0);
1690 const unsigned int v1 = cell->face(f)->vertex_index(1);
1691
1692 if (cell->reference_cell() == ReferenceCells::Triangle)
1693 {
1694 // add indices and first mapped vertex of the face
1695 vertex_to_point[v0] = v_mapped[f];
1696 face_vertex_indices[v0] = v1;
1697 }
1698 else if (cell->reference_cell() ==
1700 {
1701 // Ensure that vertex indices of the face are in
1702 // counterclockwise order inserted in the map.
1703 if (f == 0 || f == 3)
1704 {
1705 // add indices and first mapped vertex of the face
1706 vertex_to_point[v1] =
1708 1)];
1709 face_vertex_indices[v1] = v0;
1710 }
1711 else
1712 {
1713 // add indices and first mapped vertex of the face
1714 vertex_to_point[v0] =
1716 0)];
1717 face_vertex_indices[v0] = v1;
1718 }
1719 }
1720 else
1721 {
1723 }
1724 }
1725 }
1726 }
1727
1728 std::vector<std::vector<std::pair<unsigned int, Point<spacedim>>>>
1729 boundaries;
1730 std::vector<std::pair<unsigned int, Point<spacedim>>> current_boundary;
1731
1732 // Vertex to start counterclockwise insertion
1733 unsigned int start_index = face_vertex_indices.begin()->first;
1734 unsigned int current_index = start_index;
1735
1736 // As long as still entries in the map, use last vertex index to
1737 // find next vertex index
1738 while (face_vertex_indices.size() > 0)
1739 {
1740 const auto vertex_it = vertex_to_point.find(current_index);
1741 Assert(vertex_it != vertex_to_point.end(),
1742 ExcMessage("This should not occur, please report bug"));
1743 current_boundary.emplace_back(vertex_it->first, vertex_it->second);
1744 vertex_to_point.erase(vertex_it);
1745
1746 const auto it = face_vertex_indices.find(current_index);
1747 // If the boundary is one closed loop, the next vertex index
1748 // must exist as key until the map is empty.
1749 Assert(it != face_vertex_indices.end(),
1750 ExcMessage("Triangulation might contain holes"));
1751
1752 current_index = it->second;
1753 face_vertex_indices.erase(it);
1754
1755 // traversed one closed boundary loop
1756 if (current_index == start_index)
1757 {
1758 boundaries.push_back(current_boundary);
1759 current_boundary.clear();
1760
1761 if (face_vertex_indices.size() == 0)
1762 {
1763 break;
1764 }
1765
1766 // Take arbitrary remaining vertex as new start
1767 // for next boundary loop
1768 start_index = face_vertex_indices.begin()->first;
1769 current_index = start_index;
1770 }
1771 }
1772 return boundaries;
1773 }
1774
1775
1776
1777 template <int dim, int spacedim>
1778 void
1780 const bool isotropic,
1781 const unsigned int max_iterations)
1782 {
1783 unsigned int iter = 0;
1784 bool continue_refinement = true;
1785
1786 while (continue_refinement && (iter < max_iterations))
1787 {
1788 if (max_iterations != numbers::invalid_unsigned_int)
1789 ++iter;
1790 continue_refinement = false;
1791
1792 for (const auto &cell : tria.active_cell_iterators())
1793 for (const unsigned int j : cell->face_indices())
1794 if (cell->at_boundary(j) == false &&
1795 cell->neighbor(j)->has_children())
1796 {
1797 if (isotropic)
1798 {
1799 cell->set_refine_flag();
1800 continue_refinement = true;
1801 }
1802 else
1803 continue_refinement |= cell->flag_for_face_refinement(j);
1804 }
1805
1807 }
1808 }
1809
1810
1811
1812 template <int dim, int spacedim>
1813 void
1815 const double max_ratio,
1816 const unsigned int max_iterations)
1817 {
1818 unsigned int iter = 0;
1819 bool continue_refinement = true;
1820
1821 while (continue_refinement && (iter < max_iterations))
1822 {
1823 ++iter;
1824 continue_refinement = false;
1825 for (const auto &cell : tria.active_cell_iterators())
1826 {
1827 std::pair<unsigned int, double> info =
1828 GridTools::get_longest_direction<dim, spacedim>(cell);
1829 if (info.second > max_ratio)
1830 {
1831 cell->set_refine_flag(
1832 RefinementCase<dim>::cut_axis(info.first));
1833 continue_refinement = true;
1834 }
1835 }
1837 }
1838 }
1839
1840
1841
1842 template <int dim, int spacedim>
1843 std::map<unsigned int, Point<spacedim>>
1845 const Mapping<dim, spacedim> &mapping)
1846 {
1847 std::map<unsigned int, Point<spacedim>> result;
1848 for (const auto &cell : container.active_cell_iterators())
1849 {
1850 if (!cell->is_artificial())
1851 {
1852 const auto vs = mapping.get_vertices(cell);
1853 for (unsigned int i = 0; i < vs.size(); ++i)
1854 result[cell->vertex_index(i)] = vs[i];
1855 }
1856 }
1857 return result;
1858 }
1859
1860
1861
1862 template <int dim, int spacedim>
1863 std::vector<
1864 std::set<typename Triangulation<dim, spacedim>::active_cell_iterator>>
1866 {
1867 std::vector<
1868 std::set<typename Triangulation<dim, spacedim>::active_cell_iterator>>
1869 vertex_to_cell_map(triangulation.n_vertices());
1871 cell = triangulation.begin_active(),
1872 endc = triangulation.end();
1873 for (; cell != endc; ++cell)
1874 for (const unsigned int i : cell->vertex_indices())
1875 vertex_to_cell_map[cell->vertex_index(i)].insert(cell);
1876
1877 // Check if mesh has hanging nodes. Do this only locally to
1878 // prevent communication and possible deadlock.
1879 if (triangulation.Triangulation<dim, spacedim>::has_hanging_nodes())
1880 {
1883
1884 // Take care of hanging nodes
1885 cell = triangulation.begin_active();
1886 for (; cell != endc; ++cell)
1887 {
1888 for (const unsigned int i : cell->face_indices())
1889 {
1890 if ((cell->at_boundary(i) == false) &&
1891 (cell->neighbor(i)->is_active()))
1892 {
1894 adjacent_cell = cell->neighbor(i);
1895 for (unsigned int j = 0; j < cell->face(i)->n_vertices();
1896 ++j)
1897 vertex_to_cell_map[cell->face(i)->vertex_index(j)].insert(
1898 adjacent_cell);
1899 }
1900 }
1901
1902 // in 3d also loop over the edges
1903 if (dim == 3)
1904 {
1905 for (unsigned int i = 0; i < cell->n_lines(); ++i)
1906 if (cell->line(i)->has_children())
1907 // the only place where this vertex could have been
1908 // hiding is on the mid-edge point of the edge we
1909 // are looking at
1910 vertex_to_cell_map[cell->line(i)->child(0)->vertex_index(1)]
1911 .insert(cell);
1912 }
1913 }
1914 }
1915
1916 return vertex_to_cell_map;
1917 }
1918
1919
1920
1921 template <int dim, int spacedim>
1922 void
1924 const Triangulation<dim, spacedim> &triangulation,
1925 DynamicSparsityPattern &cell_connectivity)
1926 {
1927 cell_connectivity.reinit(triangulation.n_active_cells(),
1928 triangulation.n_active_cells());
1929
1930 // loop over all cells and their neighbors to build the sparsity
1931 // pattern. note that it's a bit hard to enter all the connections when a
1932 // neighbor has children since we would need to find out which of its
1933 // children is adjacent to the current cell. this problem can be omitted
1934 // if we only do something if the neighbor has no children -- in that case
1935 // it is either on the same or a coarser level than we are. in return, we
1936 // have to add entries in both directions for both cells
1937 for (const auto &cell : triangulation.active_cell_iterators())
1938 {
1939 const unsigned int index = cell->active_cell_index();
1940 cell_connectivity.add(index, index);
1941 for (auto f : cell->face_indices())
1942 if ((cell->at_boundary(f) == false) &&
1943 (cell->neighbor(f)->has_children() == false))
1944 {
1945 const unsigned int other_index =
1946 cell->neighbor(f)->active_cell_index();
1947 cell_connectivity.add(index, other_index);
1948 cell_connectivity.add(other_index, index);
1949 }
1950 }
1951 }
1952
1953
1954
1955 template <int dim, int spacedim>
1956 void
1958 const Triangulation<dim, spacedim> &triangulation,
1959 DynamicSparsityPattern &cell_connectivity)
1960 {
1961 // The choice of 16 or fewer neighbors here is based on empirical
1962 // measurements.
1963 //
1964 // Vertices in a structured hexahedral mesh have 8 adjacent cells. In a
1965 // structured tetrahedral mesh, about 98% of vertices have 16 neighbors or
1966 // fewer. Similarly, in an unstructured tetrahedral mesh, if we count the
1967 // number of neighbors each vertex has we obtain the following distribution:
1968 //
1969 // 3, 1
1970 // 4, 728
1971 // 5, 4084
1972 // 6, 7614
1973 // 7, 17329
1974 // 8, 31145
1975 // 9, 46698
1976 // 10, 64193
1977 // 11, 68269
1978 // 12, 63574
1979 // 13, 57016
1980 // 14, 50476
1981 // 15, 41886
1982 // 16, 31820
1983 // 17, 21269
1984 // 18, 12217
1985 // 19, 6072
1986 // 20, 2527
1987 // 21, 825
1988 // 22, 262
1989 // 23, 61
1990 // 24, 12
1991 // 26, 1
1992 //
1993 // so about 86% of vertices have 16 neighbors or fewer. Hence, we picked 16
1994 // neighbors here to cover most cases without allocation.
1995 std::vector<boost::container::small_vector<unsigned int, 16>>
1996 vertex_to_cell(triangulation.n_vertices());
1997 for (const auto &cell : triangulation.active_cell_iterators())
1998 {
1999 for (const unsigned int v : cell->vertex_indices())
2000 vertex_to_cell[cell->vertex_index(v)].push_back(
2001 cell->active_cell_index());
2002 }
2003
2004 cell_connectivity.reinit(triangulation.n_active_cells(),
2005 triangulation.n_active_cells());
2006 std::vector<types::global_dof_index> neighbors;
2007 for (const auto &cell : triangulation.active_cell_iterators())
2008 {
2009 neighbors.clear();
2010 for (const unsigned int v : cell->vertex_indices())
2011 neighbors.insert(neighbors.end(),
2012 vertex_to_cell[cell->vertex_index(v)].begin(),
2013 vertex_to_cell[cell->vertex_index(v)].end());
2014 std::sort(neighbors.begin(), neighbors.end());
2015 cell_connectivity.add_entries(cell->active_cell_index(),
2016 neighbors.begin(),
2017 std::unique(neighbors.begin(),
2018 neighbors.end()),
2019 true);
2020 }
2021 }
2022
2023
2024 template <int dim, int spacedim>
2025 void
2027 const Triangulation<dim, spacedim> &triangulation,
2028 const unsigned int level,
2029 DynamicSparsityPattern &cell_connectivity)
2030 {
2031 std::vector<boost::container::small_vector<unsigned int, 16>>
2032 vertex_to_cell(triangulation.n_vertices());
2034 triangulation.begin(level);
2035 cell != triangulation.end(level);
2036 ++cell)
2037 {
2038 for (const unsigned int v : cell->vertex_indices())
2039 vertex_to_cell[cell->vertex_index(v)].push_back(cell->index());
2040 }
2041
2042 cell_connectivity.reinit(triangulation.n_cells(level),
2043 triangulation.n_cells(level));
2044 std::vector<types::global_dof_index> neighbors;
2045 for (const auto &cell : triangulation.cell_iterators_on_level(level))
2046 {
2047 neighbors.clear();
2048 for (const unsigned int v : cell->vertex_indices())
2049 neighbors.insert(neighbors.end(),
2050 vertex_to_cell[cell->vertex_index(v)].begin(),
2051 vertex_to_cell[cell->vertex_index(v)].end());
2052 std::sort(neighbors.begin(), neighbors.end());
2053 cell_connectivity.add_entries(cell->index(),
2054 neighbors.begin(),
2055 std::unique(neighbors.begin(),
2056 neighbors.end()),
2057 true);
2058 }
2059 }
2060
2061 namespace internal
2062 {
2063 template <int dim, int spacedim>
2064 void
2068 {
2069 AssertDimension(vertex_indices.size(), cell->n_vertices());
2070
2071 // to reduce the cost of this function when passing down into quads,
2072 // then lines, then vertices, we use a more low-level access method
2073 // for hexahedral cells, where we can streamline most of the logic
2074 const ReferenceCell<dim> ref_cell = cell->reference_cell();
2075 if (ref_cell == ReferenceCells::Hexahedron)
2076 for (unsigned int face = 4; face < 6; ++face)
2077 {
2078 const auto face_iter = cell->face(face);
2079 const std::array<types::geometric_orientation, 2> line_orientations{
2080 {face_iter->line_orientation(0), face_iter->line_orientation(1)}};
2081 const std::array<unsigned int, 2> line_vertex_indices{
2082 {line_orientations[0] == numbers::default_geometric_orientation,
2083 line_orientations[1] == numbers::default_geometric_orientation}};
2084 const std::array<unsigned int, 4> raw_vertex_indices{
2085 {face_iter->line(0)->vertex_index(1 - line_vertex_indices[0]),
2086 face_iter->line(1)->vertex_index(1 - line_vertex_indices[1]),
2087 face_iter->line(0)->vertex_index(line_vertex_indices[0]),
2088 face_iter->line(1)->vertex_index(line_vertex_indices[1])}};
2089
2090 const auto combined_orientation =
2091 cell->combined_face_orientation(face);
2092 const std::array<unsigned int, 4> vertex_order{
2093 {ref_cell.standard_to_real_face_vertex(0,
2094 face,
2095 combined_orientation),
2097 face,
2098 combined_orientation),
2100 face,
2101 combined_orientation),
2103 face,
2104 combined_orientation)}};
2105
2106 const unsigned int index = 4 * (face - 4);
2107 for (unsigned int i = 0; i < 4; ++i)
2108 vertex_indices[index + i] = raw_vertex_indices[vertex_order[i]];
2109 }
2110 else if (ref_cell == ReferenceCells::Quadrilateral)
2111 {
2112 const std::array<types::geometric_orientation, 2> line_orientations{
2113 {cell->line_orientation(0), cell->line_orientation(1)}};
2114 const std::array<unsigned int, 2> line_vertex_indices{
2115 {line_orientations[0] == numbers::default_geometric_orientation,
2116 line_orientations[1] == numbers::default_geometric_orientation}};
2117 const std::array<unsigned int, 4> raw_vertex_indices{
2118 {cell->line(0)->vertex_index(1 - line_vertex_indices[0]),
2119 cell->line(1)->vertex_index(1 - line_vertex_indices[1]),
2120 cell->line(0)->vertex_index(line_vertex_indices[0]),
2121 cell->line(1)->vertex_index(line_vertex_indices[1])}};
2122 for (unsigned int i = 0; i < 4; ++i)
2123 vertex_indices[i] = raw_vertex_indices[i];
2124 }
2125 else if (ref_cell == ReferenceCells::Line)
2126 {
2127 vertex_indices[0] = cell->vertex_index(0);
2128 vertex_indices[1] = cell->vertex_index(1);
2129 }
2130 else
2131 {
2132 Assert(dim == 2 || dim == 3, ExcInternalError());
2133 for (const unsigned int i : cell->vertex_indices())
2134 {
2135 const auto [face_index, vertex_index] =
2137 const auto vertex_within_face_index =
2139 vertex_index,
2140 face_index,
2141 cell->combined_face_orientation(face_index));
2142 vertex_indices[i] =
2143 cell->face(face_index)->vertex_index(vertex_within_face_index);
2144 }
2145 }
2146 }
2147 } // namespace internal
2148} /* namespace GridTools */
2149
2150// explicit instantiations
2151#include "grid/grid_tools_topology.inst"
2152
*  iterator end()
*  *  iterator begin()
*  *  const_iterator()=default
***mech_lbc_system increment_interpolation_handlers push_back(scale_z_handler)
bool operator==(const AlignedVector< T > &lhs, const AlignedVector< T > &rhs)
std::size_t size() const
Definition array_view.h:737
Number side_length(const unsigned int direction) const
void add_entries(const size_type row, ForwardIterator begin, ForwardIterator end, const bool indices_are_unique_and_sorted=false)
void reinit(const size_type m, const size_type n, const IndexSet &rowset=IndexSet())
void add(const size_type i, const size_type j)
void insert_face_data(const FaceIteratorType &)
void insert_face_data(const FaceIteratorType &face)
std::set< CellData< dim - 1 >, internal::CellDataComparator< dim - 1 > > face_data
Abstract base class for mapping classes.
Definition mapping.h:318
virtual boost::container::small_vector< Point< spacedim >, ReferenceCells::max_n_vertices< dim >() > get_vertices(const typename Triangulation< dim, spacedim >::cell_iterator &cell) const
Definition point.h:111
std::array< unsigned int, 2 > standard_vertex_to_face_and_vertex_index(const unsigned int vertex) const
unsigned int standard_to_real_face_vertex(const unsigned int vertex, const unsigned int face, const types::geometric_orientation face_orientation) const
bool all_reference_cells_are_hyper_cube() const
cell_iterator begin(const unsigned int level=0) const
unsigned int n_active_cells() const
const std::vector< Point< spacedim > > & get_vertices() const
unsigned int n_levels() const
cell_iterator end() const
virtual void execute_coarsening_and_refinement()
unsigned int n_cells() const
unsigned int n_vertices() const
active_cell_iterator begin_active(const unsigned int level=0) const
#define DEAL_II_NAMESPACE_OPEN
Definition config.h:38
constexpr bool running_in_debug_mode()
Definition config.h:76
#define DEAL_II_NAMESPACE_CLOSE
Definition config.h:39
#define DEAL_II_ASSERT_UNREACHABLE()
#define DEAL_II_NOT_IMPLEMENTED()
unsigned int level
Definition grid_out.cc:4642
unsigned int edge_within_cell
unsigned int edge_indices[GeometryInfo< dim >::lines_per_cell]
AdjacentCell adjacent_cells[2]
static const unsigned int n_other_parallel_edges
static const unsigned int starter_edges[dim]
static const unsigned int parallel_edges[GeometryInfo< dim >::lines_per_cell][n_other_parallel_edges]
OrientationStatus orientation_status
unsigned int vertex_indices[2]
const unsigned int v0
const unsigned int v1
unsigned int cell_index
IteratorRange< active_cell_iterator > active_cell_iterators() const
IteratorRange< cell_iterator > cell_iterators_on_level(const unsigned int level) const
static ::ExceptionBase & ExcMeshNotOrientable()
static ::ExceptionBase & ExcNotImplemented()
#define Assert(cond, exc)
#define AssertDimension(dim1, dim2)
static ::ExceptionBase & ExcGridHasInvalidCell(int arg1)
#define AssertIndexRange(index, range)
static ::ExceptionBase & ExcInternalError()
static ::ExceptionBase & ExcMessage(std::string arg1)
#define AssertThrow(cond, exc)
void consistently_order_cells(std::vector< CellData< dim > > &cells)
CGAL::Exact_predicates_exact_constructions_kernel_with_sqrt K
void extract_vertices_without_cache(const typename Triangulation< dim, spacedim >::cell_iterator &cell, const ArrayView< unsigned int > &vertex_indices)
void get_face_connectivity_of_cells(const Triangulation< dim, spacedim > &triangulation, DynamicSparsityPattern &connectivity)
void delete_unused_vertices(std::vector< Point< spacedim > > &vertices, std::vector< CellData< dim > > &cells, SubCellData &subcelldata)
std::map< unsigned int, Point< spacedim > > get_all_vertices_at_boundary(const Triangulation< dim, spacedim > &tria)
std::vector< std::vector< std::pair< unsigned int, Point< spacedim > > > > extract_ordered_boundary_vertices(const Triangulation< dim, spacedim > &tria, const Mapping< dim, spacedim > &mapping=(ReferenceCells::get_hypercube< dim >() .template get_default_linear_mapping< spacedim >()))
void remove_anisotropy(Triangulation< dim, spacedim > &tria, const double max_ratio=1.6180339887, const unsigned int max_iterations=5)
void remove_hanging_nodes(Triangulation< dim, spacedim > &tria, const bool isotropic=false, const unsigned int max_iterations=100)
std::size_t invert_cells_with_negative_measure(const std::vector< Point< spacedim > > &all_vertices, std::vector< CellData< dim > > &cells)
void delete_duplicated_vertices(std::vector< Point< spacedim > > &all_vertices, std::vector< CellData< dim > > &cells, SubCellData &subcelldata, std::vector< unsigned int > &considered_vertices, const double tol=1e-12)
std::vector< std::set< typename Triangulation< dim, spacedim >::active_cell_iterator > > vertex_to_cell_map(const Triangulation< dim, spacedim > &triangulation)
void get_vertex_connectivity_of_cells(const Triangulation< dim, spacedim > &triangulation, DynamicSparsityPattern &connectivity)
void get_vertex_connectivity_of_cells_on_level(const Triangulation< dim, spacedim > &triangulation, const unsigned int level, DynamicSparsityPattern &connectivity)
void invert_all_negative_measure_cells(const std::vector< Point< spacedim > > &all_vertices, std::vector< CellData< dim > > &cells)
std::tuple< std::vector< Point< spacedim > >, std::vector< CellData< dim > >, SubCellData > get_coarse_mesh_description(const Triangulation< dim, spacedim > &tria)
double cell_measure(const std::vector< Point< dim > > &all_vertices, const ArrayView< const unsigned int > &vertex_indices)
std::map< unsigned int, Point< spacedim > > extract_used_vertices(const Triangulation< dim, spacedim > &container, const Mapping< dim, spacedim > &mapping=(ReferenceCells::get_hypercube< dim >() .template get_default_linear_mapping< spacedim >()))
SymmetricTensor< 2, dim, Number > e(const Tensor< 2, dim, Number > &F)
Tensor< 2, dim, Number > l(const Tensor< 2, dim, Number > &F, const Tensor< 2, dim, Number > &dF_dt)
constexpr ReferenceCell< 3 > Hexahedron
constexpr ReferenceCell< 2 > Quadrilateral
constexpr ReferenceCell< 1 > Line
constexpr ReferenceCell< 2 > Triangle
constexpr ReferenceCell< 3 > Pyramid
constexpr ReferenceCell< 3 > Wedge
std::string int_to_string(const unsigned int value, const unsigned int digits=numbers::invalid_unsigned_int)
Definition utilities.cc:464
constexpr unsigned int invalid_unsigned_int
Definition types.h:228
constexpr types::boundary_id internal_face_boundary_id
Definition types.h:319
constexpr types::manifold_id flat_manifold_id
Definition types.h:332
constexpr types::geometric_orientation default_geometric_orientation
Definition types.h:342
::VectorizedArray< Number, width > abs(const ::VectorizedArray< Number, width > &)
void swap(ObserverPointer< T, P > &t1, ObserverPointer< T, Q > &t2)
types::manifold_id manifold_id
Definition cell_data.h:125
std_cxx26::inplace_vector< unsigned int, ReferenceCells::max_n_vertices< structdim >()> vertices
Definition cell_data.h:84
types::material_id material_id
Definition cell_data.h:103
types::boundary_id boundary_id
Definition cell_data.h:114
static unsigned int face_to_cell_vertices(const unsigned int face, const unsigned int vertex, const bool face_orientation=true, const bool face_flip=false, const bool face_rotation=false)
static unsigned int line_to_cell_vertices(const unsigned int line, const unsigned int vertex)
static std_cxx20::ranges::iota_view< unsigned int, unsigned int > vertex_indices()
bool operator()(const CellData< structdim > &a, const CellData< structdim > &b) const
std::vector< CellData< 2 > > boundary_quads
Definition cell_data.h:247
bool check_consistency(const unsigned int dim) const
std::vector< CellData< 1 > > boundary_lines
Definition cell_data.h:231
bool operator<(const SynchronousIterators< Iterators > &a, const SynchronousIterators< Iterators > &b)