deal.II version 9.8.0
\(\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_generator.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) 1999 - 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
15
19
25#include <deal.II/grid/tria.h>
28
30
31#include <array>
32#include <cmath>
33#include <complex>
34#include <limits>
35
36
38
39// work around the problem that doxygen for some reason lists all template
40// specializations in this file
41#ifndef DOXYGEN
42
43namespace GridGenerator
44{
45 namespace Airfoil
46 {
48 // airfoil configuration
49 : airfoil_type("NACA")
50 , naca_id("2412")
51 , joukowski_center(-0.1, 0.14)
52 , airfoil_length(1.0)
53 // far field
54 , height(30.0)
55 , length_b2(15.0)
56 // mesh
57 , incline_factor(0.35)
58 , bias_factor(2.5)
59 , refinements(2)
60 , n_subdivision_x_0(3)
61 , n_subdivision_x_1(2)
62 , n_subdivision_x_2(5)
63 , n_subdivision_y(3)
64 , airfoil_sampling_factor(2)
65 {
66 Assert(
67 airfoil_length <= height,
69 "Mesh is to small to enclose airfoil! Choose larger field or smaller"
70 " chord length!"));
71 Assert(incline_factor < 1.0 && incline_factor >= 0.0,
72 ExcMessage("incline_factor has to be in [0,1)!"));
73 }
74
75
76
77 void
78 AdditionalData::add_parameters(ParameterHandler &prm)
79 {
80 prm.enter_subsection("FarField");
81 {
82 prm.add_parameter(
83 "Height",
84 height,
85 "Mesh height measured from airfoil nose to horizontal boundaries");
86 prm.add_parameter(
87 "LengthB2",
88 length_b2,
89 "Length measured from airfoil leading edge to vertical outlet boundary");
90 prm.add_parameter(
91 "InclineFactor",
92 incline_factor,
93 "Define obliqueness of the vertical mesh around the airfoil");
94 }
95 prm.leave_subsection();
96
97 prm.enter_subsection("AirfoilType");
98 {
99 prm.add_parameter(
100 "Type",
101 airfoil_type,
102 "Type of airfoil geometry, either NACA or Joukowski airfoil",
103 Patterns::Selection("NACA|Joukowski"));
104 }
105 prm.leave_subsection();
106
107 prm.enter_subsection("NACA");
108 {
109 prm.add_parameter("NacaId", naca_id, "Naca serial number");
110 }
111 prm.leave_subsection();
112
113 prm.enter_subsection("Joukowski");
114 {
115 prm.add_parameter("Center",
116 joukowski_center,
117 "Joukowski circle center coordinates");
118 prm.add_parameter("AirfoilLength",
119 airfoil_length,
120 "Joukowski airfoil length leading to trailing edge");
121 }
122 prm.leave_subsection();
123
124 prm.enter_subsection("Mesh");
125 {
126 prm.add_parameter("Refinements",
127 refinements,
128 "Number of global refinements");
129 prm.add_parameter(
130 "NumberSubdivisionX0",
131 n_subdivision_x_0,
132 "Number of subdivisions along the airfoil in blocks with material ID 1 and 4");
133 prm.add_parameter(
134 "NumberSubdivisionX1",
135 n_subdivision_x_1,
136 "Number of subdivisions along the airfoil in blocks with material ID 2 and 5");
137 prm.add_parameter(
138 "NumberSubdivisionX2",
139 n_subdivision_x_2,
140 "Number of subdivisions in horizontal direction on the right of the trailing edge, i.e., blocks with material ID 3 and 6");
141 prm.add_parameter("NumberSubdivisionY",
142 n_subdivision_y,
143 "Number of subdivisions normal to airfoil");
144 prm.add_parameter(
145 "BiasFactor",
146 bias_factor,
147 "Factor to obtain a finer mesh at the airfoil surface");
148 }
149 prm.leave_subsection();
150 }
151
152
153 namespace
154 {
158 class MeshGenerator
159 {
160 public:
161 // IDs of the mesh blocks
162 static const unsigned int id_block_1 = 1;
163 static const unsigned int id_block_2 = 2;
164 static const unsigned int id_block_3 = 3;
165 static const unsigned int id_block_4 = 4;
166 static const unsigned int id_block_5 = 5;
167 static const unsigned int id_block_6 = 6;
168
172 MeshGenerator(const AdditionalData &data)
173 : refinements(data.refinements)
174 , n_subdivision_x_0(data.n_subdivision_x_0)
175 , n_subdivision_x_1(data.n_subdivision_x_1)
176 , n_subdivision_x_2(data.n_subdivision_x_2)
177 , n_subdivision_y(data.n_subdivision_y)
178 , height(data.height)
179 , length_b2(data.length_b2)
180 , incline_factor(data.incline_factor)
181 , bias_factor(data.bias_factor)
182 , edge_length(1.0)
183 , n_cells_x_0(Utilities::pow(2, refinements) * n_subdivision_x_0)
184 , n_cells_x_1(Utilities::pow(2, refinements) * n_subdivision_x_1)
185 , n_cells_x_2(Utilities::pow(2, refinements) * n_subdivision_x_2)
186 , n_cells_y(Utilities::pow(2, refinements) * n_subdivision_y)
187 , n_points_on_each_side(n_cells_x_0 + n_cells_x_1 + 1)
188 // create points on the airfoil
189 , airfoil_1D(set_airfoil_length(
190 // call either the 'joukowski' or 'naca' static member function
191 data.airfoil_type == "Joukowski" ?
192 joukowski(data.joukowski_center,
193 n_points_on_each_side,
194 data.airfoil_sampling_factor) :
195 (data.airfoil_type == "NACA" ?
196 naca(data.naca_id,
197 n_points_on_each_side,
198 data.airfoil_sampling_factor) :
199 std::array<std::vector<Point<2>>, 2>{
200 {std::vector<Point<2>>{Point<2>(0), Point<2>(1)},
201 std::vector<Point<2>>{
202 Point<2>(0),
203 Point<2>(
204 1)}}} /* dummy vector since we are asserting later*/),
205 data.airfoil_length))
206 , end_b0_x_u(airfoil_1D[0][n_cells_x_0][0])
207 , end_b0_x_l(airfoil_1D[1][n_cells_x_0][0])
208 , nose_x(airfoil_1D[0].front()[0])
209 , tail_x(airfoil_1D[0].back()[0])
210 , tail_y(airfoil_1D[0].back()[1])
211 , center_mesh(0.5 * std::abs(end_b0_x_u + end_b0_x_l))
212 , length_b1_x(tail_x - center_mesh)
214 (edge_length + std::abs(nose_x - center_mesh))))
215 // points on coarse grid
216 // coarse grid has to be symmetric in respect to x-axis to allow
217 // periodic BC and make sure that interpolate() works
218 , A(nose_x - edge_length, 0)
219 , B(nose_x, 0)
220 , C(center_mesh, +std::abs(nose_x - center_mesh) * std::tan(gamma))
221 , D(center_mesh, height)
222 , E(center_mesh, -std::abs(nose_x - center_mesh) * std::tan(gamma))
223 , F(center_mesh, -height)
224 , G(tail_x, height)
225 , H(tail_x, 0)
226 , I(tail_x, -height)
227 , J(tail_x + length_b2, 0)
228 , K(J[0], G[1])
229 , L(J[0], I[1])
230 {
231 Assert(data.airfoil_type == "Joukowski" ||
232 data.airfoil_type == "NACA",
233 ExcMessage("Unknown airfoil type."));
234 }
235
239 void
241 Triangulation<2> &tria_grid,
242 std::vector<GridTools::PeriodicFacePair<
243 typename Triangulation<2>::cell_iterator>> *periodic_faces) const
244 {
245 make_coarse_grid(tria_grid);
246
247 set_boundary_ids(tria_grid);
248
249 if (periodic_faces != nullptr)
250 {
252 tria_grid, 5, 4, 1, *periodic_faces);
253 tria_grid.add_periodicity(*periodic_faces);
254 }
255
256 tria_grid.refine_global(refinements);
257 interpolate(tria_grid);
258 }
259
263 void
265 parallel::fullydistributed::Triangulation<2> &parallel_grid,
266 std::vector<GridTools::PeriodicFacePair<
267 typename Triangulation<2>::cell_iterator>> *periodic_faces) const
268 {
269 (void)parallel_grid;
270 (void)periodic_faces;
271
272 AssertThrow(false, ExcMessage("Not implemented, yet!")); // TODO [PM]
273 }
274
275 private:
276 // number of global refinements
277 const unsigned int refinements;
278
279 // number of subdivisions of coarse grid in blocks 1 and 4
280 const unsigned int n_subdivision_x_0;
281
282 // number of subdivisions of coarse grid in blocks 2 and 5
283 const unsigned int n_subdivision_x_1;
284
285 // number of subdivisions of coarse grid in blocks 3 and 6
286 const unsigned int n_subdivision_x_2;
287
288 // number of subdivisions of coarse grid in all blocks (normal to
289 // airfoil or in y-direction, respectively)
290 const unsigned int n_subdivision_y;
291
292 // height of mesh, i.e. length JK or JL and radius of semicircle
293 // (C-Mesh) that arises after interpolation in blocks 1 and 4
294 const double height;
295
296 // length block 3 and 6
297 const double length_b2;
298
299 // factor to move points G and I horizontal to the right, i.e. make
300 // faces HG and HI inclined instead of vertical
301 const double incline_factor;
302
303 // bias factor (if factor goes to zero than equal y = x)
304 const double bias_factor;
305
306 // x-distance between coarse grid vertices A and B, i.e. used only once;
307 const double edge_length;
308
309 // number of cells (after refining) in block 1 and 4 along airfoil
310 const unsigned int n_cells_x_0;
311
312 // number of cells (after refining) in block 2 and 5 along airfoil
313 const unsigned int n_cells_x_1;
314
315 // number of cells (after refining) in block 3 and 6 in x-direction
316 const unsigned int n_cells_x_2;
317
318 // number of cells (after refining) in all blocks normal to airfoil or
319 // in y-direction, respectively
320 const unsigned int n_cells_y;
321
322 // number of airfoil points on each side
323 const unsigned int n_points_on_each_side;
324
325 // vector containing upper/lower airfoil points. First and last point
326 // are identical
327 const std::array<std::vector<Point<2>>, 2> airfoil_1D;
328
329 // x-coordinate of n-th airfoilpoint where n indicates number of cells
330 // in block 1. end_b0_x_u = end_b0_x_l for symmetric airfoils
331 const double end_b0_x_u;
332
333 // x-coordinate of n-th airfoilpoint where n indicates number of cells
334 // in block 4. end_b0_x_u = end_b0_x_l for symmetric airfoils
335 const double end_b0_x_l;
336
337 // x-coordinate of first airfoil point in airfoil_1d[0] and
338 // airfoil_1d[1]
339 const double nose_x;
340
341 // x-coordinate of last airfoil point in airfoil_1d[0] and airfoil_1d[1]
342 const double tail_x;
343
344 // y-coordinate of last airfoil point in airfoil_1d[0] and airfoil_1d[1]
345 const double tail_y;
346
347 // x-coordinate of C,D,E,F indicating ending of blocks 1 and 4 or
348 // beginning of blocks 2 and 5, respectively
349 const double center_mesh;
350
351 // length of blocks 2 and 5
352 const double length_b1_x;
353
354 // angle enclosed between faces DAB and FAB
355 const double gamma;
356
357
358
379 const Point<2> A, B, C, D, E, F, G, H, I, J, K, L;
380
381
382
418 static std::array<std::vector<Point<2>>, 2>
419 joukowski(const Point<2> &centerpoint,
420 const unsigned int number_points,
421 const unsigned int factor)
422 {
423 std::array<std::vector<Point<2>>, 2> airfoil_1D;
424 const unsigned int total_points = 2 * number_points - 2;
425 const unsigned int n_airfoilpoints = factor * total_points;
426 // joukowski points on the entire airfoil, i.e. upper and lower side
427 const auto jouk_points =
428 joukowski_transform(joukowski_circle(centerpoint, n_airfoilpoints));
429
430 // vectors to collect airfoil points on either upper or lower side
431 std::vector<Point<2>> upper_points;
432 std::vector<Point<2>> lower_points;
433
434 {
435 // find point on nose and point on tail
436 unsigned int nose_index = 0;
437 unsigned int tail_index = 0;
438 double nose_x_coordinate = 0;
439 double tail_x_coordinate = 0;
440
441
442 // find index in vector to nose point (min) and tail point (max)
443 for (unsigned int i = 0; i < jouk_points.size(); ++i)
444 {
445 if (jouk_points[i][0] < nose_x_coordinate)
446 {
447 nose_x_coordinate = jouk_points[i][0];
448 nose_index = i;
449 }
450 if (jouk_points[i][0] > tail_x_coordinate)
451 {
452 tail_x_coordinate = jouk_points[i][0];
453 tail_index = i;
454 }
455 }
456
457 // copy point on upper side of airfoil
458 for (unsigned int i = tail_index; i < jouk_points.size(); ++i)
459 upper_points.emplace_back(jouk_points[i]);
460 for (unsigned int i = 0; i <= nose_index; ++i)
461 upper_points.emplace_back(jouk_points[i]);
462 std::reverse(upper_points.begin(), upper_points.end());
463
464 // copy point on lower side of airfoil
465 lower_points.insert(lower_points.end(),
466 jouk_points.begin() + nose_index,
467 jouk_points.begin() + tail_index + 1);
468 }
469
470 airfoil_1D[0] = make_points_equidistant(upper_points, number_points);
471 airfoil_1D[1] = make_points_equidistant(lower_points, number_points);
472
473 // move nose to origin
474 auto move_nose_to_origin = [](std::vector<Point<2>> &vector) {
475 const double nose_x_pos = vector.front()[0];
476 for (auto &i : vector)
477 i[0] -= nose_x_pos;
478 };
479
480 move_nose_to_origin(airfoil_1D[1]);
481 move_nose_to_origin(airfoil_1D[0]);
482
483 return airfoil_1D;
484 }
485
510 static std::vector<Point<2>>
511 joukowski_circle(const Point<2> &center,
512 const unsigned int number_points)
513 {
514 std::vector<Point<2>> circle_points;
515
516 // Create Circle with number_points - points
517 // unsigned int number_points = 2 * points_per_side - 2;
518
519 // Calculate radius so that point (x=1|y=0) is enclosed - requirement
520 // for Joukowski transform
521 const double radius = std::sqrt(center[1] * center[1] +
522 (1 - center[0]) * (1 - center[0]));
523 const double radius_test = std::sqrt(
524 center[1] * center[1] + (1 + center[0]) * (1 + center[0]));
525 // Make sure point (x=-1|y=0) is enclosed by the circle
526 (void)radius_test;
528 radius_test < radius,
530 "Error creating lower circle: Circle for Joukowski-transform does"
531 " not enclose point zeta = -1! Choose different center "
532 "coordinate."));
533 // Create a full circle with radius 'radius' around Point 'center' of
534 // (number_points) equidistant points.
535 const double theta = 2 * numbers::PI / number_points;
536 // first point is leading edge then counterclockwise
537 circle_points.reserve(number_points);
538 for (unsigned int i = 0; i < number_points; ++i)
539 circle_points.emplace_back(center[0] - radius * std::cos(i * theta),
540 center[1] -
541 radius * std::sin(i * theta));
542
543 return circle_points;
544 }
545
554 static std::vector<Point<2>>
555 joukowski_transform(const std::vector<Point<2>> &circle_points)
556 {
557 std::vector<Point<2>> joukowski_points(circle_points.size());
558
559 // transform each point
560 for (unsigned int i = 0; i < circle_points.size(); ++i)
561 {
562 const double chi = circle_points[i][0];
563 const double eta = circle_points[i][1];
564 const std::complex<double> zeta(chi, eta);
565 const std::complex<double> z = zeta + 1. / zeta;
566
567 joukowski_points[i] = {std::real(z), std::imag(z)};
568 }
569 return joukowski_points;
570 }
571
588 static std::array<std::vector<Point<2>>, 2>
589 naca(const std::string &serialnumber,
590 const unsigned int number_points,
591 const unsigned int factor)
592 {
593 // number of non_equidistant airfoilpoints among which will be
594 // interpolated
595 const unsigned int n_airfoilpoints = factor * number_points;
596
597 // create equidistant airfoil points for upper and lower side
598 return {{make_points_equidistant(
599 naca_create_points(serialnumber, n_airfoilpoints, true),
600 number_points),
601 make_points_equidistant(
602 naca_create_points(serialnumber, n_airfoilpoints, false),
603 number_points)}};
604 }
605
617 static std::vector<Point<2>>
618 naca_create_points(const std::string &serialnumber,
619 const unsigned int number_points,
620 const bool is_upper)
621 {
622 Assert(serialnumber.size() == 4,
623 ExcMessage("This NACA-serial number is not implemented!"));
624
625 return naca_create_points_4_digits(serialnumber,
626 number_points,
627 is_upper);
628 }
629
644 static std::vector<Point<2>>
645 naca_create_points_4_digits(const std::string &serialnumber,
646 const unsigned int number_points,
647 const bool is_upper)
648 {
649 // conversion string (char * ) to int
650 const unsigned int digit_0 = (serialnumber[0] - '0');
651 const unsigned int digit_1 = (serialnumber[1] - '0');
652 const unsigned int digit_2 = (serialnumber[2] - '0');
653 const unsigned int digit_3 = (serialnumber[3] - '0');
654
655 const unsigned int digit_23 = 10 * digit_2 + digit_3;
656
657 // maximum thickness in percentage of the cord
658 const double t = static_cast<double>(digit_23) / 100.0;
659
660 std::vector<Point<2>> naca_points;
661
662 if (digit_0 == 0 && digit_1 == 0) // is symmetric
663 for (unsigned int i = 0; i < number_points; ++i)
664 {
665 const double x = i * 1 / (1.0 * number_points - 1);
666 const double y_t =
667 5 * t *
668 (0.2969 * std::sqrt(x) - 0.126 * x -
669 0.3516 * Utilities::fixed_power<2>(x) +
670 0.2843 * Utilities::fixed_power<3>(x) -
672 x)); // half thickness at a position x
673
674 if (is_upper)
675 naca_points.emplace_back(x, +y_t);
676 else
677 naca_points.emplace_back(x, -y_t);
678 }
679 else // is asymmetric
680 for (unsigned int i = 0; i < number_points; ++i)
681 {
682 const double m = 1.0 * digit_0 / 100; // max. chamber
683 const double p = 1.0 * digit_1 / 10; // location of max. chamber
684 const double x = i * 1 / (1.0 * number_points - 1);
685
686 const double y_c =
687 (x <= p) ?
689 (2 * p * x - Utilities::fixed_power<2>(x)) :
690 m / Utilities::fixed_power<2>(1 - p) *
691 ((1 - 2 * p) + 2 * p * x - Utilities::fixed_power<2>(x));
692
693 const double dy_c =
694 (x <= p) ? 2 * m / Utilities::fixed_power<2>(p) * (p - x) :
695 2 * m / Utilities::fixed_power<2>(1 - p) * (p - x);
696
697 const double y_t =
698 5 * t *
699 (0.2969 * std::sqrt(x) - 0.126 * x -
700 0.3516 * Utilities::fixed_power<2>(x) +
701 0.2843 * Utilities::fixed_power<3>(x) -
703 x)); // half thickness at a position x
704
705 const double theta = std::atan(dy_c);
706
707 if (is_upper)
708 naca_points.emplace_back(x - y_t * std::sin(theta),
709 y_c + y_t * std::cos(theta));
710 else
711 naca_points.emplace_back(x + y_t * std::sin(theta),
712 y_c - y_t * std::cos(theta));
713 }
714
715 return naca_points;
716 }
717
718
719
728 static std::array<std::vector<Point<2>>, 2>
729 set_airfoil_length(const std::array<std::vector<Point<2>>, 2> &input,
730 const double desired_len)
731 {
732 std::array<std::vector<Point<2>>, 2> output;
733 output[0] = set_airfoil_length(input[0], desired_len);
734 output[1] = set_airfoil_length(input[1], desired_len);
735
736 return output;
737 }
738
746 static std::vector<Point<2>>
747 set_airfoil_length(const std::vector<Point<2>> &input,
748 const double desired_len)
749 {
750 std::vector<Point<2>> output = input;
751
752 const double scale =
753 desired_len / input.front().distance(input.back());
754
755 for (auto &x : output)
756 x *= scale;
757
758 return output;
759 }
760
771 static std::vector<Point<2>>
772 make_points_equidistant(
773 const std::vector<Point<2>> &non_equidistant_points,
774 const unsigned int number_points)
775 {
776 const unsigned int n_points =
777 non_equidistant_points
778 .size(); // number provided airfoilpoints to interpolate
779
780 // calculate arclength
781 std::vector<double> arclength_L(non_equidistant_points.size(), 0);
782 for (unsigned int i = 0; i < non_equidistant_points.size() - 1; ++i)
783 arclength_L[i + 1] =
784 arclength_L[i] +
785 non_equidistant_points[i + 1].distance(non_equidistant_points[i]);
786
787
788 const auto airfoil_length =
789 arclength_L.back(); // arclength upper or lower side
790 const auto deltaX = airfoil_length / (number_points - 1);
791
792 // Create equidistant points: keep the first (and last) point
793 // unchanged
794 std::vector<Point<2>> equidist(
795 number_points); // number_points is required points on each side for
796 // mesh
797 equidist[0] = non_equidistant_points[0];
798 equidist[number_points - 1] = non_equidistant_points[n_points - 1];
799
800
801 // loop over all subsections
802 for (unsigned int j = 0, i = 1; j < n_points - 1; ++j)
803 {
804 // get reference left and right end of this section
805 const auto Lj = arclength_L[j];
806 const auto Ljp = arclength_L[j + 1];
807
808 while (Lj <= i * deltaX && i * deltaX <= Ljp &&
809 i < number_points - 1)
810 {
811 equidist[i] = Point<2>((i * deltaX - Lj) / (Ljp - Lj) *
812 (non_equidistant_points[j + 1] -
813 non_equidistant_points[j]) +
814 non_equidistant_points[j]);
815 ++i;
816 }
817 }
818 return equidist;
819 }
820
821
822
829 void
830 make_coarse_grid(Triangulation<2> &tria) const
831 {
832 // create vector of serial triangulations for each block and
833 // temporary storage for merging them
834 std::vector<Triangulation<2>> trias(10);
835
836 // helper function to create a subdivided quadrilateral
837 auto make = [](Triangulation<2> &tria,
838 const std::vector<Point<2>> &corner_vertices,
839 const std::vector<unsigned int> &repetitions,
840 const unsigned int material_id) {
841 // create subdivided rectangle with corner points (-1,-1)
842 // and (+1, +1). It serves as reference system
844 repetitions,
845 {-1, -1},
846 {+1, +1});
847
848 // move all vertices to the correct position
849 for (auto it = tria.begin_vertex(); it < tria.end_vertex(); ++it)
850 {
851 auto &point = it->vertex();
852 const double xi = point[0];
853 const double eta = point[1];
854
855 // bilinear mapping
856 point = 0.25 * ((1 - xi) * (1 - eta) * corner_vertices[0] +
857 (1 + xi) * (1 - eta) * corner_vertices[1] +
858 (1 - xi) * (1 + eta) * corner_vertices[2] +
859 (1 + xi) * (1 + eta) * corner_vertices[3]);
860 }
861
862 // set material id of block
863 for (auto cell : tria.active_cell_iterators())
864 cell->set_material_id(material_id);
865 };
866
867 // create a subdivided quadrilateral for each block (see last number
868 // of block id)
869 make(trias[0],
870 {A, B, D, C},
871 {n_subdivision_y, n_subdivision_x_0},
872 id_block_1);
873 make(trias[1],
874 {F, E, A, B},
875 {n_subdivision_y, n_subdivision_x_0},
876 id_block_4);
877 make(trias[2],
878 {C, H, D, G},
879 {n_subdivision_x_1, n_subdivision_y},
880 id_block_2);
881 make(trias[3],
882 {F, I, E, H},
883 {n_subdivision_x_1, n_subdivision_y},
884 id_block_5);
885 make(trias[4],
886 {H, J, G, K},
887 {n_subdivision_x_2, n_subdivision_y},
888 id_block_3);
889 make(trias[5],
890 {I, L, H, J},
891 {n_subdivision_x_2, n_subdivision_y},
892 id_block_6);
893
894
895 // merge triangulation (warning: do not change the order here since
896 // this might change the face ids)
897 GridGenerator::merge_triangulations(trias[0], trias[1], trias[6]);
898 GridGenerator::merge_triangulations(trias[2], trias[3], trias[7]);
899 GridGenerator::merge_triangulations(trias[4], trias[5], trias[8]);
900 GridGenerator::merge_triangulations(trias[6], trias[7], trias[9]);
901 GridGenerator::merge_triangulations(trias[8], trias[9], tria);
902 }
903
904 /*
905 * Loop over all (cells and) boundary faces of a given triangulation
906 * and set the boundary_ids depending on the material_id of the cell and
907 * the face number. The resulting boundary_ids are:
908 * - 0: inlet
909 * - 1: outlet
910 * - 2: upper airfoil surface (aka. suction side)
911 * - 3, lower airfoil surface (aka. pressure side),
912 * - 4: upper far-field side
913 * - 5: lower far-field side
914 */
915 static void
916 set_boundary_ids(Triangulation<2> &tria)
917 {
918 for (auto cell : tria.active_cell_iterators())
919 for (const unsigned int f : cell->face_indices())
920 {
921 if (cell->face(f)->at_boundary() == false)
922 continue;
923
924 const auto mid = cell->material_id();
925
926 if ((mid == id_block_1 && f == 0) ||
927 (mid == id_block_4 && f == 0))
928 cell->face(f)->set_boundary_id(0); // inlet
929 else if ((mid == id_block_3 && f == 0) ||
930 (mid == id_block_6 && f == 2))
931 cell->face(f)->set_boundary_id(1); // outlet
932 else if ((mid == id_block_1 && f == 1) ||
933 (mid == id_block_2 && f == 1))
934 cell->face(f)->set_boundary_id(2); // upper airfoil side
935 else if ((mid == id_block_4 && f == 1) ||
936 (mid == id_block_5 && f == 3))
937 cell->face(f)->set_boundary_id(3); // lower airfoil side
938 else if ((mid == id_block_2 && f == 0) ||
939 (mid == id_block_3 && f == 2))
940 cell->face(f)->set_boundary_id(4); // upper far-field side
941 else if ((mid == id_block_5 && f == 2) ||
942 (mid == id_block_6 && f == 0))
943 cell->face(f)->set_boundary_id(5); // lower far-field side
944 else
945 Assert(false, ExcIndexRange(mid, id_block_1, id_block_6));
946 }
947 }
948
949 /*
950 * Interpolate all vertices of the given triangulation onto the airfoil
951 * geometry, depending on the material_id of the block.
952 * Due to symmetry of coarse grid in respect to
953 * x-axis (by definition of points A-L), blocks 1&4, 2&4 and 3&6 can be
954 * interpolated with the same geometric computations Consider a
955 * bias_factor and incline_factor during interpolation to obtain a more
956 * dense mesh next to airfoil geometry and receive an inclined boundary
957 * between block 2&3 and 5&6, respectively
958 */
959 void
960 interpolate(Triangulation<2> &tria) const
961 {
962 // array storing the information if a vertex was processed
963 std::vector<bool> vertex_processed(tria.n_vertices(), false);
964
965 // rotation matrix for clockwise rotation of block 1 by angle gamma
966 const Tensor<2, 2, double> rotation_matrix_1 =
968 const Tensor<2, 2, double> rotation_matrix_2 =
969 transpose(rotation_matrix_1);
970
971 // horizontal offset in order to place coarse-grid node A in the
972 // origin
973 const Point<2, double> horizontal_offset(A[0], 0.0);
974
975 // Move block 1 so that face BC coincides the x-axis
976 const Point<2, double> trapeze_offset(0.0,
977 std::sin(gamma) * edge_length);
978
979 // loop over vertices of all cells
980 for (const auto &cell : tria.cell_iterators())
981 for (const unsigned int v : cell->vertex_indices())
982 {
983 // vertex has been already processed: nothing to do
984 if (vertex_processed[cell->vertex_index(v)])
985 continue;
986
987 // mark vertex as processed
988 vertex_processed[cell->vertex_index(v)] = true;
989
990 auto &node = cell->vertex(v);
991
992 // distinguish blocks
993 if (cell->material_id() == id_block_1 ||
994 cell->material_id() == id_block_4) // block 1 and 4
995 {
996 // step 1: rotate block 1 clockwise by gamma and move block
997 // 1 so that A[0] is on y-axis so that faces AD and BC are
998 // horizontal. This simplifies the computation of the
999 // required indices for interpolation (all x-nodes are
1000 // positive) Move trapeze to be in first quadrant by adding
1001 // trapeze_offset
1002 Point<2, double> node_;
1003 if (cell->material_id() == id_block_1)
1004 {
1005 node_ = Point<2, double>(rotation_matrix_1 *
1006 (node - horizontal_offset) +
1007 trapeze_offset);
1008 }
1009 // step 1: rotate block 4 counterclockwise and move down so
1010 // that trapeze is located in fourth quadrant (subtracting
1011 // trapeze_offset)
1012 else if (cell->material_id() == id_block_4)
1013 {
1014 node_ = Point<2, double>(rotation_matrix_2 *
1015 (node - horizontal_offset) -
1016 trapeze_offset);
1017 }
1018 // step 2: compute indices ix and iy and interpolate
1019 // trapezoid to a rectangle of length pi/2.
1020 {
1021 const double trapeze_height =
1022 std::sin(gamma) * edge_length;
1023 const double L = height / std::sin(gamma);
1024 const double l_a = std::cos(gamma) * edge_length;
1025 const double l_b = trapeze_height * std::tan(gamma);
1026 const double x1 = std::abs(node_[1]) / std::tan(gamma);
1027 const double x2 = L - l_a - l_b;
1028 const double x3 = std::abs(node_[1]) * std::tan(gamma);
1029 const double Dx = x1 + x2 + x3;
1030 const double deltax =
1031 (trapeze_height - std::abs(node_[1])) / std::tan(gamma);
1032 const double dx = Dx / n_cells_x_0;
1033 const double dy = trapeze_height / n_cells_y;
1034 const int ix =
1035 static_cast<int>(std::round((node_[0] - deltax) / dx));
1036 const int iy =
1037 static_cast<int>(std::round(std::abs(node_[1]) / dy));
1038
1039 node_[0] = numbers::PI / 2 * (1.0 * ix) / n_cells_x_0;
1040 node_[1] = height * (1.0 * iy) / n_cells_y;
1041 }
1042
1043 // step 3: Interpolation between semicircle (of C-Mesh) and
1044 // airfoil contour
1045 {
1046 const double dx = numbers::PI / 2 / n_cells_x_0;
1047 const double dy = height / n_cells_y;
1048 const int ix =
1049 static_cast<int>(std::round(node_[0] / dx));
1050 const int iy =
1051 static_cast<int>(std::round(node_[1] / dy));
1052 const double alpha =
1053 bias_alpha(1 - (1.0 * iy) / n_cells_y);
1054 const double theta = node_[0];
1055 const Point<2> p(-height * std::cos(theta) + center_mesh,
1056 ((cell->material_id() == id_block_1) ?
1057 (height) :
1058 (-height)) *
1059 std::sin(theta));
1060 node = airfoil_1D[(
1061 (cell->material_id() == id_block_1) ? (0) : (1))]
1062 [ix] *
1063 alpha +
1064 p * (1 - alpha);
1065 }
1066 }
1067 else if (cell->material_id() == id_block_2 ||
1068 cell->material_id() == id_block_5) // block 2 and 5
1069 {
1070 // geometric parameters and indices for interpolation
1071 Assert(
1072 (std::abs(D[1] - C[1]) == std::abs(F[1] - E[1])) &&
1073 (std::abs(C[1]) == std::abs(E[1])) &&
1074 (std::abs(G[1]) == std::abs(I[1])),
1075 ExcMessage(
1076 "Points D,C,G and E,F,I are not defined symmetric to "
1077 "x-axis, which is required to interpolate block 2"
1078 " and 5 with same geometric computations."));
1079 const double l_y = D[1] - C[1];
1080 const double l_h = D[1] - l_y;
1081 const double by = -l_h / length_b1_x * (node[0] - H[0]);
1082 const double dy = (height - by) / n_cells_y;
1083 const int iy = static_cast<int>(
1084 std::round((std::abs(node[1]) - by) / dy));
1085 const double dx = length_b1_x / n_cells_x_1;
1086 const int ix = static_cast<int>(
1087 std::round(std::abs(node[0] - center_mesh) / dx));
1088
1089 const double alpha = bias_alpha(1 - (1.0 * iy) / n_cells_y);
1090 // define points on upper/lower horizontal far field side,
1091 // i.e. face DG or FI. Incline factor to move points G and I
1092 // to the right by distance incline_factor*length_b2
1093 const Point<2> p(ix * dx + center_mesh +
1094 incline_factor * length_b2 * ix /
1095 n_cells_x_1,
1096 ((cell->material_id() == id_block_2) ?
1097 (height) :
1098 (-height)));
1099 // interpolate between y = height and upper airfoil points
1100 // (block2) or y = -height and lower airfoil points (block5)
1101 node = airfoil_1D[(
1102 (cell->material_id() == id_block_2) ? (0) : (1))]
1103 [n_cells_x_0 + ix] *
1104 alpha +
1105 p * (1 - alpha);
1106 }
1107 else if (cell->material_id() == id_block_3 ||
1108 cell->material_id() == id_block_6) // block 3 and 6
1109 {
1110 // compute indices ix and iy
1111 const double dx = length_b2 / n_cells_x_2;
1112 const double dy = height / n_cells_y;
1113 const int ix = static_cast<int>(
1114 std::round(std::abs(node[0] - H[0]) / dx));
1115 const int iy =
1116 static_cast<int>(std::round(std::abs(node[1]) / dy));
1117
1118 const double alpha_y = bias_alpha(1 - 1.0 * iy / n_cells_y);
1119 const double alpha_x =
1120 bias_alpha(1 - (static_cast<double>(ix)) / n_cells_x_2);
1121 // define on upper/lower horizontal far field side at y =
1122 // +/- height, i.e. face GK or IL incline factor to move
1123 // points G and H to the right
1124 const Point<2> p1(J[0] - (1 - incline_factor) * length_b2 *
1125 (alpha_x),
1126 ((cell->material_id() == id_block_3) ?
1127 (height) :
1128 (-height)));
1129 // define points on HJ but use tail_y as y-coordinate, in
1130 // case last airfoil point has y =/= 0
1131 const Point<2> p2(J[0] - alpha_x * length_b2, tail_y);
1132 node = p1 * (1 - alpha_y) + p2 * alpha_y;
1133 }
1134 else
1135 {
1136 Assert(false,
1137 ExcIndexRange(cell->material_id(),
1138 id_block_1,
1139 id_block_6));
1140 }
1141 }
1142 }
1143
1144
1145 /*
1146 * This function returns a bias factor 'alpha' which is used to make the
1147 * mesh more tight in close distance of the airfoil.
1148 * It is a bijective function mapping from [0,1] onto [0,1] where values
1149 * near 1 are made tighter.
1150 */
1151 double
1152 bias_alpha(double alpha) const
1153 {
1154 return std::tanh(bias_factor * alpha) / std::tanh(bias_factor);
1155 }
1156 };
1157 } // namespace
1158
1159
1160
1161 void
1162 internal_create_triangulation(
1163 Triangulation<2, 2> &tria,
1164 std::vector<GridTools::PeriodicFacePair<
1165 typename Triangulation<2, 2>::cell_iterator>> *periodic_faces,
1166 const AdditionalData &additional_data)
1167 {
1168 MeshGenerator mesh_generator(additional_data);
1169 // Cast the triangulation to the right type so that the right
1170 // specialization of the function create_triangulation is picked up.
1171 if (auto *parallel_tria =
1173 &tria))
1174 mesh_generator.create_triangulation(*parallel_tria, periodic_faces);
1175 else if (auto *parallel_tria = dynamic_cast<
1177 &tria))
1178 mesh_generator.create_triangulation(*parallel_tria, periodic_faces);
1179 else
1180 mesh_generator.create_triangulation(tria, periodic_faces);
1181 }
1182
1183 template <>
1184 void
1185 create_triangulation(Triangulation<1, 1> &, const AdditionalData &)
1186 {
1187 Assert(false, ExcMessage("Airfoils only exist for 2D and 3d!"));
1188 }
1189
1190
1191
1192 template <>
1193 void
1195 std::vector<GridTools::PeriodicFacePair<
1197 const AdditionalData &)
1198 {
1199 Assert(false, ExcMessage("Airfoils only exist for 2D and 3d!"));
1200 }
1201
1202
1203
1204 template <>
1205 void
1207 const AdditionalData &additional_data)
1208 {
1209 internal_create_triangulation(tria, nullptr, additional_data);
1210 }
1211
1212
1213
1214 template <>
1215 void
1217 Triangulation<2, 2> &tria,
1218 std::vector<GridTools::PeriodicFacePair<
1219 typename Triangulation<2, 2>::cell_iterator>> &periodic_faces,
1220 const AdditionalData &additional_data)
1221 {
1222 internal_create_triangulation(tria, &periodic_faces, additional_data);
1223 }
1224
1225
1226
1227 template <>
1228 void
1230 Triangulation<3, 3> &tria,
1231 std::vector<GridTools::PeriodicFacePair<
1232 typename Triangulation<3, 3>::cell_iterator>> &periodic_faces,
1233 const AdditionalData &additional_data)
1234 {
1235 Assert(false, ExcMessage("3d airfoils are not implemented yet!"));
1236 (void)tria;
1237 (void)additional_data;
1238 (void)periodic_faces;
1239 }
1240 } // namespace Airfoil
1241
1242
1243 namespace
1244 {
1249 template <int dim, int spacedim>
1250 void
1251 colorize_hyper_rectangle(Triangulation<dim, spacedim> &tria)
1252 {
1253 // there is nothing to do in 1d
1254 if constexpr (dim > 1)
1255 {
1256 // there is only one cell
1257 for (const auto &f : tria.begin()->face_indices())
1258 tria.begin()->face(f)->set_boundary_id(f);
1259 }
1260 }
1261
1262
1263
1264 template <int spacedim>
1265 void
1266 colorize_subdivided_hyper_rectangle(Triangulation<1, spacedim> &tria,
1267 const Point<spacedim> &,
1268 const Point<spacedim> &,
1269 const double)
1270 {
1271 for (const auto &cell : tria.cell_iterators())
1272 // boundary indicators are set to
1273 // 0 (left) and 1 (right) by default.
1274 if (cell->center()[0] > 0)
1275 cell->set_material_id(1);
1276 }
1277
1278
1279
1280 template <int dim, int spacedim>
1281 void
1282 colorize_subdivided_hyper_rectangle(Triangulation<dim, spacedim> &tria,
1283 const Point<spacedim> &p1,
1284 const Point<spacedim> &p2,
1285 const double epsilon)
1286 {
1287 // run through all faces and check
1288 // if one of their center coordinates matches
1289 // one of the corner points. Comparisons
1290 // are made using an epsilon which
1291 // should be smaller than the smallest cell
1292 // diameter.
1293
1295 tria.begin_face(),
1296 endface =
1297 tria.end_face();
1298 for (; face != endface; ++face)
1299 if (face->at_boundary())
1300 if (face->boundary_id() == 0)
1301 {
1302 const Point<spacedim> center(face->center());
1303
1304 if (std::abs(center[0] - p1[0]) < epsilon)
1305 face->set_boundary_id(0);
1306 else if (std::abs(center[0] - p2[0]) < epsilon)
1307 face->set_boundary_id(1);
1308 else if (dim > 1 && std::abs(center[1] - p1[1]) < epsilon)
1309 face->set_boundary_id(2);
1310 else if (dim > 1 && std::abs(center[1] - p2[1]) < epsilon)
1311 face->set_boundary_id(3);
1312 else if (dim > 2 && std::abs(center[2] - p1[2]) < epsilon)
1313 face->set_boundary_id(4);
1314 else if (dim > 2 && std::abs(center[2] - p2[2]) < epsilon)
1315 face->set_boundary_id(5);
1316 else
1317 // triangulation says it
1318 // is on the boundary,
1319 // but we could not find
1320 // on which boundary.
1322 }
1323
1324 for (const auto &cell : tria.cell_iterators())
1325 {
1326 types::material_id id = 0;
1327 for (unsigned int d = 0; d < dim; ++d)
1328 if (cell->center()[d] > 0)
1329 id += (1 << d);
1330 cell->set_material_id(id);
1331 }
1332 }
1333
1334
1339 template <int spacdim>
1340 void
1341 colorize_hyper_shell(Triangulation<2, spacdim> &tria,
1342 const Point<spacdim> &,
1343 const double,
1344 const double)
1345 {
1346 // In spite of receiving geometrical
1347 // data, we do this only based on
1348 // topology.
1349
1350 // For the mesh based on cube,
1351 // this is highly irregular
1352 for (auto cell = tria.begin(); cell != tria.end(); ++cell)
1353 {
1354 Assert(cell->face(2)->at_boundary(), ExcInternalError());
1355 cell->face(2)->set_all_boundary_ids(1);
1356 }
1357 }
1358
1359
1364 void
1365 colorize_hyper_shell(Triangulation<3> &tria,
1366 const Point<3> &,
1367 const double,
1368 const double)
1369 {
1370 // the following uses a good amount
1371 // of knowledge about the
1372 // orientation of cells. this is
1373 // probably not good style...
1374 if (tria.n_cells() == 6)
1375 {
1377
1378 Assert(cell->face(4)->at_boundary(), ExcInternalError());
1379 cell->face(4)->set_all_boundary_ids(1);
1380
1381 ++cell;
1382 Assert(cell->face(2)->at_boundary(), ExcInternalError());
1383 cell->face(2)->set_all_boundary_ids(1);
1384
1385 ++cell;
1386 Assert(cell->face(2)->at_boundary(), ExcInternalError());
1387 cell->face(2)->set_all_boundary_ids(1);
1388
1389 ++cell;
1390 Assert(cell->face(0)->at_boundary(), ExcInternalError());
1391 cell->face(0)->set_all_boundary_ids(1);
1392
1393 ++cell;
1394 Assert(cell->face(2)->at_boundary(), ExcInternalError());
1395 cell->face(2)->set_all_boundary_ids(1);
1396
1397 ++cell;
1398 Assert(cell->face(0)->at_boundary(), ExcInternalError());
1399 cell->face(0)->set_all_boundary_ids(1);
1400 }
1401 else if (tria.n_cells() == 12)
1402 {
1403 // again use some internal
1404 // knowledge
1405 for (Triangulation<3>::cell_iterator cell = tria.begin();
1406 cell != tria.end();
1407 ++cell)
1408 {
1409 Assert(cell->face(5)->at_boundary(), ExcInternalError());
1410 cell->face(5)->set_all_boundary_ids(1);
1411 }
1412 }
1413 else if (tria.n_cells() == 96)
1414 {
1415 // the 96-cell hypershell is based on a once refined 12-cell
1416 // mesh. consequently, since the outer faces all are face_no==5
1417 // above, so they are here (unless they are in the interior). Use
1418 // this to assign boundary indicators, but also make sure that we
1419 // encounter exactly 48 such faces
1420 unsigned int count = 0;
1421 for (const auto &cell : tria.cell_iterators())
1422 if (cell->face(5)->at_boundary())
1423 {
1424 cell->face(5)->set_all_boundary_ids(1);
1425 ++count;
1426 }
1427 Assert(count == 48, ExcInternalError());
1428 }
1429 else
1431 }
1432
1433
1434
1440 void
1441 colorize_quarter_hyper_shell(Triangulation<3> &tria,
1442 const Point<3> &center,
1443 const double inner_radius,
1444 const double outer_radius)
1445 {
1446 if (tria.n_cells() != 3)
1448
1449 const double middle_radius =
1450 (outer_radius - inner_radius) / 2e0 + inner_radius;
1451 const double eps = 1e-3 * middle_radius;
1452
1453 for (const auto &cell : tria.cell_iterators())
1454 for (const unsigned int f : cell->face_indices())
1455 {
1456 const auto face = cell->face(f);
1457 if (!face->at_boundary())
1458 continue;
1459
1460 const double face_center_radius =
1461 (face->center(true) - center).norm();
1462
1463 if (std::fabs(face->center()[0]) < eps) // x = 0 set boundary 2
1464 {
1465 face->set_boundary_id(2);
1466 for (const unsigned int line_no : face->line_indices())
1467 if (face->line(line_no)->at_boundary())
1468 if (std::fabs(face->line(line_no)->vertex(0).norm() -
1469 face->line(line_no)->vertex(1).norm()) > eps)
1470 face->line(line_no)->set_boundary_id(2);
1471 }
1472 else if (std::fabs(face->center()[1]) < eps) // y = 0 set boundary 3
1473 {
1474 face->set_boundary_id(3);
1475 for (const unsigned int line_no : face->line_indices())
1476 if (face->line(line_no)->at_boundary())
1477 if (std::fabs(face->line(line_no)->vertex(0).norm() -
1478 face->line(line_no)->vertex(1).norm()) > eps)
1479 face->line(line_no)->set_boundary_id(3);
1480 }
1481 else if (std::fabs(face->center()[2]) < eps) // z = 0 set boundary 4
1482 {
1483 face->set_boundary_id(4);
1484 for (const unsigned int line_no : face->line_indices())
1485 if (face->line(line_no)->at_boundary())
1486 if (std::fabs(face->line(line_no)->vertex(0).norm() -
1487 face->line(line_no)->vertex(1).norm()) > eps)
1488 face->line(line_no)->set_boundary_id(4);
1489 }
1490 else if (face_center_radius <
1491 middle_radius) // inner radius set boundary 0
1492 {
1493 face->set_boundary_id(0);
1494 for (const unsigned int line_no : face->line_indices())
1495 if (face->line(line_no)->at_boundary())
1496 if (std::fabs(face->line(line_no)->vertex(0).norm() -
1497 face->line(line_no)->vertex(1).norm()) < eps)
1498 face->line(line_no)->set_boundary_id(0);
1499 }
1500 else if (face_center_radius >
1501 middle_radius) // outer radius set boundary 1
1502 {
1503 face->set_boundary_id(1);
1504 for (const unsigned int line_no : face->line_indices())
1505 if (face->line(line_no)->at_boundary())
1506 if (std::fabs(face->line(line_no)->vertex(0).norm() -
1507 face->line(line_no)->vertex(1).norm()) < eps)
1508 face->line(line_no)->set_boundary_id(1);
1509 }
1510 else
1512 }
1513 }
1514
1515 } // namespace
1516
1517
1518 template <int dim, int spacedim>
1519 void
1521 const Point<dim> &p_1,
1522 const Point<dim> &p_2,
1523 const bool colorize)
1524 {
1525 // First, extend dimensions from dim to spacedim and
1526 // normalize such that p1 is lower in all coordinate
1527 // directions. Additional entries will be 0.
1528 Point<spacedim> p1, p2;
1529 for (unsigned int i = 0; i < dim; ++i)
1530 {
1531 p1[i] = std::min(p_1[i], p_2[i]);
1532 p2[i] = std::max(p_1[i], p_2[i]);
1533 }
1534
1535 std::vector<Point<spacedim>> vertices(
1536 ReferenceCells::get_hypercube<dim>().n_vertices());
1537 switch (dim)
1538 {
1539 case 1:
1540 vertices[0] = p1;
1541 vertices[1] = p2;
1542 break;
1543 case 2:
1544 vertices[0] = vertices[1] = p1;
1545 vertices[2] = vertices[3] = p2;
1546
1547 vertices[1][0] = p2[0];
1548 vertices[2][0] = p1[0];
1549 break;
1550 case 3:
1551 vertices[0] = vertices[1] = vertices[2] = vertices[3] = p1;
1552 vertices[4] = vertices[5] = vertices[6] = vertices[7] = p2;
1553
1554 vertices[1][0] = p2[0];
1555 vertices[2][1] = p2[1];
1556 vertices[3][0] = p2[0];
1557 vertices[3][1] = p2[1];
1558
1559 vertices[4][0] = p1[0];
1560 vertices[4][1] = p1[1];
1561 vertices[5][1] = p1[1];
1562 vertices[6][0] = p1[0];
1563
1564 break;
1565 default:
1567 }
1568
1569 // Prepare cell data
1570 std::vector<CellData<dim>> cells(1);
1571 for (const unsigned int i :
1572 ReferenceCells::get_hypercube<dim>().vertex_indices())
1573 cells[0].vertices[i] = i;
1574 cells[0].material_id = 0;
1575
1576 tria.create_triangulation(vertices, cells, SubCellData());
1577
1578 // Assign boundary indicators
1579 if (colorize)
1580 colorize_hyper_rectangle(tria);
1581 }
1582
1583
1584
1585 template <int dim, int spacedim>
1586 void
1588 const double left,
1589 const double right,
1590 const bool colorize)
1591 {
1592 Assert(left < right,
1593 ExcMessage("Invalid left-to-right bounds of hypercube"));
1594
1595 Point<dim> p1, p2;
1596 for (unsigned int i = 0; i < dim; ++i)
1597 {
1598 p1[i] = left;
1599 p2[i] = right;
1600 }
1601 hyper_rectangle(tria, p1, p2, colorize);
1602 }
1603
1604
1605
1606 template <int dim>
1607 void
1608 simplex(Triangulation<dim> &tria, const std::vector<Point<dim>> &vertices)
1609 {
1610 AssertDimension(vertices.size(), dim + 1);
1611 Assert(dim > 1, ExcNotImplemented());
1612 Assert(dim < 4, ExcNotImplemented());
1613
1614 if constexpr (running_in_debug_mode())
1615 {
1616 Tensor<2, dim> vector_matrix;
1617 for (unsigned int d = 0; d < dim; ++d)
1618 for (unsigned int c = 1; c <= dim; ++c)
1619 vector_matrix[c - 1][d] = vertices[c][d] - vertices[0][d];
1620 Assert(determinant(vector_matrix) > 0.,
1621 ExcMessage(
1622 "Vertices of simplex must form a right handed system"));
1623 }
1624
1625 // Set up the vertices by first copying into points.
1626 std::vector<Point<dim>> points = vertices;
1627 Point<dim> center;
1628 // Compute the edge midpoints and add up everything to compute the
1629 // center point.
1630 for (unsigned int i = 0; i <= dim; ++i)
1631 {
1632 points.push_back(0.5 * (points[i] + points[(i + 1) % (dim + 1)]));
1633 center += points[i];
1634 }
1635 if (dim > 2)
1636 {
1637 // In 3d, we have some more edges to deal with
1638 for (unsigned int i = 1; i < dim; ++i)
1639 points.push_back(0.5 * (points[i - 1] + points[i + 1]));
1640 // And we need face midpoints
1641 for (unsigned int i = 0; i <= dim; ++i)
1642 points.push_back(1. / 3. *
1643 (points[i] + points[(i + 1) % (dim + 1)] +
1644 points[(i + 2) % (dim + 1)]));
1645 }
1646 points.push_back((1. / (dim + 1)) * center);
1647
1648 std::vector<CellData<dim>> cells(dim + 1);
1649 switch (dim)
1650 {
1651 case 2:
1652 AssertDimension(points.size(), 7);
1653 cells[0].vertices[0] = 0;
1654 cells[0].vertices[1] = 3;
1655 cells[0].vertices[2] = 5;
1656 cells[0].vertices[3] = 6;
1657 cells[0].material_id = 0;
1658
1659 cells[1].vertices[0] = 3;
1660 cells[1].vertices[1] = 1;
1661 cells[1].vertices[2] = 6;
1662 cells[1].vertices[3] = 4;
1663 cells[1].material_id = 0;
1664
1665 cells[2].vertices[0] = 5;
1666 cells[2].vertices[1] = 6;
1667 cells[2].vertices[2] = 2;
1668 cells[2].vertices[3] = 4;
1669 cells[2].material_id = 0;
1670 break;
1671 case 3:
1672 AssertDimension(points.size(), 15);
1673 cells[0].vertices[0] = 0;
1674 cells[0].vertices[1] = 4;
1675 cells[0].vertices[2] = 8;
1676 cells[0].vertices[3] = 10;
1677 cells[0].vertices[4] = 7;
1678 cells[0].vertices[5] = 13;
1679 cells[0].vertices[6] = 12;
1680 cells[0].vertices[7] = 14;
1681 cells[0].material_id = 0;
1682
1683 cells[1].vertices[0] = 4;
1684 cells[1].vertices[1] = 1;
1685 cells[1].vertices[2] = 10;
1686 cells[1].vertices[3] = 5;
1687 cells[1].vertices[4] = 13;
1688 cells[1].vertices[5] = 9;
1689 cells[1].vertices[6] = 14;
1690 cells[1].vertices[7] = 11;
1691 cells[1].material_id = 0;
1692
1693 cells[2].vertices[0] = 8;
1694 cells[2].vertices[1] = 10;
1695 cells[2].vertices[2] = 2;
1696 cells[2].vertices[3] = 5;
1697 cells[2].vertices[4] = 12;
1698 cells[2].vertices[5] = 14;
1699 cells[2].vertices[6] = 6;
1700 cells[2].vertices[7] = 11;
1701 cells[2].material_id = 0;
1702
1703 cells[3].vertices[0] = 7;
1704 cells[3].vertices[1] = 13;
1705 cells[3].vertices[2] = 12;
1706 cells[3].vertices[3] = 14;
1707 cells[3].vertices[4] = 3;
1708 cells[3].vertices[5] = 9;
1709 cells[3].vertices[6] = 6;
1710 cells[3].vertices[7] = 11;
1711 cells[3].material_id = 0;
1712 break;
1713 default:
1715 }
1716 tria.create_triangulation(points, cells, SubCellData());
1717 }
1718
1719
1720
1721 template <int dim, int spacedim>
1722 void
1724 const ReferenceCell<dim> &reference_cell)
1725 {
1726 AssertDimension(dim, reference_cell.get_dimension());
1727
1728 if (reference_cell == ReferenceCells::get_hypercube<dim>())
1729 {
1730 GridGenerator::hyper_cube(tria, 0, 1);
1731 }
1732 else
1733 {
1734 // Create an array that contains the vertices of the reference cell.
1735 // We can query these points from ReferenceCell, but then we have
1736 // to embed them into the spacedim-dimensional space.
1737 std::vector<Point<spacedim>> vertices(reference_cell.n_vertices());
1738 for (const unsigned int v : reference_cell.vertex_indices())
1739 {
1740 const Point<dim> this_vertex = reference_cell.vertex(v);
1741 for (unsigned int d = 0; d < dim; ++d)
1742 vertices[v][d] = this_vertex[d];
1743 // Point<spacedim> initializes everything to zero, so any remaining
1744 // elements are left at zero and we don't have to explicitly pad
1745 // from 'dim' to 'spacedim' here.
1746 }
1747
1748 // Then make one cell out of these vertices. They are ordered correctly
1749 // already, so we just need to enumerate them
1750 std::vector<CellData<dim>> cells(1);
1751 cells[0].vertices.resize(reference_cell.n_vertices());
1752 for (const unsigned int v : reference_cell.vertex_indices())
1753 cells[0].vertices[v] = v;
1754
1755 // Turn all of this into a triangulation
1756 tria.create_triangulation(vertices, cells, {});
1757 }
1758 }
1759
1760 void
1762 const unsigned int n_cells,
1763 const unsigned int n_rotations,
1764 const double R,
1765 const double r)
1766 {
1767 const unsigned int dim = 3;
1768 Assert(n_cells > 4,
1769 ExcMessage(
1770 "More than 4 cells are needed to create a moebius grid."));
1771 Assert(r > 0 && R > 0,
1772 ExcMessage("Outer and inner radius must be positive."));
1773 Assert(R > r,
1774 ExcMessage("Outer radius must be greater than inner radius."));
1775
1776
1777 std::vector<Point<dim>> vertices(4 * n_cells);
1778 double beta_step = n_rotations * numbers::PI / 2.0 / n_cells;
1779 double alpha_step = 2.0 * numbers::PI / n_cells;
1780
1781 for (unsigned int i = 0; i < n_cells; ++i)
1782 for (unsigned int j = 0; j < 4; ++j)
1783 {
1784 vertices[4 * i + j][0] =
1785 R * std::cos(i * alpha_step) +
1786 r * std::cos(i * beta_step + j * numbers::PI / 2.0) *
1787 std::cos(i * alpha_step);
1788 vertices[4 * i + j][1] =
1789 R * std::sin(i * alpha_step) +
1790 r * std::cos(i * beta_step + j * numbers::PI / 2.0) *
1791 std::sin(i * alpha_step);
1792 vertices[4 * i + j][2] =
1793 r * std::sin(i * beta_step + j * numbers::PI / 2.0);
1794 }
1795
1796 unsigned int offset = 0;
1797
1798 // This Triangulation is constructed using a numbering scheme in which
1799 // the front face is first and the back face is second,
1800 // which is more convenient for creating a Moebius loop
1801 static constexpr std::array<unsigned int, 8> local_vertex_numbering{
1802 {0, 1, 5, 4, 2, 3, 7, 6}};
1803 std::vector<CellData<dim>> cells(n_cells);
1804 for (unsigned int i = 0; i < n_cells; ++i)
1805 {
1806 for (unsigned int j = 0; j < 2; ++j)
1807 {
1808 cells[i].vertices[local_vertex_numbering[0 + 4 * j]] =
1809 offset + 0 + 4 * j;
1810 cells[i].vertices[local_vertex_numbering[1 + 4 * j]] =
1811 offset + 3 + 4 * j;
1812 cells[i].vertices[local_vertex_numbering[2 + 4 * j]] =
1813 offset + 2 + 4 * j;
1814 cells[i].vertices[local_vertex_numbering[3 + 4 * j]] =
1815 offset + 1 + 4 * j;
1816 }
1817 offset += 4;
1818 cells[i].material_id = 0;
1819 }
1820
1821 // now correct the last four vertices
1822 cells[n_cells - 1].vertices[local_vertex_numbering[4]] =
1823 (0 + n_rotations) % 4;
1824 cells[n_cells - 1].vertices[local_vertex_numbering[5]] =
1825 (3 + n_rotations) % 4;
1826 cells[n_cells - 1].vertices[local_vertex_numbering[6]] =
1827 (2 + n_rotations) % 4;
1828 cells[n_cells - 1].vertices[local_vertex_numbering[7]] =
1829 (1 + n_rotations) % 4;
1830
1832 tria.create_triangulation(vertices, cells, SubCellData());
1833 }
1834
1835
1836
1837 template <>
1838 void
1839 torus<2, 3>(Triangulation<2, 3> &tria,
1840 const double centerline_radius,
1841 const double inner_radius,
1842 const unsigned int,
1843 const double)
1844 {
1845 Assert(centerline_radius > inner_radius,
1846 ExcMessage("The centerline radius must be greater than the "
1847 "inner radius."));
1848 Assert(inner_radius > 0.0,
1849 ExcMessage("The inner radius must be positive."));
1850
1851 const unsigned int dim = 2;
1852 const unsigned int spacedim = 3;
1853 std::vector<Point<spacedim>> vertices(16);
1854
1855 vertices[0] = Point<spacedim>(centerline_radius - inner_radius, 0, 0);
1856 vertices[1] = Point<spacedim>(centerline_radius, -inner_radius, 0);
1857 vertices[2] = Point<spacedim>(centerline_radius + inner_radius, 0, 0);
1858 vertices[3] = Point<spacedim>(centerline_radius, inner_radius, 0);
1859 vertices[4] = Point<spacedim>(0, 0, centerline_radius - inner_radius);
1860 vertices[5] = Point<spacedim>(0, -inner_radius, centerline_radius);
1861 vertices[6] = Point<spacedim>(0, 0, centerline_radius + inner_radius);
1862 vertices[7] = Point<spacedim>(0, inner_radius, centerline_radius);
1863 vertices[8] = Point<spacedim>(-(centerline_radius - inner_radius), 0, 0);
1864 vertices[9] = Point<spacedim>(-centerline_radius, -inner_radius, 0);
1865 vertices[10] = Point<spacedim>(-(centerline_radius + inner_radius), 0, 0);
1866 vertices[11] = Point<spacedim>(-centerline_radius, inner_radius, 0);
1867 vertices[12] = Point<spacedim>(0, 0, -(centerline_radius - inner_radius));
1868 vertices[13] = Point<spacedim>(0, -inner_radius, -centerline_radius);
1869 vertices[14] = Point<spacedim>(0, 0, -(centerline_radius + inner_radius));
1870 vertices[15] = Point<spacedim>(0, inner_radius, -centerline_radius);
1871
1872 std::vector<CellData<dim>> cells(16);
1873 // Right Hand Orientation
1874 cells[0].vertices[0] = 0;
1875 cells[0].vertices[1] = 4;
1876 cells[0].vertices[2] = 3;
1877 cells[0].vertices[3] = 7;
1878 cells[0].material_id = 0;
1879
1880 cells[1].vertices[0] = 1;
1881 cells[1].vertices[1] = 5;
1882 cells[1].vertices[2] = 0;
1883 cells[1].vertices[3] = 4;
1884 cells[1].material_id = 0;
1885
1886 cells[2].vertices[0] = 2;
1887 cells[2].vertices[1] = 6;
1888 cells[2].vertices[2] = 1;
1889 cells[2].vertices[3] = 5;
1890 cells[2].material_id = 0;
1891
1892 cells[3].vertices[0] = 3;
1893 cells[3].vertices[1] = 7;
1894 cells[3].vertices[2] = 2;
1895 cells[3].vertices[3] = 6;
1896 cells[3].material_id = 0;
1897
1898 cells[4].vertices[0] = 4;
1899 cells[4].vertices[1] = 8;
1900 cells[4].vertices[2] = 7;
1901 cells[4].vertices[3] = 11;
1902 cells[4].material_id = 0;
1903
1904 cells[5].vertices[0] = 5;
1905 cells[5].vertices[1] = 9;
1906 cells[5].vertices[2] = 4;
1907 cells[5].vertices[3] = 8;
1908 cells[5].material_id = 0;
1909
1910 cells[6].vertices[0] = 6;
1911 cells[6].vertices[1] = 10;
1912 cells[6].vertices[2] = 5;
1913 cells[6].vertices[3] = 9;
1914 cells[6].material_id = 0;
1915
1916 cells[7].vertices[0] = 7;
1917 cells[7].vertices[1] = 11;
1918 cells[7].vertices[2] = 6;
1919 cells[7].vertices[3] = 10;
1920 cells[7].material_id = 0;
1921
1922 cells[8].vertices[0] = 8;
1923 cells[8].vertices[1] = 12;
1924 cells[8].vertices[2] = 11;
1925 cells[8].vertices[3] = 15;
1926 cells[8].material_id = 0;
1927
1928 cells[9].vertices[0] = 9;
1929 cells[9].vertices[1] = 13;
1930 cells[9].vertices[2] = 8;
1931 cells[9].vertices[3] = 12;
1932 cells[9].material_id = 0;
1933
1934 cells[10].vertices[0] = 10;
1935 cells[10].vertices[1] = 14;
1936 cells[10].vertices[2] = 9;
1937 cells[10].vertices[3] = 13;
1938 cells[10].material_id = 0;
1939
1940 cells[11].vertices[0] = 11;
1941 cells[11].vertices[1] = 15;
1942 cells[11].vertices[2] = 10;
1943 cells[11].vertices[3] = 14;
1944 cells[11].material_id = 0;
1945
1946 cells[12].vertices[0] = 12;
1947 cells[12].vertices[1] = 0;
1948 cells[12].vertices[2] = 15;
1949 cells[12].vertices[3] = 3;
1950 cells[12].material_id = 0;
1951
1952 cells[13].vertices[0] = 13;
1953 cells[13].vertices[1] = 1;
1954 cells[13].vertices[2] = 12;
1955 cells[13].vertices[3] = 0;
1956 cells[13].material_id = 0;
1957
1958 cells[14].vertices[0] = 14;
1959 cells[14].vertices[1] = 2;
1960 cells[14].vertices[2] = 13;
1961 cells[14].vertices[3] = 1;
1962 cells[14].material_id = 0;
1963
1964 cells[15].vertices[0] = 15;
1965 cells[15].vertices[1] = 3;
1966 cells[15].vertices[2] = 14;
1967 cells[15].vertices[3] = 2;
1968 cells[15].material_id = 0;
1969
1971 tria.create_triangulation(vertices, cells, SubCellData());
1972
1973 tria.set_all_manifold_ids(0);
1974 tria.set_manifold(0, TorusManifold<2>(centerline_radius, inner_radius));
1975 }
1976
1977
1978
1979 namespace
1980 {
1981 static constexpr int circle_cell_vertices[5][4] = {{0, 1, 2, 3},
1982 {0, 2, 6, 4},
1983 {2, 3, 4, 5},
1984 {1, 7, 3, 5},
1985 {6, 4, 7, 5}};
1986 }
1987
1988
1989
1990 template <>
1991 void
1992 torus<3, 3>(Triangulation<3, 3> &tria,
1993 const double centerline_radius,
1994 const double inner_radius,
1995 const unsigned int n_cells_toroidal,
1996 const double phi)
1997 {
1998 Assert(centerline_radius > inner_radius,
1999 ExcMessage("The centerline radius must be greater than the "
2000 "inner radius."));
2001 Assert(inner_radius > 0.0,
2002 ExcMessage("The inner radius must be positive."));
2003 Assert(n_cells_toroidal > static_cast<unsigned int>(phi / numbers::PI),
2004 ExcMessage("Number of cells in toroidal direction has "
2005 "to be at least 3 for a torus of polar extent 2*pi."));
2006 AssertThrow(phi > 0.0 && phi < 2.0 * numbers::PI + 1.0e-15,
2007 ExcMessage("Invalid angle phi specified."));
2008
2009 // the first 8 vertices are in the x-y-plane
2010 const Point<3> p = Point<3>(centerline_radius, 0.0, 0.0);
2011 const double a = 1. / (1 + std::sqrt(2.0));
2012 // A value of 1 indicates "open" torus with angle < 2*pi, which
2013 // means that we need an additional layer of vertices
2014 const unsigned int additional_layer =
2015 (phi < 2.0 * numbers::PI - 1.0e-15) ?
2016 1 :
2017 0; // torus is closed (angle of 2*pi)
2018 const unsigned int n_point_layers_toroidal =
2019 n_cells_toroidal + additional_layer;
2020 std::vector<Point<3>> vertices(8 * n_point_layers_toroidal);
2021 vertices[0] = p + Point<3>(-1, -1, 0) * (inner_radius / std::sqrt(2.0)),
2022 vertices[1] = p + Point<3>(+1, -1, 0) * (inner_radius / std::sqrt(2.0)),
2023 vertices[2] = p + Point<3>(-1, -1, 0) * (inner_radius / std::sqrt(2.0) * a),
2024 vertices[3] = p + Point<3>(+1, -1, 0) * (inner_radius / std::sqrt(2.0) * a),
2025 vertices[4] = p + Point<3>(-1, +1, 0) * (inner_radius / std::sqrt(2.0) * a),
2026 vertices[5] = p + Point<3>(+1, +1, 0) * (inner_radius / std::sqrt(2.0) * a),
2027 vertices[6] = p + Point<3>(-1, +1, 0) * (inner_radius / std::sqrt(2.0)),
2028 vertices[7] = p + Point<3>(+1, +1, 0) * (inner_radius / std::sqrt(2.0));
2029
2030 // create remaining vertices by rotating around negative y-axis (the
2031 // direction is to ensure positive cell measures)
2032 const double phi_cell = phi / n_cells_toroidal;
2033 for (unsigned int c = 1; c < n_point_layers_toroidal; ++c)
2034 {
2035 for (unsigned int v = 0; v < 8; ++v)
2036 {
2037 const double inner_radius_2d = vertices[v][0];
2038 vertices[8 * c + v][0] = inner_radius_2d * std::cos(phi_cell * c);
2039 vertices[8 * c + v][1] = vertices[v][1];
2040 vertices[8 * c + v][2] = inner_radius_2d * std::sin(phi_cell * c);
2041 }
2042 }
2043
2044 // cell connectivity
2045 std::vector<CellData<3>> cells(5 * n_cells_toroidal);
2046 for (unsigned int c = 0; c < n_cells_toroidal; ++c)
2047 {
2048 for (unsigned int j = 0; j < 2; ++j)
2049 {
2050 const unsigned int offset =
2051 (8 * (c + j)) % (8 * n_point_layers_toroidal);
2052
2053 // cells in x-y-plane
2054 for (unsigned int c2 = 0; c2 < 5; ++c2)
2055 for (unsigned int i = 0; i < 4; ++i)
2056 cells[5 * c + c2].vertices[i + j * 4] =
2057 offset + circle_cell_vertices[c2][i];
2058 }
2059
2060 cells[5 * c].material_id = 0;
2061 // mark cell on torus centerline
2062 cells[5 * c + 1].material_id = 0;
2063 cells[5 * c + 2].material_id = 1;
2064 cells[5 * c + 3].material_id = 0;
2065 cells[5 * c + 4].material_id = 0;
2066 }
2067
2068 tria.create_triangulation(vertices, cells, SubCellData());
2069
2070 tria.reset_all_manifolds();
2071 tria.set_all_manifold_ids(0);
2072
2073 for (const auto &cell : tria.cell_iterators())
2074 {
2075 // identify faces on torus surface and set manifold to 1
2076 for (const unsigned int f : cell->face_indices())
2077 {
2078 // This is a hex mesh, so faces 4 and 5 are those with
2079 // normal vector aligned with torus centerline
2080 if (cell->face(f)->at_boundary() && f != 4 && f != 5)
2081 {
2082 cell->face(f)->set_all_manifold_ids(1);
2083 }
2084 }
2085
2086 // set manifold id to 2 for those cells that are on the torus centerline
2087 if (cell->material_id() == 1)
2088 {
2089 cell->set_all_manifold_ids(2);
2090 // reset to 0
2091 cell->set_material_id(0);
2092 }
2093 }
2094
2095 tria.set_manifold(1, TorusManifold<3>(centerline_radius, inner_radius));
2096 tria.set_manifold(2,
2098 Point<3>()));
2099
2100 tria.set_manifold(0, FlatManifold<3>());
2102 transfinite.initialize(tria);
2103 tria.set_manifold(0, transfinite);
2104 }
2105
2106
2107
2108 template <int dim, int spacedim>
2109 void
2111 const std::vector<Point<spacedim>> &vertices,
2112 const bool colorize)
2113 {
2114 Assert(vertices.size() == ReferenceCells::get_hypercube<dim>().n_vertices(),
2115 ExcMessage("Wrong number of vertices."));
2116
2117 // First create a hyper_rectangle and then deform it.
2118 hyper_cube(tria, 0, 1, colorize);
2119
2120 // Then move the vertices of that one cell as requested:
2122 tria.begin_active();
2123 for (const unsigned int i : cell->vertex_indices())
2124 cell->vertex(i) = vertices[i];
2125
2126 // Check that the order of the vertices makes sense, i.e., the volume of the
2127 // cell is positive.
2129 ExcMessage(
2130 "The volume of the cell is not greater than zero. "
2131 "This could be due to the wrong ordering of the vertices."));
2132 }
2133
2134
2135
2136 template <>
2137 void
2139 const Point<3> (& /*corners*/)[3],
2140 const bool /*colorize*/)
2141 {
2143 }
2144
2145 template <>
2146 void
2148 const Point<1> (& /*corners*/)[1],
2149 const bool /*colorize*/)
2150 {
2152 }
2153
2154 // Implementation for 2d only
2155 template <>
2156 void
2158 const Point<2> (&corners)[2],
2159 const bool colorize)
2160 {
2161 Point<2> origin;
2162 std::array<Tensor<1, 2>, 2> edges;
2163 edges[0] = corners[0];
2164 edges[1] = corners[1];
2165 std::vector<unsigned int> subdivisions;
2166 subdivided_parallelepiped<2, 2>(
2167 tria, origin, edges, subdivisions, colorize);
2168 }
2169
2170
2171
2172 template <int dim>
2173 void
2175 const Point<dim> (&corners)[dim],
2176 const bool colorize)
2177 {
2178 unsigned int n_subdivisions[dim];
2179 for (unsigned int i = 0; i < dim; ++i)
2180 n_subdivisions[i] = 1;
2181
2182 // and call the function below
2183 subdivided_parallelepiped(tria, n_subdivisions, corners, colorize);
2184 }
2185
2186 template <int dim>
2187 void
2189 const unsigned int n_subdivisions,
2190 const Point<dim> (&corners)[dim],
2191 const bool colorize)
2192 {
2193 // Equalize number of subdivisions in each dim-direction, their
2194 // validity will be checked later
2195 unsigned int n_subdivisions_[dim];
2196 for (unsigned int i = 0; i < dim; ++i)
2197 n_subdivisions_[i] = n_subdivisions;
2198
2199 // and call the function below
2200 subdivided_parallelepiped(tria, n_subdivisions_, corners, colorize);
2201 }
2202
2203 template <int dim>
2204 void
2206# ifndef _MSC_VER
2207 const unsigned int (&n_subdivisions)[dim],
2208# else
2209 const unsigned int *n_subdivisions,
2210# endif
2211 const Point<dim> (&corners)[dim],
2212 const bool colorize)
2213 {
2214 Point<dim> origin;
2215 std::vector<unsigned int> subdivisions;
2216 std::array<Tensor<1, dim>, dim> edges;
2217 for (unsigned int i = 0; i < dim; ++i)
2218 {
2219 subdivisions.push_back(n_subdivisions[i]);
2220 edges[i] = corners[i];
2221 }
2222
2223 subdivided_parallelepiped<dim, dim>(
2224 tria, origin, edges, subdivisions, colorize);
2225 }
2226
2227 // Parallelepiped implementation in 1d, 2d, and 3d. @note The
2228 // implementation in 1d is similar to hyper_rectangle(), and in 2d is
2229 // similar to parallelogram().
2230 template <int dim, int spacedim>
2231 void
2233 const Point<spacedim> &origin,
2234 const std::array<Tensor<1, spacedim>, dim> &edges,
2235 const std::vector<unsigned int> &subdivisions,
2236 const bool colorize)
2237 {
2238 std::vector<unsigned int> compute_subdivisions = subdivisions;
2239 if (compute_subdivisions.empty())
2240 {
2241 compute_subdivisions.resize(dim, 1);
2242 }
2243
2244 Assert(compute_subdivisions.size() == dim,
2245 ExcMessage("One subdivision must be provided for each dimension."));
2246 // check subdivisions
2247 for (unsigned int i = 0; i < dim; ++i)
2248 {
2249 Assert(compute_subdivisions[i] > 0,
2250 ExcInvalidRepetitions(subdivisions[i]));
2251 Assert(
2252 edges[i].norm() > 0,
2253 ExcMessage(
2254 "Edges in subdivided_parallelepiped() must not be degenerate."));
2255 }
2256
2257 /*
2258 * Verify that the edge points to the right in 1d, vectors are oriented in
2259 * a counter clockwise direction in 2d, or form a right handed system in
2260 * 3d.
2261 */
2262 bool twisted_data = false;
2263 switch (dim)
2264 {
2265 case 1:
2266 {
2267 twisted_data = (edges[0][0] < 0);
2268 break;
2269 }
2270 case 2:
2271 {
2272 if (spacedim == 2) // this check does not make sense otherwise
2273 {
2274 const double plane_normal =
2275 edges[0][0] * edges[1][1] - edges[0][1] * edges[1][0];
2276 twisted_data = (plane_normal < 0.0);
2277 }
2278 break;
2279 }
2280 case 3:
2281 {
2282 // Check that the first two vectors are not linear combinations to
2283 // avoid zero division later on.
2284 Assert(std::abs(edges[0] * edges[1] /
2285 (edges[0].norm() * edges[1].norm()) -
2286 1.0) > 1.0e-15,
2287 ExcMessage(
2288 "Edges in subdivided_parallelepiped() must point in"
2289 " different directions."));
2290 const Tensor<1, spacedim> plane_normal =
2291 cross_product_3d(edges[0], edges[1]);
2292
2293 /*
2294 * Ensure that edges 1, 2, and 3 form a right-handed set of
2295 * vectors. This works by applying the definition of the dot product
2296 *
2297 * cos(theta) = dot(x, y)/(norm(x)*norm(y))
2298 *
2299 * and then, since the normal vector and third edge should both
2300 * point away from the plane formed by the first two edges, the
2301 * angle between them must be between 0 and pi/2; hence we just need
2302 *
2303 * 0 < dot(x, y).
2304 */
2305 twisted_data = (plane_normal * edges[2] < 0.0);
2306 break;
2307 }
2308 default:
2310 }
2311 Assert(
2312 !twisted_data,
2313 ExcInvalidInputOrientation(
2314 "The triangulation you are trying to create will consist of cells"
2315 " with negative measures. This is usually the result of input data"
2316 " that does not define a right-handed coordinate system. The usual"
2317 " fix for this is to ensure that in 1d the given point is to the"
2318 " right of the origin (or the given edge tensor is positive), in 2d"
2319 " that the two edges (and their cross product) obey the right-hand"
2320 " rule (which may usually be done by switching the order of the"
2321 " points or edge tensors), or in 3d that the edges form a"
2322 " right-handed coordinate system (which may also be accomplished by"
2323 " switching the order of the first two points or edge tensors)."));
2324
2325 // Check corners do not overlap (unique)
2326 for (unsigned int i = 0; i < dim; ++i)
2327 for (unsigned int j = i + 1; j < dim; ++j)
2328 Assert((edges[i] != edges[j]),
2329 ExcMessage(
2330 "Degenerate edges of subdivided_parallelepiped encountered."));
2331
2332 // Create a list of points
2333 std::vector<Point<spacedim>> points;
2334
2335 switch (dim)
2336 {
2337 case 1:
2338 for (unsigned int x = 0; x <= compute_subdivisions[0]; ++x)
2339 points.push_back(origin + edges[0] / compute_subdivisions[0] * x);
2340 break;
2341
2342 case 2:
2343 for (unsigned int y = 0; y <= compute_subdivisions[1]; ++y)
2344 for (unsigned int x = 0; x <= compute_subdivisions[0]; ++x)
2345 points.push_back(origin + edges[0] / compute_subdivisions[0] * x +
2346 edges[1] / compute_subdivisions[1] * y);
2347 break;
2348
2349 case 3:
2350 for (unsigned int z = 0; z <= compute_subdivisions[2]; ++z)
2351 for (unsigned int y = 0; y <= compute_subdivisions[1]; ++y)
2352 for (unsigned int x = 0; x <= compute_subdivisions[0]; ++x)
2353 points.push_back(origin +
2354 edges[0] / compute_subdivisions[0] * x +
2355 edges[1] / compute_subdivisions[1] * y +
2356 edges[2] / compute_subdivisions[2] * z);
2357 break;
2358
2359 default:
2361 }
2362
2363 // Prepare cell data
2364 unsigned int n_cells = 1;
2365 for (unsigned int i = 0; i < dim; ++i)
2366 n_cells *= compute_subdivisions[i];
2367 std::vector<CellData<dim>> cells(n_cells);
2368
2369 // Create fixed ordering of
2370 switch (dim)
2371 {
2372 case 1:
2373 for (unsigned int x = 0; x < compute_subdivisions[0]; ++x)
2374 {
2375 cells[x].vertices[0] = x;
2376 cells[x].vertices[1] = x + 1;
2377
2378 // wipe material id
2379 cells[x].material_id = 0;
2380 }
2381 break;
2382
2383 case 2:
2384 {
2385 // Shorthand
2386 const unsigned int n_dy = compute_subdivisions[1];
2387 const unsigned int n_dx = compute_subdivisions[0];
2388
2389 for (unsigned int y = 0; y < n_dy; ++y)
2390 for (unsigned int x = 0; x < n_dx; ++x)
2391 {
2392 const unsigned int c = y * n_dx + x;
2393 cells[c].vertices[0] = y * (n_dx + 1) + x;
2394 cells[c].vertices[1] = y * (n_dx + 1) + x + 1;
2395 cells[c].vertices[2] = (y + 1) * (n_dx + 1) + x;
2396 cells[c].vertices[3] = (y + 1) * (n_dx + 1) + x + 1;
2397
2398 // wipe material id
2399 cells[c].material_id = 0;
2400 }
2401 }
2402 break;
2403
2404 case 3:
2405 {
2406 // Shorthand
2407 const unsigned int n_dz = compute_subdivisions[2];
2408 const unsigned int n_dy = compute_subdivisions[1];
2409 const unsigned int n_dx = compute_subdivisions[0];
2410
2411 for (unsigned int z = 0; z < n_dz; ++z)
2412 for (unsigned int y = 0; y < n_dy; ++y)
2413 for (unsigned int x = 0; x < n_dx; ++x)
2414 {
2415 const unsigned int c = z * n_dy * n_dx + y * n_dx + x;
2416
2417 cells[c].vertices[0] =
2418 z * (n_dy + 1) * (n_dx + 1) + y * (n_dx + 1) + x;
2419 cells[c].vertices[1] =
2420 z * (n_dy + 1) * (n_dx + 1) + y * (n_dx + 1) + x + 1;
2421 cells[c].vertices[2] =
2422 z * (n_dy + 1) * (n_dx + 1) + (y + 1) * (n_dx + 1) + x;
2423 cells[c].vertices[3] = z * (n_dy + 1) * (n_dx + 1) +
2424 (y + 1) * (n_dx + 1) + x + 1;
2425 cells[c].vertices[4] =
2426 (z + 1) * (n_dy + 1) * (n_dx + 1) + y * (n_dx + 1) + x;
2427 cells[c].vertices[5] = (z + 1) * (n_dy + 1) * (n_dx + 1) +
2428 y * (n_dx + 1) + x + 1;
2429 cells[c].vertices[6] = (z + 1) * (n_dy + 1) * (n_dx + 1) +
2430 (y + 1) * (n_dx + 1) + x;
2431 cells[c].vertices[7] = (z + 1) * (n_dy + 1) * (n_dx + 1) +
2432 (y + 1) * (n_dx + 1) + x + 1;
2433
2434 // wipe material id
2435 cells[c].material_id = 0;
2436 }
2437 break;
2438 }
2439
2440 default:
2442 }
2443
2444 // Create triangulation
2445 // reorder the cells to ensure that they satisfy the convention for
2446 // edge and face directions
2448 tria.create_triangulation(points, cells, SubCellData());
2449
2450 // Finally assign boundary indicators according to hyper_rectangle
2451 if (colorize)
2452 {
2453 for (const auto &cell : tria.active_cell_iterators())
2454 for (const unsigned int face : cell->face_indices())
2455 {
2456 if (cell->face(face)->at_boundary())
2457 cell->face(face)->set_boundary_id(face);
2458 }
2459 }
2460 }
2461
2462
2463 template <int dim, int spacedim>
2464 void
2466 const unsigned int repetitions,
2467 const double left,
2468 const double right,
2469 const bool colorize)
2470 {
2471 Assert(repetitions >= 1, ExcInvalidRepetitions(repetitions));
2472 Assert(left < right,
2473 ExcMessage("Invalid left-to-right bounds of hypercube"));
2474
2475 Point<dim> p0, p1;
2476 for (unsigned int i = 0; i < dim; ++i)
2477 {
2478 p0[i] = left;
2479 p1[i] = right;
2480 }
2481
2482 std::vector<unsigned int> reps(dim, repetitions);
2483 subdivided_hyper_rectangle(tria, reps, p0, p1, colorize);
2484 }
2485
2486
2487
2488 template <int dim, int spacedim>
2489 void
2491 const std::vector<unsigned int> &repetitions,
2492 const Point<dim> &p_1,
2493 const Point<dim> &p_2,
2494 const bool colorize)
2495 {
2496 Assert(repetitions.size() == dim, ExcInvalidRepetitionsDimension(dim));
2497
2498 // First, extend dimensions from dim to spacedim and
2499 // normalize such that p1 is lower in all coordinate
2500 // directions. Additional entries will be 0.
2501 Point<spacedim> p1, p2;
2502 for (unsigned int i = 0; i < dim; ++i)
2503 {
2504 p1[i] = std::min(p_1[i], p_2[i]);
2505 p2[i] = std::max(p_1[i], p_2[i]);
2506 }
2507
2508 // calculate deltas and validate input
2509 std::array<Point<spacedim>, dim> delta;
2510 for (unsigned int i = 0; i < dim; ++i)
2511 {
2512 Assert(repetitions[i] >= 1, ExcInvalidRepetitions(repetitions[i]));
2513
2514 delta[i][i] = (p2[i] - p1[i]) / repetitions[i];
2515 Assert(
2516 delta[i][i] > 0.0,
2517 ExcMessage(
2518 "The first dim entries of coordinates of p1 and p2 need to be different."));
2519 }
2520
2521 // then generate the points
2522 std::vector<Point<spacedim>> points;
2523 switch (dim)
2524 {
2525 case 1:
2526 for (unsigned int x = 0; x <= repetitions[0]; ++x)
2527 points.push_back(p1 + x * delta[0]);
2528 break;
2529
2530 case 2:
2531 for (unsigned int y = 0; y <= repetitions[1]; ++y)
2532 for (unsigned int x = 0; x <= repetitions[0]; ++x)
2533 points.push_back(p1 + x * delta[0] + y * delta[1]);
2534 break;
2535
2536 case 3:
2537 for (unsigned int z = 0; z <= repetitions[2]; ++z)
2538 for (unsigned int y = 0; y <= repetitions[1]; ++y)
2539 for (unsigned int x = 0; x <= repetitions[0]; ++x)
2540 points.push_back(p1 + x * delta[0] + y * delta[1] +
2541 z * delta[2]);
2542 break;
2543
2544 default:
2546 }
2547
2548 // next create the cells
2549 std::vector<CellData<dim>> cells;
2550 switch (dim)
2551 {
2552 case 1:
2553 {
2554 cells.resize(repetitions[0]);
2555 for (unsigned int x = 0; x < repetitions[0]; ++x)
2556 {
2557 cells[x].vertices[0] = x;
2558 cells[x].vertices[1] = x + 1;
2559 cells[x].material_id = 0;
2560 }
2561 break;
2562 }
2563
2564 case 2:
2565 {
2566 cells.resize(repetitions[1] * repetitions[0]);
2567 for (unsigned int y = 0; y < repetitions[1]; ++y)
2568 for (unsigned int x = 0; x < repetitions[0]; ++x)
2569 {
2570 const unsigned int c = x + y * repetitions[0];
2571 cells[c].vertices[0] = y * (repetitions[0] + 1) + x;
2572 cells[c].vertices[1] = y * (repetitions[0] + 1) + x + 1;
2573 cells[c].vertices[2] = (y + 1) * (repetitions[0] + 1) + x;
2574 cells[c].vertices[3] = (y + 1) * (repetitions[0] + 1) + x + 1;
2575 cells[c].material_id = 0;
2576 }
2577 break;
2578 }
2579
2580 case 3:
2581 {
2582 const unsigned int n_x = (repetitions[0] + 1);
2583 const unsigned int n_xy =
2584 (repetitions[0] + 1) * (repetitions[1] + 1);
2585
2586 cells.resize(repetitions[2] * repetitions[1] * repetitions[0]);
2587 for (unsigned int z = 0; z < repetitions[2]; ++z)
2588 for (unsigned int y = 0; y < repetitions[1]; ++y)
2589 for (unsigned int x = 0; x < repetitions[0]; ++x)
2590 {
2591 const unsigned int c = x + y * repetitions[0] +
2592 z * repetitions[0] * repetitions[1];
2593 cells[c].vertices[0] = z * n_xy + y * n_x + x;
2594 cells[c].vertices[1] = z * n_xy + y * n_x + x + 1;
2595 cells[c].vertices[2] = z * n_xy + (y + 1) * n_x + x;
2596 cells[c].vertices[3] = z * n_xy + (y + 1) * n_x + x + 1;
2597 cells[c].vertices[4] = (z + 1) * n_xy + y * n_x + x;
2598 cells[c].vertices[5] = (z + 1) * n_xy + y * n_x + x + 1;
2599 cells[c].vertices[6] = (z + 1) * n_xy + (y + 1) * n_x + x;
2600 cells[c].vertices[7] =
2601 (z + 1) * n_xy + (y + 1) * n_x + x + 1;
2602 cells[c].material_id = 0;
2603 }
2604 break;
2605 }
2606
2607 default:
2609 }
2610
2611 tria.create_triangulation(points, cells, SubCellData());
2612
2613 if (colorize)
2614 {
2615 // to colorize, run through all
2616 // faces of all cells and set
2617 // boundary indicator to the
2618 // correct value if it was 0.
2619
2620 // use a large epsilon to
2621 // compare numbers to avoid
2622 // roundoff problems.
2623 double epsilon = std::numeric_limits<double>::max();
2624 for (unsigned int i = 0; i < dim; ++i)
2625 epsilon = std::min(epsilon, 0.01 * delta[i][i]);
2626 Assert(epsilon > 0,
2627 ExcMessage(
2628 "The distance between corner points must be positive."));
2629
2630 // actual code is external since
2631 // 1-D is different from 2/3d.
2632 colorize_subdivided_hyper_rectangle(tria, p1, p2, epsilon);
2633 }
2634 }
2635
2636
2637
2638 template <int dim>
2639 void
2641 const std::vector<std::vector<double>> &step_sz,
2642 const Point<dim> &p_1,
2643 const Point<dim> &p_2,
2644 const bool colorize)
2645 {
2646 Assert(step_sz.size() == dim, ExcInvalidRepetitionsDimension(dim));
2647
2648 // First, normalize input such that
2649 // p1 is lower in all coordinate
2650 // directions and check the consistency of
2651 // step sizes, i.e. that they all
2652 // add up to the sizes specified by
2653 // p_1 and p_2
2654 Point<dim> p1(p_1);
2655 Point<dim> p2(p_2);
2656 std::vector<std::vector<double>> step_sizes(step_sz);
2657
2658 for (unsigned int i = 0; i < dim; ++i)
2659 {
2660 if (p1[i] > p2[i])
2661 {
2662 std::swap(p1[i], p2[i]);
2663 std::reverse(step_sizes[i].begin(), step_sizes[i].end());
2664 }
2665
2666 if constexpr (running_in_debug_mode())
2667 {
2668 double x = 0;
2669 for (unsigned int j = 0; j < step_sizes.at(i).size(); ++j)
2670 x += step_sizes[i][j];
2671 Assert(std::fabs(x - (p2[i] - p1[i])) <= 1e-12 * std::fabs(x),
2672 ExcMessage(
2673 "The sequence of step sizes in coordinate direction " +
2675 " must be equal to the distance of the two given "
2676 "points in this coordinate direction."));
2677 }
2678 }
2679
2680
2681 // then generate the necessary
2682 // points
2683 std::vector<Point<dim>> points;
2684 switch (dim)
2685 {
2686 case 1:
2687 {
2688 double x = 0;
2689 for (unsigned int i = 0;; ++i)
2690 {
2691 points.push_back(Point<dim>(p1[0] + x));
2692
2693 // form partial sums. in
2694 // the last run through
2695 // avoid accessing
2696 // non-existent values
2697 // and exit early instead
2698 if (i == step_sizes[0].size())
2699 break;
2700
2701 x += step_sizes[0][i];
2702 }
2703 break;
2704 }
2705
2706 case 2:
2707 {
2708 double y = 0;
2709 for (unsigned int j = 0;; ++j)
2710 {
2711 double x = 0;
2712 for (unsigned int i = 0;; ++i)
2713 {
2714 points.push_back(Point<dim>(p1[0] + x, p1[1] + y));
2715 if (i == step_sizes[0].size())
2716 break;
2717
2718 x += step_sizes[0][i];
2719 }
2720
2721 if (j == step_sizes[1].size())
2722 break;
2723
2724 y += step_sizes[1][j];
2725 }
2726 break;
2727 }
2728 case 3:
2729 {
2730 double z = 0;
2731 for (unsigned int k = 0;; ++k)
2732 {
2733 double y = 0;
2734 for (unsigned int j = 0;; ++j)
2735 {
2736 double x = 0;
2737 for (unsigned int i = 0;; ++i)
2738 {
2739 points.push_back(
2740 Point<dim>(p1[0] + x, p1[1] + y, p1[2] + z));
2741 if (i == step_sizes[0].size())
2742 break;
2743
2744 x += step_sizes[0][i];
2745 }
2746
2747 if (j == step_sizes[1].size())
2748 break;
2749
2750 y += step_sizes[1][j];
2751 }
2752
2753 if (k == step_sizes[2].size())
2754 break;
2755
2756 z += step_sizes[2][k];
2757 }
2758 break;
2759 }
2760
2761 default:
2763 }
2764
2765 // next create the cells
2766 // Prepare cell data
2767 std::vector<CellData<dim>> cells;
2768 switch (dim)
2769 {
2770 case 1:
2771 {
2772 cells.resize(step_sizes[0].size());
2773 for (unsigned int x = 0; x < step_sizes[0].size(); ++x)
2774 {
2775 cells[x].vertices[0] = x;
2776 cells[x].vertices[1] = x + 1;
2777 cells[x].material_id = 0;
2778 }
2779 break;
2780 }
2781
2782 case 2:
2783 {
2784 cells.resize(step_sizes[1].size() * step_sizes[0].size());
2785 for (unsigned int y = 0; y < step_sizes[1].size(); ++y)
2786 for (unsigned int x = 0; x < step_sizes[0].size(); ++x)
2787 {
2788 const unsigned int c = x + y * step_sizes[0].size();
2789 cells[c].vertices[0] = y * (step_sizes[0].size() + 1) + x;
2790 cells[c].vertices[1] = y * (step_sizes[0].size() + 1) + x + 1;
2791 cells[c].vertices[2] =
2792 (y + 1) * (step_sizes[0].size() + 1) + x;
2793 cells[c].vertices[3] =
2794 (y + 1) * (step_sizes[0].size() + 1) + x + 1;
2795 cells[c].material_id = 0;
2796 }
2797 break;
2798 }
2799
2800 case 3:
2801 {
2802 const unsigned int n_x = (step_sizes[0].size() + 1);
2803 const unsigned int n_xy =
2804 (step_sizes[0].size() + 1) * (step_sizes[1].size() + 1);
2805
2806 cells.resize(step_sizes[2].size() * step_sizes[1].size() *
2807 step_sizes[0].size());
2808 for (unsigned int z = 0; z < step_sizes[2].size(); ++z)
2809 for (unsigned int y = 0; y < step_sizes[1].size(); ++y)
2810 for (unsigned int x = 0; x < step_sizes[0].size(); ++x)
2811 {
2812 const unsigned int c =
2813 x + y * step_sizes[0].size() +
2814 z * step_sizes[0].size() * step_sizes[1].size();
2815 cells[c].vertices[0] = z * n_xy + y * n_x + x;
2816 cells[c].vertices[1] = z * n_xy + y * n_x + x + 1;
2817 cells[c].vertices[2] = z * n_xy + (y + 1) * n_x + x;
2818 cells[c].vertices[3] = z * n_xy + (y + 1) * n_x + x + 1;
2819 cells[c].vertices[4] = (z + 1) * n_xy + y * n_x + x;
2820 cells[c].vertices[5] = (z + 1) * n_xy + y * n_x + x + 1;
2821 cells[c].vertices[6] = (z + 1) * n_xy + (y + 1) * n_x + x;
2822 cells[c].vertices[7] =
2823 (z + 1) * n_xy + (y + 1) * n_x + x + 1;
2824 cells[c].material_id = 0;
2825 }
2826 break;
2827 }
2828
2829 default:
2831 }
2832
2833 tria.create_triangulation(points, cells, SubCellData());
2834
2835 if (colorize)
2836 {
2837 // to colorize, run through all
2838 // faces of all cells and set
2839 // boundary indicator to the
2840 // correct value if it was 0.
2841
2842 // use a large epsilon to
2843 // compare numbers to avoid
2844 // roundoff problems.
2845 double min_size =
2846 *std::min_element(step_sizes[0].begin(), step_sizes[0].end());
2847 for (unsigned int i = 1; i < dim; ++i)
2848 min_size = std::min(min_size,
2849 *std::min_element(step_sizes[i].begin(),
2850 step_sizes[i].end()));
2851 const double epsilon = 0.01 * min_size;
2852
2853 // actual code is external since
2854 // 1-D is different from 2/3d.
2855 colorize_subdivided_hyper_rectangle(tria, p1, p2, epsilon);
2856 }
2857 }
2858
2859
2860
2861 template <>
2862 void
2864 const std::vector<std::vector<double>> &spacing,
2865 const Point<1> &p,
2866 const Table<1, types::material_id> &material_id,
2867 const bool colorize)
2868 {
2869 Assert(spacing.size() == 1, ExcInvalidRepetitionsDimension(1));
2870
2871 const unsigned int n_cells = material_id.size(0);
2872
2873 Assert(spacing[0].size() == n_cells, ExcInvalidRepetitionsDimension(1));
2874
2875 double delta = std::numeric_limits<double>::max();
2876 for (unsigned int i = 0; i < n_cells; ++i)
2877 {
2878 Assert(spacing[0][i] >= 0, ExcInvalidRepetitions(-1));
2879 delta = std::min(delta, spacing[0][i]);
2880 }
2881
2882 // generate the necessary points
2883 std::vector<Point<1>> points;
2884 double ax = p[0];
2885 for (unsigned int x = 0; x <= n_cells; ++x)
2886 {
2887 points.emplace_back(ax);
2888 if (x < n_cells)
2889 ax += spacing[0][x];
2890 }
2891 // create the cells
2892 unsigned int n_val_cells = 0;
2893 for (unsigned int i = 0; i < n_cells; ++i)
2894 if (material_id[i] != numbers::invalid_material_id)
2895 ++n_val_cells;
2896
2897 std::vector<CellData<1>> cells(n_val_cells);
2898 unsigned int id = 0;
2899 for (unsigned int x = 0; x < n_cells; ++x)
2900 if (material_id[x] != numbers::invalid_material_id)
2901 {
2902 cells[id].vertices[0] = x;
2903 cells[id].vertices[1] = x + 1;
2904 cells[id].material_id = material_id[x];
2905 ++id;
2906 }
2907 // create triangulation
2908 SubCellData t;
2909 GridTools::delete_unused_vertices(points, cells, t);
2910
2911 tria.create_triangulation(points, cells, t);
2912
2913 // set boundary indicator
2914 if (colorize)
2916 }
2917
2918
2919 template <>
2920 void
2922 const std::vector<std::vector<double>> &spacing,
2923 const Point<2> &p,
2924 const Table<2, types::material_id> &material_id,
2925 const bool colorize)
2926 {
2927 Assert(spacing.size() == 2, ExcInvalidRepetitionsDimension(2));
2928
2929 std::vector<unsigned int> repetitions(2);
2930 double delta = std::numeric_limits<double>::max();
2931 for (unsigned int i = 0; i < 2; ++i)
2932 {
2933 repetitions[i] = spacing[i].size();
2934 for (unsigned int j = 0; j < repetitions[i]; ++j)
2935 {
2936 Assert(spacing[i][j] >= 0, ExcInvalidRepetitions(-1));
2937 delta = std::min(delta, spacing[i][j]);
2938 }
2939 Assert(material_id.size(i) == repetitions[i],
2940 ExcInvalidRepetitionsDimension(i));
2941 }
2942
2943 // generate the necessary points
2944 std::vector<Point<2>> points;
2945 double ay = p[1];
2946 for (unsigned int y = 0; y <= repetitions[1]; ++y)
2947 {
2948 double ax = p[0];
2949 for (unsigned int x = 0; x <= repetitions[0]; ++x)
2950 {
2951 points.emplace_back(ax, ay);
2952 if (x < repetitions[0])
2953 ax += spacing[0][x];
2954 }
2955 if (y < repetitions[1])
2956 ay += spacing[1][y];
2957 }
2958
2959 // create the cells
2960 unsigned int n_val_cells = 0;
2961 for (unsigned int i = 0; i < material_id.size(0); ++i)
2962 for (unsigned int j = 0; j < material_id.size(1); ++j)
2963 if (material_id[i][j] != numbers::invalid_material_id)
2964 ++n_val_cells;
2965
2966 std::vector<CellData<2>> cells(n_val_cells);
2967 unsigned int id = 0;
2968 for (unsigned int y = 0; y < repetitions[1]; ++y)
2969 for (unsigned int x = 0; x < repetitions[0]; ++x)
2970 if (material_id[x][y] != numbers::invalid_material_id)
2971 {
2972 cells[id].vertices[0] = y * (repetitions[0] + 1) + x;
2973 cells[id].vertices[1] = y * (repetitions[0] + 1) + x + 1;
2974 cells[id].vertices[2] = (y + 1) * (repetitions[0] + 1) + x;
2975 cells[id].vertices[3] = (y + 1) * (repetitions[0] + 1) + x + 1;
2976 cells[id].material_id = material_id[x][y];
2977 ++id;
2978 }
2979
2980 // create triangulation
2981 SubCellData t;
2982 GridTools::delete_unused_vertices(points, cells, t);
2983
2984 tria.create_triangulation(points, cells, t);
2985
2986 // set boundary indicator
2987 if (colorize)
2988 {
2989 double eps = 0.01 * delta;
2990 Triangulation<2>::cell_iterator cell = tria.begin(), endc = tria.end();
2991 for (; cell != endc; ++cell)
2992 {
2993 Point<2> cell_center = cell->center();
2994 for (const unsigned int f : cell->face_indices())
2995 if (cell->face(f)->boundary_id() == 0)
2996 {
2997 Point<2> face_center = cell->face(f)->center();
2998 for (unsigned int i = 0; i < 2; ++i)
2999 {
3000 if (face_center[i] < cell_center[i] - eps)
3001 cell->face(f)->set_boundary_id(i * 2);
3002 if (face_center[i] > cell_center[i] + eps)
3003 cell->face(f)->set_boundary_id(i * 2 + 1);
3004 }
3005 }
3006 }
3007 }
3008 }
3009
3010
3011 template <>
3012 void
3014 const std::vector<std::vector<double>> &spacing,
3015 const Point<3> &p,
3016 const Table<3, types::material_id> &material_id,
3017 const bool colorize)
3018 {
3019 const unsigned int dim = 3;
3020
3021 Assert(spacing.size() == dim, ExcInvalidRepetitionsDimension(dim));
3022
3023 std::array<unsigned int, dim> repetitions;
3024 double delta = std::numeric_limits<double>::max();
3025 for (unsigned int i = 0; i < dim; ++i)
3026 {
3027 repetitions[i] = spacing[i].size();
3028 for (unsigned int j = 0; j < repetitions[i]; ++j)
3029 {
3030 Assert(spacing[i][j] >= 0, ExcInvalidRepetitions(-1));
3031 delta = std::min(delta, spacing[i][j]);
3032 }
3033 Assert(material_id.size(i) == repetitions[i],
3034 ExcInvalidRepetitionsDimension(i));
3035 }
3036
3037 // generate the necessary points
3038 std::vector<Point<dim>> points;
3039 double az = p[2];
3040 for (unsigned int z = 0; z <= repetitions[2]; ++z)
3041 {
3042 double ay = p[1];
3043 for (unsigned int y = 0; y <= repetitions[1]; ++y)
3044 {
3045 double ax = p[0];
3046 for (unsigned int x = 0; x <= repetitions[0]; ++x)
3047 {
3048 points.emplace_back(ax, ay, az);
3049 if (x < repetitions[0])
3050 ax += spacing[0][x];
3051 }
3052 if (y < repetitions[1])
3053 ay += spacing[1][y];
3054 }
3055 if (z < repetitions[2])
3056 az += spacing[2][z];
3057 }
3058
3059 // create the cells
3060 unsigned int n_val_cells = 0;
3061 for (unsigned int i = 0; i < material_id.size(0); ++i)
3062 for (unsigned int j = 0; j < material_id.size(1); ++j)
3063 for (unsigned int k = 0; k < material_id.size(2); ++k)
3064 if (material_id[i][j][k] != numbers::invalid_material_id)
3065 ++n_val_cells;
3066
3067 std::vector<CellData<dim>> cells(n_val_cells);
3068 unsigned int id = 0;
3069 const unsigned int n_x = (repetitions[0] + 1);
3070 const unsigned int n_xy = (repetitions[0] + 1) * (repetitions[1] + 1);
3071 for (unsigned int z = 0; z < repetitions[2]; ++z)
3072 for (unsigned int y = 0; y < repetitions[1]; ++y)
3073 for (unsigned int x = 0; x < repetitions[0]; ++x)
3074 if (material_id[x][y][z] != numbers::invalid_material_id)
3075 {
3076 cells[id].vertices[0] = z * n_xy + y * n_x + x;
3077 cells[id].vertices[1] = z * n_xy + y * n_x + x + 1;
3078 cells[id].vertices[2] = z * n_xy + (y + 1) * n_x + x;
3079 cells[id].vertices[3] = z * n_xy + (y + 1) * n_x + x + 1;
3080 cells[id].vertices[4] = (z + 1) * n_xy + y * n_x + x;
3081 cells[id].vertices[5] = (z + 1) * n_xy + y * n_x + x + 1;
3082 cells[id].vertices[6] = (z + 1) * n_xy + (y + 1) * n_x + x;
3083 cells[id].vertices[7] = (z + 1) * n_xy + (y + 1) * n_x + x + 1;
3084 cells[id].material_id = material_id[x][y][z];
3085 ++id;
3086 }
3087
3088 // create triangulation
3089 SubCellData t;
3090 GridTools::delete_unused_vertices(points, cells, t);
3091
3092 tria.create_triangulation(points, cells, t);
3093
3094 // set boundary indicator
3095 if (colorize)
3096 {
3097 double eps = 0.01 * delta;
3099 endc = tria.end();
3100 for (; cell != endc; ++cell)
3101 {
3102 Point<dim> cell_center = cell->center();
3103 for (auto f : cell->face_indices())
3104 if (cell->face(f)->boundary_id() == 0)
3105 {
3106 Point<dim> face_center = cell->face(f)->center();
3107 for (unsigned int i = 0; i < dim; ++i)
3108 {
3109 if (face_center[i] < cell_center[i] - eps)
3110 cell->face(f)->set_boundary_id(i * 2);
3111 if (face_center[i] > cell_center[i] + eps)
3112 cell->face(f)->set_boundary_id(i * 2 + 1);
3113 }
3114 }
3115 }
3116 }
3117 }
3118
3119
3120
3121 template <int dim, int spacedim>
3122 void
3124 const std::vector<unsigned int> &holes)
3125 {
3126 AssertDimension(holes.size(), dim);
3127 // The corner points of the first cell. If there is a desire at
3128 // some point to change the geometry of the cells, they can be
3129 // made an argument to the function.
3130
3131 Point<spacedim> origin;
3132
3133 // then calculate the number of repetitions and check that
3134 // they are >= 1
3135 std::array<Point<spacedim>, dim> delta;
3136 std::array<unsigned int, dim> repetitions;
3137 for (unsigned int i = 0; i < dim; ++i)
3138 {
3139 Assert(holes[i] >= 1,
3140 ExcMessage("At least one hole needed in each direction"));
3141 repetitions[i] = 2 * holes[i] + 1;
3142 delta[i] = Point<spacedim>::unit_vector(i);
3143 }
3144
3145 // then generate the necessary
3146 // points
3147 std::vector<Point<spacedim>> points;
3148 switch (dim)
3149 {
3150 case 1:
3151 points.reserve(repetitions[0] + 1);
3152 for (unsigned int x = 0; x <= repetitions[0]; ++x)
3153 points.push_back(origin + x * delta[0]);
3154 break;
3155
3156 case 2:
3157 points.reserve((repetitions[0] + 1) * (repetitions[1] + 1));
3158 for (unsigned int y = 0; y <= repetitions[1]; ++y)
3159 for (unsigned int x = 0; x <= repetitions[0]; ++x)
3160 points.push_back(origin + x * delta[0] + y * delta[1]);
3161 break;
3162
3163 case 3:
3164 points.reserve((repetitions[0] + 1) * (repetitions[1] + 1) *
3165 (repetitions[2] + 1));
3166 for (unsigned int z = 0; z <= repetitions[2]; ++z)
3167 for (unsigned int y = 0; y <= repetitions[1]; ++y)
3168 for (unsigned int x = 0; x <= repetitions[0]; ++x)
3169 points.push_back(origin + x * delta[0] + y * delta[1] +
3170 z * delta[2]);
3171 break;
3172
3173 default:
3175 }
3176
3177 // next create the cells
3178 // Prepare cell data
3179 std::vector<CellData<dim>> cells;
3180 switch (dim)
3181 {
3182 case 2:
3183 {
3184 const unsigned int n_x = (repetitions[0] + 1);
3185
3186 cells.resize(repetitions[1] * repetitions[0] - holes[1] * holes[0]);
3187 unsigned int c = 0;
3188 for (unsigned int y = 0; y < repetitions[1]; ++y)
3189 for (unsigned int x = 0; x < repetitions[0]; ++x)
3190 {
3191 // Holes are on a regular grid of cells: Namely, on cells
3192 // that are both on odd rows and odd columns.
3193 if ((x % 2 == 1) && (y % 2 == 1))
3194 continue;
3195
3196 Assert(c < cells.size(), ExcInternalError());
3197 cells[c].vertices[0] = y * n_x + x;
3198 cells[c].vertices[1] = y * n_x + x + 1;
3199 cells[c].vertices[2] = (y + 1) * n_x + x;
3200 cells[c].vertices[3] = (y + 1) * n_x + x + 1;
3201 cells[c].material_id = 0;
3202 ++c;
3203 }
3204 Assert(c == cells.size(), ExcInternalError());
3205 break;
3206 }
3207
3208 case 3:
3209 {
3210 const unsigned int n_x = (repetitions[0] + 1);
3211 const unsigned int n_xy =
3212 (repetitions[0] + 1) * (repetitions[1] + 1);
3213
3214 // Allocate as many cells as there are voxels (whether we need
3215 // them or not):
3216 cells.resize(repetitions[2] * repetitions[1] * repetitions[0]);
3217
3218 unsigned int c = 0;
3219 for (unsigned int z = 0; z < repetitions[2]; ++z)
3220 for (unsigned int y = 0; y < repetitions[1]; ++y)
3221 for (unsigned int x = 0; x < repetitions[0]; ++x)
3222 {
3223 // Holes are on a regular grid of cells: Namely, on cells
3224 // that are both on odd rows and odd columns, and the same
3225 // for the third direction.
3226 if ((x % 2 == 1) && (y % 2 == 1) && z % 2 == 1)
3227 continue;
3228
3229 Assert(c < cells.size(), ExcInternalError());
3230 cells[c].vertices[0] = z * n_xy + y * n_x + x;
3231 cells[c].vertices[1] = z * n_xy + y * n_x + x + 1;
3232 cells[c].vertices[2] = z * n_xy + (y + 1) * n_x + x;
3233 cells[c].vertices[3] = z * n_xy + (y + 1) * n_x + x + 1;
3234 cells[c].vertices[4] = (z + 1) * n_xy + y * n_x + x;
3235 cells[c].vertices[5] = (z + 1) * n_xy + y * n_x + x + 1;
3236 cells[c].vertices[6] = (z + 1) * n_xy + (y + 1) * n_x + x;
3237 cells[c].vertices[7] =
3238 (z + 1) * n_xy + (y + 1) * n_x + x + 1;
3239 cells[c].material_id = 0;
3240 ++c;
3241 }
3242 // If there are holes in each coordinate direction, then we have
3243 // fewer cells than possible cell locations previously allocated.
3244 // Check this, then drop all of the unused memory locations.
3245 Assert(c < cells.size(), ExcInternalError());
3246 cells.resize(c);
3247 break;
3248 }
3249
3250 default:
3252 }
3253
3254 tria.create_triangulation(points, cells, SubCellData());
3255 }
3256
3257
3258
3259 template <int dim, int spacedim>
3260 void
3262 const Table<dim, bool> &present_voxels,
3263 const double voxel_edge_length)
3264 {
3265 // First create all vertices of the mesh (regardless of whether they
3266 // are used -- unused ones will simply be deleted later on). The
3267 // points are indexed lexicographically.
3268 std::array<unsigned int, dim> voxels_per_direction;
3269 for (unsigned int d = 0; d < dim; ++d)
3270 {
3271 Assert(present_voxels.size()[d] > 0,
3272 ExcMessage(
3273 "The 2d or 3d array that describes which pixels/voxels "
3274 "are present in the domain must have a non-zero extent in"
3275 "in coordinate direction."));
3276 voxels_per_direction[d] = present_voxels.size()[d];
3277 }
3278
3279 std::vector<Point<spacedim>> points;
3280 switch (dim)
3281 {
3282 case 1:
3283 points.reserve(voxels_per_direction[0] + 1);
3284 for (unsigned int x = 0; x < voxels_per_direction[0] + 1; ++x)
3285 points.emplace_back(x * voxel_edge_length);
3286 break;
3287
3288 case 2:
3289 points.reserve((voxels_per_direction[0] + 1) *
3290 (voxels_per_direction[1] + 1));
3291 for (unsigned int y = 0; y < voxels_per_direction[1] + 1; ++y)
3292 for (unsigned int x = 0; x < voxels_per_direction[0] + 1; ++x)
3293 points.emplace_back(x * voxel_edge_length, y * voxel_edge_length);
3294 break;
3295
3296 case 3:
3297 points.reserve((voxels_per_direction[0] + 1) *
3298 (voxels_per_direction[1] + 1) *
3299 (voxels_per_direction[2] + 1));
3300 for (unsigned int z = 0; z < voxels_per_direction[2] + 1; ++z)
3301 for (unsigned int y = 0; y < voxels_per_direction[1] + 1; ++y)
3302 for (unsigned int x = 0; x < voxels_per_direction[0] + 1; ++x)
3303 points.emplace_back(x * voxel_edge_length,
3304 y * voxel_edge_length,
3305 z * voxel_edge_length);
3306 break;
3307
3308 default:
3310 }
3311
3312 // Next count and then create the cells we need:
3313 unsigned int n_voxels = 0;
3314 if constexpr (dim == 2)
3315 {
3316 for (unsigned int x = 0; x < voxels_per_direction[0]; ++x)
3317 for (unsigned int y = 0; y < voxels_per_direction[1]; ++y)
3318 if (present_voxels[x][y] == true)
3319 ++n_voxels;
3320 }
3321 else if constexpr (dim == 3)
3322 {
3323 for (unsigned int x = 0; x < voxels_per_direction[0]; ++x)
3324 for (unsigned int y = 0; y < voxels_per_direction[1]; ++y)
3325 for (unsigned int z = 0; z < voxels_per_direction[2]; ++z)
3326 if (present_voxels[x][y][z] == true)
3327 ++n_voxels;
3328 }
3329 else
3331
3332 std::vector<CellData<dim>> cells(n_voxels);
3333 if constexpr (dim == 2)
3334 {
3335 const unsigned int n_x = (voxels_per_direction[0] + 1);
3336
3337 unsigned int c = 0;
3338 for (unsigned int y = 0; y < voxels_per_direction[1]; ++y)
3339 for (unsigned int x = 0; x < voxels_per_direction[0]; ++x)
3340 if (present_voxels[x][y] == true)
3341 {
3342 cells[c].vertices[0] = y * n_x + x;
3343 cells[c].vertices[1] = y * n_x + x + 1;
3344 cells[c].vertices[2] = (y + 1) * n_x + x;
3345 cells[c].vertices[3] = (y + 1) * n_x + x + 1;
3346 cells[c].material_id = 0;
3347 ++c;
3348 }
3349 Assert(c == cells.size(), ExcInternalError());
3350 }
3351 else if constexpr (dim == 3)
3352 {
3353 const unsigned int n_x = (voxels_per_direction[0] + 1);
3354 const unsigned int n_xy =
3355 (voxels_per_direction[0] + 1) * (voxels_per_direction[1] + 1);
3356
3357 unsigned int c = 0;
3358 for (unsigned int z = 0; z < voxels_per_direction[2]; ++z)
3359 for (unsigned int y = 0; y < voxels_per_direction[1]; ++y)
3360 for (unsigned int x = 0; x < voxels_per_direction[0]; ++x)
3361 if (present_voxels[x][y][z] == true)
3362 {
3363 Assert(c < cells.size(), ExcInternalError());
3364 cells[c].vertices[0] = z * n_xy + y * n_x + x;
3365 cells[c].vertices[1] = z * n_xy + y * n_x + x + 1;
3366 cells[c].vertices[2] = z * n_xy + (y + 1) * n_x + x;
3367 cells[c].vertices[3] = z * n_xy + (y + 1) * n_x + x + 1;
3368 cells[c].vertices[4] = (z + 1) * n_xy + y * n_x + x;
3369 cells[c].vertices[5] = (z + 1) * n_xy + y * n_x + x + 1;
3370 cells[c].vertices[6] = (z + 1) * n_xy + (y + 1) * n_x + x;
3371 cells[c].vertices[7] = (z + 1) * n_xy + (y + 1) * n_x + x + 1;
3372 cells[c].material_id = 0;
3373 ++c;
3374 }
3375 Assert(c == cells.size(), ExcInternalError());
3376 }
3377 else
3379
3380 // We may have vertices that are not part of any cell because all
3381 // cells that might be adjacent were not part of the mask. Delete these:
3382 SubCellData subcell_data;
3383 GridTools::delete_unused_vertices(points, cells, subcell_data);
3384
3385 tria.create_triangulation(points, cells, SubCellData());
3386 }
3387
3388
3389
3390 template <>
3391 void
3393 const double /*inner_radius*/,
3394 const double /*outer_radius*/,
3395 const double /*pad_bottom*/,
3396 const double /*pad_top*/,
3397 const double /*pad_left*/,
3398 const double /*pad_right*/,
3399 const Point<1> & /*center*/,
3400 const types::manifold_id /*polar_manifold_id*/,
3401 const types::manifold_id /*tfi_manifold_id*/,
3402 const double /*L*/,
3403 const unsigned int /*n_slices*/,
3404 const bool /*colorize*/)
3405 {
3407 }
3408
3409
3410
3411 template <>
3412 void
3414 const double /*shell_region_width*/,
3415 const unsigned int /*n_shells*/,
3416 const double /*skewness*/,
3417 const bool /*colorize*/)
3418 {
3420 }
3421
3422
3423
3424 namespace internal
3425 {
3426 // helper function to check if point is in 2d box
3427 bool
3428 point_in_2d_box(const Point<2> &p, const Point<2> &c, const double radius)
3429 {
3430 return (std::abs(p[0] - c[0]) < radius) &&
3431 (std::abs(p[1] - c[1]) < radius);
3432 }
3433
3434
3435
3436 // Find the minimal distance between two vertices. This is useful for
3437 // computing a tolerance for merging vertices in
3438 // GridTools::merge_triangulations.
3439 template <int dim, int spacedim>
3440 double
3441 minimal_vertex_distance(const Triangulation<dim, spacedim> &triangulation)
3442 {
3443 double length = std::numeric_limits<double>::max();
3444 for (const auto &cell : triangulation.active_cell_iterators())
3445 for (unsigned int n = 0; n < GeometryInfo<dim>::lines_per_cell; ++n)
3446 length = std::min(length, cell->line(n)->diameter());
3447 return length;
3448 }
3449 } // namespace internal
3450
3451
3452
3453 template <>
3454 void
3456 const double inner_radius,
3457 const double outer_radius,
3458 const double pad_bottom,
3459 const double pad_top,
3460 const double pad_left,
3461 const double pad_right,
3462 const Point<2> &new_center,
3463 const types::manifold_id polar_manifold_id,
3464 const types::manifold_id tfi_manifold_id,
3465 const double L,
3466 const unsigned int /*n_slices*/,
3467 const bool colorize)
3468 {
3469 const bool with_padding =
3470 pad_bottom > 0 || pad_top > 0 || pad_left > 0 || pad_right > 0;
3471
3472 Assert(pad_bottom >= 0., ExcMessage("Negative bottom padding."));
3473 Assert(pad_top >= 0., ExcMessage("Negative top padding."));
3474 Assert(pad_left >= 0., ExcMessage("Negative left padding."));
3475 Assert(pad_right >= 0., ExcMessage("Negative right padding."));
3476
3477 const Point<2> center;
3478
3479 auto min_line_length = [](const Triangulation<2> &tria) -> double {
3480 double length = std::numeric_limits<double>::max();
3481 for (const auto &cell : tria.active_cell_iterators())
3482 for (unsigned int n = 0; n < cell->n_lines(); ++n)
3483 length = std::min(length, cell->line(n)->diameter());
3484 return length;
3485 };
3486
3487 // start by setting up the cylinder triangulation
3488 Triangulation<2> cylinder_tria_maybe;
3489 Triangulation<2> &cylinder_tria = with_padding ? cylinder_tria_maybe : tria;
3491 inner_radius,
3493 L,
3494 /*repetitions*/ 1,
3495 colorize);
3496
3497 // we will deal with face manifold ids after we merge triangulations
3498 for (const auto &cell : cylinder_tria.active_cell_iterators())
3499 cell->set_manifold_id(tfi_manifold_id);
3500
3501 const Point<2> bl(-outer_radius - pad_left, -outer_radius - pad_bottom);
3502 const Point<2> tr(outer_radius + pad_right, outer_radius + pad_top);
3503 if (with_padding)
3504 {
3505 // hyper_cube_with_cylindrical_hole will have 2 cells along
3506 // each face, so the element size is outer_radius
3507
3508 auto add_sizes = [](std::vector<double> &step_sizes,
3509 const double padding,
3510 const double h) -> void {
3511 // use std::round instead of std::ceil to improve aspect ratio
3512 // in case padding is only slightly larger than h.
3513 const auto rounded =
3514 static_cast<unsigned int>(std::round(padding / h));
3515 // in case padding is much smaller than h, make sure we
3516 // have at least 1 element
3517 const unsigned int num = (padding > 0. && rounded == 0) ? 1 : rounded;
3518 for (unsigned int i = 0; i < num; ++i)
3519 step_sizes.push_back(padding / num);
3520 };
3521
3522 std::vector<std::vector<double>> step_sizes(2);
3523 // x-coord
3524 // left:
3525 add_sizes(step_sizes[0], pad_left, outer_radius);
3526 // center
3527 step_sizes[0].push_back(outer_radius);
3528 step_sizes[0].push_back(outer_radius);
3529 // right
3530 add_sizes(step_sizes[0], pad_right, outer_radius);
3531 // y-coord
3532 // bottom
3533 add_sizes(step_sizes[1], pad_bottom, outer_radius);
3534 // center
3535 step_sizes[1].push_back(outer_radius);
3536 step_sizes[1].push_back(outer_radius);
3537 // top
3538 add_sizes(step_sizes[1], pad_top, outer_radius);
3539
3540 // now create bulk
3541 Triangulation<2> bulk_tria;
3543 bulk_tria, step_sizes, bl, tr, colorize);
3544
3545 // now remove cells reserved from the cylindrical hole
3546 std::set<Triangulation<2>::active_cell_iterator> cells_to_remove;
3547 for (const auto &cell : bulk_tria.active_cell_iterators())
3548 if (internal::point_in_2d_box(cell->center(), center, outer_radius))
3549 cells_to_remove.insert(cell);
3550
3551 Triangulation<2> tria_without_cylinder;
3553 bulk_tria, cells_to_remove, tria_without_cylinder);
3554
3555 const double tolerance =
3556 std::min(min_line_length(tria_without_cylinder),
3557 min_line_length(cylinder_tria)) /
3558 2.0;
3559
3561 tria_without_cylinder, cylinder_tria, tria, tolerance, true);
3562 }
3563
3564 // now set manifold ids:
3565 for (const auto &cell : tria.active_cell_iterators())
3566 {
3567 // set all non-boundary manifold ids on the cells that came from the
3568 // grid around the cylinder to the new TFI manifold id.
3569 if (cell->manifold_id() == tfi_manifold_id)
3570 {
3571 for (const unsigned int face_n : cell->face_indices())
3572 {
3573 const auto &face = cell->face(face_n);
3574 if (face->at_boundary() &&
3575 internal::point_in_2d_box(face->center(),
3576 center,
3577 outer_radius * (1. - 1e-12)))
3578 face->set_manifold_id(polar_manifold_id);
3579 else
3580 face->set_manifold_id(tfi_manifold_id);
3581 }
3582 }
3583 else
3584 {
3585 // ensure that all other manifold ids (including the faces
3586 // opposite the cylinder) are set to the flat id
3588 }
3589 }
3590
3591 static constexpr double tol =
3592 std::numeric_limits<double>::epsilon() * 10000;
3593 if (colorize)
3594 for (const auto &cell : tria.active_cell_iterators())
3595 for (const unsigned int face_n : cell->face_indices())
3596 {
3597 const auto face = cell->face(face_n);
3598 if (face->at_boundary())
3599 {
3600 const Point<2> center = face->center();
3601 // left side
3602 if (std::abs(center[0] - bl[0]) < tol * std::abs(bl[0]))
3603 face->set_boundary_id(0);
3604 // right side
3605 else if (std::abs(center[0] - tr[0]) < tol * std::abs(tr[0]))
3606 face->set_boundary_id(1);
3607 // bottom
3608 else if (std::abs(center[1] - bl[1]) < tol * std::abs(bl[1]))
3609 face->set_boundary_id(2);
3610 // top
3611 else if (std::abs(center[1] - tr[1]) < tol * std::abs(tr[1]))
3612 face->set_boundary_id(3);
3613 // cylinder boundary
3614 else
3615 {
3616 Assert(cell->manifold_id() == tfi_manifold_id,
3618 face->set_boundary_id(4);
3619 }
3620 }
3621 }
3622
3623 // move to the new center
3624 GridTools::shift(new_center, tria);
3625
3626 PolarManifold<2> polar_manifold(new_center);
3627 tria.set_manifold(polar_manifold_id, polar_manifold);
3628 tria.set_manifold(tfi_manifold_id, FlatManifold<2>());
3630 inner_manifold.initialize(tria);
3631 tria.set_manifold(tfi_manifold_id, inner_manifold);
3632 }
3633
3634
3635
3636 template <>
3637 void
3639 const double inner_radius,
3640 const double outer_radius,
3641 const double pad_bottom,
3642 const double pad_top,
3643 const double pad_left,
3644 const double pad_right,
3645 const Point<3> &new_center,
3646 const types::manifold_id polar_manifold_id,
3647 const types::manifold_id tfi_manifold_id,
3648 const double L,
3649 const unsigned int n_slices,
3650 const bool colorize)
3651 {
3652 Triangulation<2> tria_2;
3653 plate_with_a_hole(tria_2,
3654 inner_radius,
3656 pad_bottom,
3657 pad_top,
3658 pad_left,
3659 pad_right,
3660 Point<2>(new_center[0], new_center[1]),
3661 polar_manifold_id,
3662 tfi_manifold_id,
3663 L,
3664 n_slices,
3665 colorize);
3666
3667 // extrude to 3d
3668 extrude_triangulation(tria_2, n_slices, L, tria, true);
3669
3670 // shift in Z direction to match specified center
3671 GridTools::shift(Point<3>(0, 0, new_center[2] - L / 2.), tria);
3672
3673 // set up the new manifolds
3674 const Tensor<1, 3> direction{{0.0, 0.0, 1.0}};
3675 const CylindricalManifold<3> cylindrical_manifold(
3676 direction,
3677 /*axial_point*/ new_center);
3678 tria.set_manifold(polar_manifold_id, FlatManifold<3>());
3679 tria.set_manifold(tfi_manifold_id, FlatManifold<3>());
3681 inner_manifold.initialize(tria);
3682
3683 tria.set_manifold(polar_manifold_id, cylindrical_manifold);
3684 tria.set_manifold(tfi_manifold_id, inner_manifold);
3685 }
3686
3687
3688
3689 template <>
3690 void
3692 const double shell_region_width,
3693 const unsigned int n_shells,
3694 const double skewness,
3695 const bool colorize)
3696 {
3697 Assert(0.0 <= shell_region_width && shell_region_width < 0.05,
3698 ExcMessage("The width of the shell region must be less than 0.05 "
3699 "(and preferably close to 0.03)"));
3700 const types::manifold_id polar_manifold_id = 0;
3701 const types::manifold_id tfi_manifold_id = 1;
3702
3703 // We begin by setting up a grid that is 4 by 22 cells. While not
3704 // squares, these have pretty good aspect ratios.
3705 Triangulation<2> bulk_tria;
3707 {22u, 4u},
3708 Point<2>(0.0, 0.0),
3709 Point<2>(2.2, 0.41));
3710 // bulk_tria now looks like this:
3711 //
3712 // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
3713 // | | | | | | | | | | | | | | | | | | | | | | |
3714 // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
3715 // | |XX|XX| | | | | | | | | | | | | | | | | | | |
3716 // +--+--O--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
3717 // | |XX|XX| | | | | | | | | | | | | | | | | | | |
3718 // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
3719 // | | | | | | | | | | | | | | | | | | | | | | |
3720 // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
3721 //
3722 // Note that these cells are not quite squares: they are all 0.1 by
3723 // 0.1025.
3724 //
3725 // The next step is to remove the cells marked with XXs: we will place
3726 // the grid around the cylinder there later. The next loop does two
3727 // things:
3728 // 1. Determines which cells need to be removed from the Triangulation
3729 // (i.e., find the cells marked with XX in the picture).
3730 // 2. Finds the location of the vertex marked with 'O' and uses that to
3731 // calculate the shift vector for aligning cylinder_tria with
3732 // tria_without_cylinder.
3733 std::set<Triangulation<2>::active_cell_iterator> cells_to_remove;
3734 Tensor<1, 2> cylinder_triangulation_offset;
3735 for (const auto &cell : bulk_tria.active_cell_iterators())
3736 {
3737 if ((cell->center() - Point<2>(0.2, 0.2)).norm() < 0.15)
3738 cells_to_remove.insert(cell);
3739
3740 if (cylinder_triangulation_offset == Tensor<1, 2>())
3741 {
3742 for (const unsigned int vertex_n :
3744 if (cell->vertex(vertex_n) == Point<2>())
3745 {
3746 // cylinder_tria is centered at zero, so we need to
3747 // shift it up and to the right by two cells:
3748 cylinder_triangulation_offset =
3749 2.0 * (cell->vertex(3) - Point<2>());
3750 break;
3751 }
3752 }
3753 }
3754 Triangulation<2> tria_without_cylinder;
3756 bulk_tria, cells_to_remove, tria_without_cylinder);
3757
3758 // set up the cylinder triangulation. Note that this function sets the
3759 // manifold ids of the interior boundary cells to 0
3760 // (polar_manifold_id).
3761 Triangulation<2> cylinder_tria;
3763 0.05 + shell_region_width,
3764 0.41 / 4.0);
3765 // The bulk cells are not quite squares, so we need to move the left
3766 // and right sides of cylinder_tria inwards so that it fits in
3767 // bulk_tria:
3768 for (const auto &cell : cylinder_tria.active_cell_iterators())
3769 for (const unsigned int vertex_n : GeometryInfo<2>::vertex_indices())
3770 {
3771 if (std::abs(cell->vertex(vertex_n)[0] - -0.41 / 4.0) < 1e-10)
3772 cell->vertex(vertex_n)[0] = -0.1;
3773 else if (std::abs(cell->vertex(vertex_n)[0] - 0.41 / 4.0) < 1e-10)
3774 cell->vertex(vertex_n)[0] = 0.1;
3775 }
3776
3777 // Assign interior manifold ids to be the TFI id.
3778 for (const auto &cell : cylinder_tria.active_cell_iterators())
3779 {
3780 cell->set_manifold_id(tfi_manifold_id);
3781 for (const unsigned int face_n : GeometryInfo<2>::face_indices())
3782 if (!cell->face(face_n)->at_boundary())
3783 cell->face(face_n)->set_manifold_id(tfi_manifold_id);
3784 }
3785 if (0.0 < shell_region_width)
3786 {
3787 Assert(0 < n_shells,
3788 ExcMessage("If the shell region has positive width then "
3789 "there must be at least one shell."));
3790 Triangulation<2> shell_tria;
3792 Point<2>(),
3793 0.05,
3794 0.05 + shell_region_width,
3795 n_shells,
3796 skewness,
3797 8);
3798
3799 // Make the tolerance as large as possible since these cells can
3800 // be quite close together
3801 const double vertex_tolerance =
3802 std::min(internal::minimal_vertex_distance(shell_tria),
3803 internal::minimal_vertex_distance(cylinder_tria)) *
3804 0.5;
3805
3806 shell_tria.set_all_manifold_ids(polar_manifold_id);
3807 Triangulation<2> temp;
3809 shell_tria, cylinder_tria, temp, vertex_tolerance, true);
3810 cylinder_tria = std::move(temp);
3811 }
3812 GridTools::shift(cylinder_triangulation_offset, cylinder_tria);
3813
3814 // Compute the tolerance again, since the shells may be very close to
3815 // each-other:
3816 const double vertex_tolerance =
3817 std::min(internal::minimal_vertex_distance(tria_without_cylinder),
3818 internal::minimal_vertex_distance(cylinder_tria)) /
3819 10;
3821 tria_without_cylinder, cylinder_tria, tria, vertex_tolerance, true);
3822
3823 // Move the vertices in the middle of the faces of cylinder_tria slightly
3824 // to give a better mesh quality. We have to balance the quality of these
3825 // cells with the quality of the outer cells (initially rectangles). For
3826 // constant radial distance, we would place them at the distance 0.1 *
3827 // sqrt(2.) from the center. In case the shell region width is more than
3828 // 0.1/6., we choose to place them at 0.1 * 4./3. from the center, which
3829 // ensures that the shortest edge of the outer cells is 2./3. of the
3830 // original length. If the shell region width is less, we make the edge
3831 // length of the inner part and outer part (in the shorter x direction)
3832 // the same.
3833 {
3834 const double shift =
3835 std::min(0.125 + shell_region_width * 0.5, 0.1 * 4. / 3.);
3836 for (const auto &cell : tria.active_cell_iterators())
3837 for (const unsigned int v : GeometryInfo<2>::vertex_indices())
3838 if (cell->vertex(v).distance(Point<2>(0.1, 0.205)) < 1e-10)
3839 cell->vertex(v) = Point<2>(0.2 - shift, 0.205);
3840 else if (cell->vertex(v).distance(Point<2>(0.3, 0.205)) < 1e-10)
3841 cell->vertex(v) = Point<2>(0.2 + shift, 0.205);
3842 else if (cell->vertex(v).distance(Point<2>(0.2, 0.1025)) < 1e-10)
3843 cell->vertex(v) = Point<2>(0.2, 0.2 - shift);
3844 else if (cell->vertex(v).distance(Point<2>(0.2, 0.3075)) < 1e-10)
3845 cell->vertex(v) = Point<2>(0.2, 0.2 + shift);
3846 }
3847
3848 // Ensure that all manifold ids on a polar cell really are set to the
3849 // polar manifold id:
3850 for (const auto &cell : tria.active_cell_iterators())
3851 if (cell->manifold_id() == polar_manifold_id)
3852 cell->set_all_manifold_ids(polar_manifold_id);
3853
3854 // Ensure that all other manifold ids (including the interior faces
3855 // opposite the cylinder) are set to the flat manifold id:
3856 for (const auto &cell : tria.active_cell_iterators())
3857 if (cell->manifold_id() != polar_manifold_id &&
3858 cell->manifold_id() != tfi_manifold_id)
3860
3861 // We need to calculate the current center so that we can move it later:
3862 // to start get a unique list of (points to) vertices on the cylinder
3863 std::vector<Point<2> *> cylinder_pointers;
3864 for (const auto &face : tria.active_face_iterators())
3865 if (face->manifold_id() == polar_manifold_id)
3866 {
3867 cylinder_pointers.push_back(&face->vertex(0));
3868 cylinder_pointers.push_back(&face->vertex(1));
3869 }
3870 // de-duplicate
3871 std::sort(cylinder_pointers.begin(), cylinder_pointers.end());
3872 cylinder_pointers.erase(std::unique(cylinder_pointers.begin(),
3873 cylinder_pointers.end()),
3874 cylinder_pointers.end());
3875
3876 // find the current center...
3877 Point<2> center;
3878 for (const Point<2> *const ptr : cylinder_pointers)
3879 center += *ptr / double(cylinder_pointers.size());
3880
3881 // and recenter at (0.2, 0.2)
3882 for (Point<2> *const ptr : cylinder_pointers)
3883 *ptr += Point<2>(0.2, 0.2) - center;
3884
3885 // attach manifolds
3886 PolarManifold<2> polar_manifold(Point<2>(0.2, 0.2));
3887 tria.set_manifold(polar_manifold_id, polar_manifold);
3888
3889 tria.set_manifold(tfi_manifold_id, FlatManifold<2>());
3891 inner_manifold.initialize(tria);
3892 tria.set_manifold(tfi_manifold_id, inner_manifold);
3893
3894 if (colorize)
3895 for (const auto &face : tria.active_face_iterators())
3896 if (face->at_boundary())
3897 {
3898 const Point<2> center = face->center();
3899 // left side
3900 if (std::abs(center[0] - 0.0) < 1e-10)
3901 face->set_boundary_id(0);
3902 // right side
3903 else if (std::abs(center[0] - 2.2) < 1e-10)
3904 face->set_boundary_id(1);
3905 // cylinder boundary
3906 else if (face->manifold_id() == polar_manifold_id)
3907 face->set_boundary_id(2);
3908 // sides of channel
3909 else
3910 {
3911 Assert(std::abs(center[1] - 0.00) < 1.0e-10 ||
3912 std::abs(center[1] - 0.41) < 1.0e-10,
3914 face->set_boundary_id(3);
3915 }
3916 }
3917 }
3918
3919
3920
3921 template <>
3922 void
3924 const double shell_region_width,
3925 const unsigned int n_shells,
3926 const double skewness,
3927 const bool colorize)
3928 {
3929 Triangulation<2> tria_2;
3931 tria_2, shell_region_width, n_shells, skewness, colorize);
3932 extrude_triangulation(tria_2, 5, 0.41, tria, true);
3933
3934 // set up the new 3d manifolds
3935 const types::manifold_id cylindrical_manifold_id = 0;
3936 const types::manifold_id tfi_manifold_id = 1;
3937 const PolarManifold<2> *const m_ptr =
3938 dynamic_cast<const PolarManifold<2> *>(
3939 &tria_2.get_manifold(cylindrical_manifold_id));
3940 Assert(m_ptr != nullptr, ExcInternalError());
3941 const Point<3> axial_point(m_ptr->get_center()[0],
3942 m_ptr->get_center()[1],
3943 0.0);
3944 const Tensor<1, 3> direction{{0.0, 0.0, 1.0}};
3945
3946 tria.set_manifold(cylindrical_manifold_id, FlatManifold<3>());
3947 tria.set_manifold(tfi_manifold_id, FlatManifold<3>());
3948 const CylindricalManifold<3> cylindrical_manifold(direction, axial_point);
3950 inner_manifold.initialize(tria);
3951 tria.set_manifold(cylindrical_manifold_id, cylindrical_manifold);
3952 tria.set_manifold(tfi_manifold_id, inner_manifold);
3953
3954 // From extrude_triangulation: since the maximum boundary id of tria_2 was
3955 // 3, the bottom boundary id is 4 and the top is 5: both are walls, so set
3956 // them to 3
3957 if (colorize)
3958 for (const auto &face : tria.active_face_iterators())
3959 if (face->boundary_id() == 4 || face->boundary_id() == 5)
3960 face->set_boundary_id(3);
3961 }
3962
3963 template <>
3964 void
3966 const std::vector<unsigned int> &,
3967 const double,
3968 unsigned int,
3969 const double,
3970 const unsigned,
3971 const double,
3972 const bool,
3973 const bool)
3974 {
3976 }
3977
3978 template <>
3979 void
3981 Triangulation<2> &tria,
3982 const std::vector<unsigned int> &lengths_and_heights,
3983 const double,
3984 unsigned int,
3985 const double shell_region_radius,
3986 const unsigned int n_shells,
3987 const double skewness,
3988 const bool use_transfinite_region,
3989 const bool colorize)
3990 {
3991 const types::manifold_id polar_manifold_id = 0;
3992 const types::manifold_id tfi_manifold_id = 1;
3993
3994 // The radius of the cylinder is 0.5, so the diameter is 1.
3995 const double radius = 0.5;
3996 const double box_radius = 1;
3997
3998 // We assume that the cylinder is centered at (0,0) and has a diameter of 1.
3999 // We use the cylinder diameter as the characteristic length of the channel.
4000 // The number of repetitions is chosen to ensure that the cylinder
4001 // occupies four cells.
4002
4003 const unsigned int length_pre = lengths_and_heights[0];
4004 const unsigned int length_post = lengths_and_heights[1];
4005 const unsigned int height_below = lengths_and_heights[2];
4006 const unsigned int height_above = lengths_and_heights[3];
4007
4008 const unsigned int length_repetitions = length_pre + length_post;
4009 const unsigned int height_repetitions = height_above + height_below;
4010
4011 // We begin by setting up a grid that is length_repetition by
4012 // height_repetitions cells. These cells are all square
4013 Triangulation<2> bulk_tria;
4015 bulk_tria,
4016 {(length_repetitions), height_repetitions},
4017 Point<2>(-double(length_pre), -double(height_below)),
4018 Point<2>(double(length_post), double(height_above)));
4019
4020 // bulk_tria now looks like this:
4021 //
4022 // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
4023 // | | | | | | | | | | | | | | | | | | | | | | |
4024 // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
4025 // | |XX|XX| | | | | | | | | | | | | | | | | | | |
4026 // +--+--O--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
4027 // | |XX|XX| | | | | | | | | | | | | | | | | | | |
4028 // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
4029 // | | | | | | | | | | | | | | | | | | | | | | |
4030 // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
4031 //
4032 // The next step is to remove the cells marked with XXs: we will place
4033 // the grid around the cylinder there later. The following loop determines
4034 // which cells need to be removed from the Triangulation
4035 // (i.e., find the cells marked with XX in the picture).
4036 std::set<Triangulation<2>::active_cell_iterator> cells_to_remove;
4037 for (const auto &cell : bulk_tria.active_cell_iterators())
4038 {
4039 if ((cell->center() - Point<2>(0., 0.)).norm() < 1.1 * box_radius)
4040 cells_to_remove.insert(cell);
4041 }
4042
4043 Triangulation<2> tria_without_cylinder;
4045 bulk_tria, cells_to_remove, tria_without_cylinder);
4046
4047 // Set up the cylinder triangulation. Note that this function sets the
4048 // manifold ids of the interior boundary cells to 0
4049 // (polar_manifold_id).
4050 Triangulation<2> cylinder_tria;
4052 shell_region_radius,
4053 box_radius);
4054
4055 // Assign interior manifold ids to be the TFI id.
4056 for (const auto &cell : cylinder_tria.active_cell_iterators())
4057 {
4058 cell->set_manifold_id(tfi_manifold_id);
4059 for (const unsigned int face_n : GeometryInfo<2>::face_indices())
4060 if (!cell->face(face_n)->at_boundary())
4061 cell->face(face_n)->set_manifold_id(tfi_manifold_id);
4062 }
4063
4064 // The shell region should have a radius that is larger than the radius of
4065 // the cylinder
4066 if (radius < shell_region_radius)
4067 {
4068 Assert(0 < n_shells,
4069 ExcMessage("If the shell region has positive width then "
4070 "there must be at least one shell."));
4071 Triangulation<2> shell_tria;
4073 Point<2>(),
4074 radius,
4075 shell_region_radius,
4076 n_shells,
4077 skewness,
4078 8);
4079
4080 // Make the tolerance as large as possible since these cells can
4081 // be quite close together
4082 const double vertex_tolerance =
4083 std::min(internal::minimal_vertex_distance(shell_tria),
4084 internal::minimal_vertex_distance(cylinder_tria)) *
4085 0.5;
4086
4087 shell_tria.set_all_manifold_ids(polar_manifold_id);
4088 Triangulation<2> temp;
4090 shell_tria, cylinder_tria, temp, vertex_tolerance, true);
4091 cylinder_tria = std::move(temp);
4092 }
4093
4094 // Compute the tolerance again, since the shells may be very close to
4095 // each-other:
4096 const double vertex_tolerance =
4097 std::min(internal::minimal_vertex_distance(tria_without_cylinder),
4098 internal::minimal_vertex_distance(cylinder_tria)) /
4099 10;
4100
4102 tria_without_cylinder, cylinder_tria, tria, vertex_tolerance, true);
4103
4104 // Ensure that all manifold ids on a polar cell really are set to the
4105 // polar manifold id:
4106 for (const auto &cell : tria.active_cell_iterators())
4107 if (cell->manifold_id() == polar_manifold_id)
4108 cell->set_all_manifold_ids(polar_manifold_id);
4109
4110 // Ensure that all other manifold ids (including the interior faces
4111 // opposite the cylinder) are set to the flat manifold id:
4112 for (const auto &cell : tria.active_cell_iterators())
4113 if (cell->manifold_id() != polar_manifold_id &&
4114 cell->manifold_id() != tfi_manifold_id)
4116
4117 // attach manifolds
4118 PolarManifold<2> polar_manifold(Point<2>(0., 0.));
4119 tria.set_manifold(polar_manifold_id, polar_manifold);
4120
4121 if (use_transfinite_region)
4122 {
4123 tria.set_manifold(tfi_manifold_id, FlatManifold<2>());
4125 inner_manifold.initialize(tria);
4126 tria.set_manifold(tfi_manifold_id, inner_manifold);
4127 }
4128
4129 if (colorize)
4130 for (const auto &face : tria.active_face_iterators())
4131 if (face->at_boundary())
4132 {
4133 const Point<2> center = face->center();
4134 // left side
4135 if (std::abs(center[0] - (-static_cast<double>(length_pre))) <
4136 1e-10)
4137 face->set_boundary_id(0);
4138 // right side
4139 else if (std::abs(center[0] - static_cast<double>(length_post)) <
4140 1e-10)
4141 face->set_boundary_id(1);
4142 // cylinder boundary
4143 else if (face->manifold_id() == polar_manifold_id)
4144 face->set_boundary_id(2);
4145 // bottom side
4146 else if (std::abs(center[1] -
4147 (-static_cast<double>(height_below))) < 1e-10)
4148 face->set_boundary_id(3);
4149 // top side
4150 else
4151 face->set_boundary_id(4);
4152 }
4153 }
4154
4155 template <>
4156 void
4158 Triangulation<3> &tria,
4159 const std::vector<unsigned int> &lengths_and_heights,
4160 const double depth,
4161 unsigned int depth_division,
4162 const double shell_region_radius,
4163 const unsigned int n_shells,
4164 const double skewness,
4165 const bool use_transfinite_region,
4166 const bool colorize)
4167 {
4168 Triangulation<2> tria_2;
4170 lengths_and_heights,
4171 depth,
4172 depth_division,
4173 shell_region_radius,
4174 n_shells,
4175 skewness,
4176 use_transfinite_region,
4177 colorize);
4178
4179 // extrude to 3d
4180 extrude_triangulation(tria_2, depth_division, depth, tria, true);
4181
4182 // set up the new 3d manifolds
4183 const types::manifold_id cylindrical_manifold_id = 0;
4184 const types::manifold_id tfi_manifold_id = 1;
4185 const PolarManifold<2> *const m_ptr =
4186 dynamic_cast<const PolarManifold<2> *>(
4187 &tria_2.get_manifold(cylindrical_manifold_id));
4188 Assert(m_ptr != nullptr, ExcInternalError());
4189 const Point<3> axial_point(m_ptr->get_center()[0],
4190 m_ptr->get_center()[1],
4191 0.0);
4192 const Tensor<1, 3> direction{{0.0, 0.0, 1.0}};
4193
4194 tria.set_manifold(cylindrical_manifold_id, FlatManifold<3>());
4195 tria.set_manifold(tfi_manifold_id, FlatManifold<3>());
4196 const CylindricalManifold<3> cylindrical_manifold(direction, axial_point);
4197
4198 tria.set_manifold(cylindrical_manifold_id, cylindrical_manifold);
4199
4200 if (use_transfinite_region)
4201 {
4203 inner_manifold.initialize(tria);
4204 tria.set_manifold(tfi_manifold_id, inner_manifold);
4205 }
4206
4207 // From extrude_triangulation: since the maximum boundary id of tria_2 was
4208 // 4, the front boundary id is 4 and the back is 5. They remain unchanged.
4209 }
4210
4211 template <int dim, int spacedim>
4212 void
4214 const std::vector<unsigned int> &sizes,
4215 const bool colorize)
4216 {
4218 Assert(dim > 1, ExcNotImplemented());
4219 Assert(dim < 4, ExcNotImplemented());
4220
4221 // If there is a desire at some point to change the geometry of
4222 // the cells, this tensor can be made an argument to the function.
4223 Tensor<1, dim> dimensions;
4224 for (unsigned int d = 0; d < dim; ++d)
4225 dimensions[d] = 1.;
4226
4227 std::vector<Point<spacedim>> points;
4228 unsigned int n_cells = 1;
4229 for (const unsigned int i : GeometryInfo<dim>::face_indices())
4230 n_cells += sizes[i];
4231
4232 std::vector<CellData<dim>> cells(n_cells);
4233 // Vertices of the center cell
4234 for (const unsigned int i : GeometryInfo<dim>::vertex_indices())
4235 {
4237 for (unsigned int d = 0; d < dim; ++d)
4238 p[d] = 0.5 * dimensions[d] *
4241 points.push_back(p);
4242 cells[0].vertices[i] = i;
4243 }
4244 cells[0].material_id = 0;
4245
4246 // The index of the first cell of the leg.
4247 unsigned int cell_index = 1;
4248 // The legs of the cross
4249 for (const unsigned int face : GeometryInfo<dim>::face_indices())
4250 {
4251 const unsigned int oface = GeometryInfo<dim>::opposite_face[face];
4252 const unsigned int dir = GeometryInfo<dim>::unit_normal_direction[face];
4253
4254 // We are moving in the direction of face
4255 for (unsigned int j = 0; j < sizes[face]; ++j, ++cell_index)
4256 {
4257 const unsigned int last_cell = (j == 0) ? 0U : (cell_index - 1);
4258
4259 for (unsigned int v = 0; v < GeometryInfo<dim>::vertices_per_face;
4260 ++v)
4261 {
4262 const unsigned int cellv =
4264 const unsigned int ocellv =
4266 // First the vertices which already exist
4267 cells[cell_index].vertices[ocellv] =
4268 cells[last_cell].vertices[cellv];
4269
4270 // Now the new vertices
4271 cells[cell_index].vertices[cellv] = points.size();
4272
4273 Point<spacedim> p = points[cells[cell_index].vertices[ocellv]];
4275 dimensions[dir];
4276 points.push_back(p);
4277 }
4278 cells[cell_index].material_id = (colorize) ? (face + 1U) : 0U;
4279 }
4280 }
4281 tria.create_triangulation(points, cells, SubCellData());
4282 }
4283
4284
4285 template <>
4286 void
4287 hyper_cube_slit(Triangulation<1> &, const double, const double, const bool)
4288 {
4290 }
4291
4292
4293
4294 template <>
4295 void
4297 const double,
4298 const double,
4299 const double,
4300 const bool)
4301 {
4303 }
4304
4305
4306
4307 template <>
4308 void
4309 hyper_L(Triangulation<1> &, const double, const double, const bool)
4310 {
4312 }
4313
4314
4315
4316 template <>
4317 void
4318 hyper_ball_balanced(Triangulation<1> &, const Point<1> &, const double)
4319 {
4321 }
4322
4323
4324
4325 template <>
4326 void
4327 cylinder(Triangulation<1> &, const double, const double)
4328 {
4330 }
4331
4332
4333 template <>
4334 void
4336 const unsigned int,
4337 const double,
4338 const double)
4339 {
4341 }
4342
4343
4344
4345 template <>
4346 void
4347 truncated_cone(Triangulation<1> &, const double, const double, const double)
4348 {
4350 }
4351
4352
4353
4354 template <>
4355 void
4357 const Point<1> &,
4358 const double,
4359 const double,
4360 const unsigned int,
4361 const bool)
4362 {
4364 }
4365
4366 template <>
4367 void
4369 const double,
4370 const double,
4371 const double,
4372 const unsigned int,
4373 const unsigned int,
4374 const bool)
4375 {
4377 }
4378
4379
4380 template <>
4381 void
4382 quarter_hyper_ball(Triangulation<1> &, const Point<1> &, const double)
4383 {
4385 }
4386
4387
4388 template <>
4389 void
4390 half_hyper_ball(Triangulation<1> &, const Point<1> &, const double)
4391 {
4393 }
4394
4395
4396 template <>
4397 void
4399 const Point<1> &,
4400 const double,
4401 const double,
4402 const unsigned int,
4403 const bool)
4404 {
4406 }
4407
4408 template <>
4409 void
4411 const Point<1> &,
4412 const double,
4413 const double,
4414 const unsigned int,
4415 const bool)
4416 {
4418 }
4419
4420 template <>
4421 void
4423 const double left,
4424 const double right,
4425 const double thickness,
4426 const bool colorize)
4427 {
4428 Assert(left < right,
4429 ExcMessage("Invalid left-to-right bounds of enclosed hypercube"));
4430
4431 std::vector<Point<2>> vertices(16);
4432 double coords[4];
4433 coords[0] = left - thickness;
4434 coords[1] = left;
4435 coords[2] = right;
4436 coords[3] = right + thickness;
4437
4438 unsigned int k = 0;
4439 for (const double y : coords)
4440 for (const double x : coords)
4441 vertices[k++] = Point<2>(x, y);
4442
4443 const types::material_id materials[9] = {5, 4, 6, 1, 0, 2, 9, 8, 10};
4444
4445 std::vector<CellData<2>> cells(9);
4446 k = 0;
4447 for (unsigned int i0 = 0; i0 < 3; ++i0)
4448 for (unsigned int i1 = 0; i1 < 3; ++i1)
4449 {
4450 cells[k].vertices[0] = i1 + 4 * i0;
4451 cells[k].vertices[1] = i1 + 4 * i0 + 1;
4452 cells[k].vertices[2] = i1 + 4 * i0 + 4;
4453 cells[k].vertices[3] = i1 + 4 * i0 + 5;
4454 if (colorize)
4455 cells[k].material_id = materials[k];
4456 ++k;
4457 }
4458 tria.create_triangulation(vertices,
4459 cells,
4460 SubCellData()); // no boundary information
4461 }
4462
4463
4464
4465 // Implementation for 2d only
4466 template <>
4467 void
4469 const double left,
4470 const double right,
4471 const bool colorize)
4472 {
4473 const double rl2 = (right + left) / 2;
4474 const Point<2> vertices[10] = {Point<2>(left, left),
4475 Point<2>(rl2, left),
4476 Point<2>(rl2, rl2),
4477 Point<2>(left, rl2),
4478 Point<2>(right, left),
4479 Point<2>(right, rl2),
4480 Point<2>(rl2, right),
4481 Point<2>(left, right),
4482 Point<2>(right, right),
4483 Point<2>(rl2, left)};
4484 const int cell_vertices[4][4] = {{0, 1, 3, 2},
4485 {9, 4, 2, 5},
4486 {3, 2, 7, 6},
4487 {2, 5, 6, 8}};
4488 std::vector<CellData<2>> cells(4, CellData<2>());
4489 for (unsigned int i = 0; i < 4; ++i)
4490 {
4491 for (unsigned int j = 0; j < 4; ++j)
4492 cells[i].vertices[j] = cell_vertices[i][j];
4493 cells[i].material_id = 0;
4494 }
4495 tria.create_triangulation(std::vector<Point<2>>(std::begin(vertices),
4496 std::end(vertices)),
4497 cells,
4498 SubCellData()); // no boundary information
4499
4500 if (colorize)
4501 {
4503 cell->face(1)->set_boundary_id(1);
4504 ++cell;
4505 cell->face(0)->set_boundary_id(2);
4506 }
4507 }
4508
4509
4510
4511 template <>
4512 void
4513 truncated_cone(Triangulation<2> &triangulation,
4514 const double radius_0,
4515 const double radius_1,
4516 const double half_length)
4517 {
4518 Point<2> vertices_tmp[4];
4519
4520 vertices_tmp[0] = Point<2>(-half_length, -radius_0);
4521 vertices_tmp[1] = Point<2>(half_length, -radius_1);
4522 vertices_tmp[2] = Point<2>(-half_length, radius_0);
4523 vertices_tmp[3] = Point<2>(half_length, radius_1);
4524
4525 const std::vector<Point<2>> vertices(std::begin(vertices_tmp),
4526 std::end(vertices_tmp));
4527 unsigned int cell_vertices[1][GeometryInfo<2>::vertices_per_cell];
4528
4529 for (const unsigned int i : GeometryInfo<2>::vertex_indices())
4530 cell_vertices[0][i] = i;
4531
4532 std::vector<CellData<2>> cells(1, CellData<2>());
4533
4534 for (const unsigned int i : GeometryInfo<2>::vertex_indices())
4535 cells[0].vertices[i] = cell_vertices[0][i];
4536
4537 cells[0].material_id = 0;
4538 triangulation.create_triangulation(vertices, cells, SubCellData());
4539
4540 Triangulation<2>::cell_iterator cell = triangulation.begin();
4541
4542 cell->face(0)->set_boundary_id(1);
4543 cell->face(1)->set_boundary_id(2);
4544
4545 for (unsigned int i = 2; i < 4; ++i)
4546 cell->face(i)->set_boundary_id(0);
4547 }
4548
4549
4550
4551 // Implementation for 2d only
4552 template <>
4553 void
4555 const double a,
4556 const double b,
4557 const bool colorize)
4558 {
4559 const Point<2> vertices[8] = {Point<2>(a, a),
4560 Point<2>((a + b) / 2, a),
4561 Point<2>(b, a),
4562 Point<2>(a, (a + b) / 2),
4563 Point<2>((a + b) / 2, (a + b) / 2),
4564 Point<2>(b, (a + b) / 2),
4565 Point<2>(a, b),
4566 Point<2>((a + b) / 2, b)};
4567 const int cell_vertices[3][4] = {{0, 1, 3, 4}, {1, 2, 4, 5}, {3, 4, 6, 7}};
4568
4569 std::vector<CellData<2>> cells(3, CellData<2>());
4570
4571 for (unsigned int i = 0; i < 3; ++i)
4572 {
4573 for (unsigned int j = 0; j < 4; ++j)
4574 cells[i].vertices[j] = cell_vertices[i][j];
4575 cells[i].material_id = 0;
4576 }
4577
4578 tria.create_triangulation(std::vector<Point<2>>(std::begin(vertices),
4579 std::end(vertices)),
4580 cells,
4581 SubCellData());
4582
4583 if (colorize)
4584 {
4586
4587 cell->face(0)->set_boundary_id(0);
4588 cell->face(2)->set_boundary_id(1);
4589 ++cell;
4590
4591 cell->face(1)->set_boundary_id(2);
4592 cell->face(2)->set_boundary_id(1);
4593 cell->face(3)->set_boundary_id(3);
4594 ++cell;
4595
4596 cell->face(0)->set_boundary_id(0);
4597 cell->face(1)->set_boundary_id(4);
4598 cell->face(3)->set_boundary_id(5);
4599 }
4600 }
4601
4602
4603
4604 template <int dim, int spacedim>
4605 void
4607 const std::vector<unsigned int> &repetitions,
4608 const Point<dim> &bottom_left,
4609 const Point<dim> &top_right,
4610 const std::vector<int> &n_cells_to_remove)
4611 {
4612 Assert(dim > 1, ExcNotImplemented());
4613 // Check the consistency of the dimensions provided.
4614 AssertDimension(repetitions.size(), dim);
4615 AssertDimension(n_cells_to_remove.size(), dim);
4616 for (unsigned int d = 0; d < dim; ++d)
4617 {
4618 Assert(std::fabs(n_cells_to_remove[d]) <= repetitions[d],
4619 ExcMessage("Attempting to cut away too many cells."));
4620 }
4621 // Create the domain to be cut
4624 repetitions,
4625 bottom_left,
4626 top_right);
4627 // compute the vertex of the cut step, we will cut according to the
4628 // location of the cartesian coordinates of the cell centers
4629 std::array<double, dim> h;
4630 Point<dim> cut_step;
4631 for (unsigned int d = 0; d < dim; ++d)
4632 {
4633 // mesh spacing in each direction in cartesian coordinates
4634 h[d] = (top_right[d] - bottom_left[d]) / repetitions[d];
4635 // left to right, bottom to top, front to back
4636 if (n_cells_to_remove[d] >= 0)
4637 {
4638 // cartesian coordinates of vertex location
4639 cut_step[d] =
4640 h[d] * std::fabs(n_cells_to_remove[d]) + bottom_left[d];
4641 }
4642 // right to left, top to bottom, back to front
4643 else
4644 {
4645 cut_step[d] = top_right[d] - h[d] * std::fabs(n_cells_to_remove[d]);
4646 }
4647 }
4648
4649
4650 // compute cells to remove
4651 std::set<typename Triangulation<dim, spacedim>::active_cell_iterator>
4652 cells_to_remove;
4653 for (const auto &cell : rectangle.active_cell_iterators())
4654 {
4655 bool remove_cell = true;
4656 for (unsigned int d = 0; d < dim && remove_cell; ++d)
4657 if ((n_cells_to_remove[d] > 0 && cell->center()[d] >= cut_step[d]) ||
4658 (n_cells_to_remove[d] < 0 && cell->center()[d] <= cut_step[d]))
4659 remove_cell = false;
4660 if (remove_cell)
4661 cells_to_remove.insert(cell);
4662 }
4663
4665 cells_to_remove,
4666 tria);
4667 }
4668
4669
4670
4671 template <int dim, int spacedim>
4672 void
4674 const Point<spacedim> &p,
4675 const double radius,
4676 const bool internal_manifolds)
4677 {
4678 if constexpr (dim == 2)
4679 {
4680 const auto embed_point = [](const double x,
4681 const double y) -> Point<spacedim> {
4682 if constexpr (spacedim == 2)
4683 return Point<spacedim>(x, y);
4684 else if constexpr (spacedim == 3)
4685 return Point<spacedim>(x, y, 0);
4686 else
4688 };
4689
4690
4691 // Equilibrate cell sizes at transition from the inner part
4692 // to the radial cells
4693 const double a = 1. / (1 + std::sqrt(2.0));
4694 const Point<spacedim> vertices[8] = {
4695 p + embed_point(-1., -1.) * (radius / std::sqrt(2.0)),
4696 p + embed_point(+1., -1.) * (radius / std::sqrt(2.0)),
4697 p + embed_point(-1., -1.) * (radius / std::sqrt(2.0) * a),
4698 p + embed_point(+1., -1.) * (radius / std::sqrt(2.0) * a),
4699 p + embed_point(-1., +1.) * (radius / std::sqrt(2.0) * a),
4700 p + embed_point(+1., +1.) * (radius / std::sqrt(2.0) * a),
4701 p + embed_point(-1., +1.) * (radius / std::sqrt(2.0)),
4702 p + embed_point(+1., +1.) * (radius / std::sqrt(2.0))};
4703
4704 std::vector<CellData<2>> cells(5, CellData<2>());
4705
4706 for (unsigned int i = 0; i < 5; ++i)
4707 {
4708 for (unsigned int j = 0; j < 4; ++j)
4709 cells[i].vertices[j] = circle_cell_vertices[i][j];
4710 cells[i].material_id = 0;
4711 cells[i].manifold_id = (i == 2 ? numbers::flat_manifold_id : 1);
4712 }
4713
4714 tria.create_triangulation(std::vector<Point<spacedim>>(
4715 std::begin(vertices), std::end(vertices)),
4716 cells,
4717 SubCellData()); // no boundary information
4720 if (internal_manifolds)
4722 else
4724 }
4725 else if constexpr (dim == 3)
4726 {
4727 const double a =
4728 1. / (1 + std::sqrt(3.0)); // equilibrate cell sizes at transition
4729 // from the inner part to the radial
4730 // cells
4731 const unsigned int n_vertices = 16;
4732 const Point<3> vertices[n_vertices] = {
4733 // first the vertices of the inner
4734 // cell
4735 p + Point<3>(-1, -1, -1) * (radius / std::sqrt(3.0) * a),
4736 p + Point<3>(+1, -1, -1) * (radius / std::sqrt(3.0) * a),
4737 p + Point<3>(+1, -1, +1) * (radius / std::sqrt(3.0) * a),
4738 p + Point<3>(-1, -1, +1) * (radius / std::sqrt(3.0) * a),
4739 p + Point<3>(-1, +1, -1) * (radius / std::sqrt(3.0) * a),
4740 p + Point<3>(+1, +1, -1) * (radius / std::sqrt(3.0) * a),
4741 p + Point<3>(+1, +1, +1) * (radius / std::sqrt(3.0) * a),
4742 p + Point<3>(-1, +1, +1) * (radius / std::sqrt(3.0) * a),
4743 // now the eight vertices at
4744 // the outer sphere
4745 p + Point<3>(-1, -1, -1) * (radius / std::sqrt(3.0)),
4746 p + Point<3>(+1, -1, -1) * (radius / std::sqrt(3.0)),
4747 p + Point<3>(+1, -1, +1) * (radius / std::sqrt(3.0)),
4748 p + Point<3>(-1, -1, +1) * (radius / std::sqrt(3.0)),
4749 p + Point<3>(-1, +1, -1) * (radius / std::sqrt(3.0)),
4750 p + Point<3>(+1, +1, -1) * (radius / std::sqrt(3.0)),
4751 p + Point<3>(+1, +1, +1) * (radius / std::sqrt(3.0)),
4752 p + Point<3>(-1, +1, +1) * (radius / std::sqrt(3.0)),
4753 };
4754
4755 // one needs to draw the seven cubes to
4756 // understand what's going on here
4757 const unsigned int n_cells = 7;
4758 const int cell_vertices[n_cells][8] = {
4759 {0, 1, 4, 5, 3, 2, 7, 6}, // center
4760 {8, 9, 12, 13, 0, 1, 4, 5}, // bottom
4761 {9, 13, 1, 5, 10, 14, 2, 6}, // right
4762 {11, 10, 3, 2, 15, 14, 7, 6}, // top
4763 {8, 0, 12, 4, 11, 3, 15, 7}, // left
4764 {8, 9, 0, 1, 11, 10, 3, 2}, // front
4765 {12, 4, 13, 5, 15, 7, 14, 6}}; // back
4766
4767 std::vector<CellData<3>> cells(n_cells, CellData<3>());
4768
4769 for (unsigned int i = 0; i < n_cells; ++i)
4770 {
4771 for (const unsigned int j : GeometryInfo<3>::vertex_indices())
4772 cells[i].vertices[j] = cell_vertices[i][j];
4773 cells[i].material_id = 0;
4774 cells[i].manifold_id = i == 0 ? numbers::flat_manifold_id : 1;
4775 }
4776
4777 tria.create_triangulation(std::vector<Point<3>>(std::begin(vertices),
4778 std::end(vertices)),
4779 cells,
4780 SubCellData()); // no boundary information
4783 if (internal_manifolds)
4785 else
4786 tria.set_manifold(1, FlatManifold<3>());
4787 }
4788 else
4790 }
4791
4792
4793
4794 template <int spacedim>
4795 void
4796 hyper_shell_2D(Triangulation<2, spacedim> &tria,
4797 const Point<spacedim> &center,
4798 const double inner_radius,
4799 const double outer_radius,
4800 const unsigned int n_cells,
4801 const bool colorize)
4802 {
4803 Assert((inner_radius > 0) && (inner_radius < outer_radius),
4804 ExcInvalidRadii());
4805
4806 const double pi = numbers::PI;
4807
4808 // determine the number of cells
4809 // for the grid. if not provided by
4810 // the user determine it such that
4811 // the length of each cell on the
4812 // median (in the middle between
4813 // the two circles) is equal to its
4814 // radial extent (which is the
4815 // difference between the two
4816 // radii)
4817 const unsigned int N =
4818 (n_cells == 0 ? static_cast<unsigned int>(
4819 std::ceil((2 * pi * (outer_radius + inner_radius) / 2) /
4820 (outer_radius - inner_radius))) :
4821 n_cells);
4822
4823 // set up N vertices on the
4824 // outer and N vertices on
4825 // the inner circle. the
4826 // first N ones are on the
4827 // outer one, and all are
4828 // numbered counter-clockwise
4829 std::vector<Point<spacedim>> vertices(2 * N);
4830 for (unsigned int i = 0; i < N; ++i)
4831 {
4833 point[0] = std::cos(2 * pi * i / N);
4834 point[1] = std::sin(2 * pi * i / N);
4835
4836 vertices[i] = point * outer_radius;
4837 vertices[i + N] = vertices[i] * (inner_radius / outer_radius);
4838
4839 vertices[i] += center;
4840 vertices[i + N] += center;
4841 }
4842
4843 std::vector<CellData<2>> cells(N, CellData<2>());
4844
4845 for (unsigned int i = 0; i < N; ++i)
4846 {
4847 cells[i].vertices[0] = i;
4848 cells[i].vertices[1] = (i + 1) % N;
4849 cells[i].vertices[2] = N + i;
4850 cells[i].vertices[3] = N + ((i + 1) % N);
4851
4852 cells[i].material_id = 0;
4853 }
4854
4855 tria.create_triangulation(vertices, cells, SubCellData());
4856
4857 if (colorize)
4858 colorize_hyper_shell(tria, center, inner_radius, outer_radius);
4859
4860 tria.set_all_manifold_ids(0);
4862 }
4863
4864
4865
4866 template <>
4867 void
4869 const Point<2> &center,
4870 const double inner_radius,
4871 const double outer_radius,
4872 const unsigned int n_cells,
4873 const bool colorize)
4874 {
4875 hyper_shell_2D(tria, center, inner_radius, outer_radius, n_cells, colorize);
4876 }
4877
4878
4879
4880 template <>
4881 void
4883 const Point<3> &center,
4884 const double inner_radius,
4885 const double outer_radius,
4886 const unsigned int n_cells,
4887 const bool colorize)
4888 {
4889 hyper_shell_2D(tria, center, inner_radius, outer_radius, n_cells, colorize);
4890 }
4891
4892
4893
4894 template <int dim>
4895 void
4897 const Point<dim> &inner_center,
4898 const Point<dim> &outer_center,
4899 const double inner_radius,
4900 const double outer_radius,
4901 const unsigned int n_cells)
4902 {
4904 tria, outer_center, inner_radius, outer_radius, n_cells, true);
4905
4906 // check the consistency of the dimensions provided
4907 Assert(
4908 outer_radius - inner_radius > outer_center.distance(inner_center),
4910 "The inner radius is greater than or equal to the outer radius plus eccentricity."));
4911
4912 // shift nodes along the inner boundary according to the position of
4913 // inner_circle
4914 std::set<Point<dim> *> vertices_to_move;
4915
4916 for (const auto &face : tria.active_face_iterators())
4917 if (face->boundary_id() == 0)
4918 for (unsigned int v = 0; v < GeometryInfo<dim>::vertices_per_face; ++v)
4919 vertices_to_move.insert(&face->vertex(v));
4920
4921 const auto shift = inner_center - outer_center;
4922 for (const auto &p : vertices_to_move)
4923 (*p) += shift;
4924
4925 // the original hyper_shell function assigns the same manifold id
4926 // to all cells and faces. Set all manifolds ids to a different
4927 // value (2), then use boundary ids to assign different manifolds to
4928 // the inner (0) and outer manifolds (1). Use a transfinite manifold
4929 // for all faces and cells aside from the boundaries.
4930 tria.set_all_manifold_ids(2);
4932
4933 SphericalManifold<dim> inner_manifold(inner_center);
4934 SphericalManifold<dim> outer_manifold(outer_center);
4935
4940 transfinite.initialize(tria);
4941
4942 tria.set_manifold(0, inner_manifold);
4943 tria.set_manifold(1, outer_manifold);
4944 tria.set_manifold(2, transfinite);
4945 }
4946
4947
4948
4949 // Implementation for 2d only
4950 template <>
4951 void
4953 const double radius,
4954 const double half_length)
4955 {
4956 Point<2> p1(-half_length, -radius);
4957 Point<2> p2(half_length, radius);
4958
4959 hyper_rectangle(tria, p1, p2, true);
4960
4963 while (f != end)
4964 {
4965 switch (f->boundary_id())
4966 {
4967 case 0:
4968 f->set_boundary_id(1);
4969 break;
4970 case 1:
4971 f->set_boundary_id(2);
4972 break;
4973 default:
4974 f->set_boundary_id(0);
4975 break;
4976 }
4977 ++f;
4978 }
4979 }
4980
4981 template <>
4982 void
4984 const unsigned int,
4985 const double,
4986 const double)
4987 {
4989 }
4990
4991
4992
4993 // Implementation for 2d only
4994 template <>
4995 void
4997 const double,
4998 const double,
4999 const double,
5000 const unsigned int,
5001 const unsigned int,
5002 const bool)
5003 {
5005 }
5006
5007
5008 template <>
5009 void
5011 const Point<2> &p,
5012 const double radius)
5013 {
5014 const unsigned int dim = 2;
5015
5016 // the numbers 0.55647 and 0.42883 have been found by a search for the
5017 // best aspect ratio (defined as the maximal between the minimal singular
5018 // value of the Jacobian)
5019 const Point<dim> vertices[7] = {p + Point<dim>(0, 0) * radius,
5020 p + Point<dim>(+1, 0) * radius,
5021 p + Point<dim>(+1, 0) * (radius * 0.55647),
5022 p + Point<dim>(0, +1) * (radius * 0.55647),
5023 p + Point<dim>(+1, +1) * (radius * 0.42883),
5024 p + Point<dim>(0, +1) * radius,
5025 p + Point<dim>(+1, +1) *
5026 (radius / std::sqrt(2.0))};
5027
5028 const int cell_vertices[3][4] = {{0, 2, 3, 4}, {1, 6, 2, 4}, {5, 3, 6, 4}};
5029
5030 std::vector<CellData<dim>> cells(3, CellData<dim>());
5031
5032 for (unsigned int i = 0; i < 3; ++i)
5033 {
5034 for (unsigned int j = 0; j < 4; ++j)
5035 cells[i].vertices[j] = cell_vertices[i][j];
5036 cells[i].material_id = 0;
5037 }
5038
5039 tria.create_triangulation(std::vector<Point<dim>>(std::begin(vertices),
5040 std::end(vertices)),
5041 cells,
5042 SubCellData()); // no boundary information
5043
5046
5048
5049 while (cell != end)
5050 {
5051 for (const unsigned int i : GeometryInfo<dim>::face_indices())
5052 {
5053 if (cell->face(i)->boundary_id() ==
5055 continue;
5056
5057 // If one the components is the same as the respective
5058 // component of the center, then this is part of the plane
5059 if (cell->face(i)->center()[0] < p[0] + 1.e-5 * radius ||
5060 cell->face(i)->center()[1] < p[1] + 1.e-5 * radius)
5061 {
5062 cell->face(i)->set_boundary_id(1);
5063 cell->face(i)->set_manifold_id(numbers::flat_manifold_id);
5064 }
5065 }
5066 ++cell;
5067 }
5069 }
5070
5071
5072 template <>
5073 void
5075 const Point<2> &p,
5076 const double radius)
5077 {
5078 // equilibrate cell sizes at
5079 // transition from the inner part
5080 // to the radial cells
5081 const double a = 1. / (1 + std::sqrt(2.0));
5082 const Point<2> vertices[8] = {
5083 p + Point<2>(0, -1) * radius,
5084 p + Point<2>(+1, -1) * (radius / std::sqrt(2.0)),
5085 p + Point<2>(0, -1) * (radius / std::sqrt(2.0) * a),
5086 p + Point<2>(+1, -1) * (radius / std::sqrt(2.0) * a),
5087 p + Point<2>(0, +1) * (radius / std::sqrt(2.0) * a),
5088 p + Point<2>(+1, +1) * (radius / std::sqrt(2.0) * a),
5089 p + Point<2>(0, +1) * radius,
5090 p + Point<2>(+1, +1) * (radius / std::sqrt(2.0))};
5091
5092 const int cell_vertices[5][4] = {{0, 1, 2, 3},
5093 {2, 3, 4, 5},
5094 {1, 7, 3, 5},
5095 {6, 4, 7, 5}};
5096
5097 std::vector<CellData<2>> cells(4, CellData<2>());
5098
5099 for (unsigned int i = 0; i < 4; ++i)
5100 {
5101 for (unsigned int j = 0; j < 4; ++j)
5102 cells[i].vertices[j] = cell_vertices[i][j];
5103 cells[i].material_id = 0;
5104 }
5105
5106 tria.create_triangulation(std::vector<Point<2>>(std::begin(vertices),
5107 std::end(vertices)),
5108 cells,
5109 SubCellData()); // no boundary information
5110
5113
5115
5116 while (cell != end)
5117 {
5118 for (const unsigned int i : GeometryInfo<2>::face_indices())
5119 {
5120 if (cell->face(i)->boundary_id() ==
5122 continue;
5123
5124 // If x is zero, then this is part of the plane
5125 if (cell->face(i)->center()[0] < p[0] + 1.e-5 * radius)
5126 {
5127 cell->face(i)->set_boundary_id(1);
5128 cell->face(i)->set_manifold_id(numbers::flat_manifold_id);
5129 }
5130 }
5131 ++cell;
5132 }
5134 }
5135
5136
5137
5138 // Implementation for 2d only
5139 template <>
5140 void
5142 const Point<2> &center,
5143 const double inner_radius,
5144 const double outer_radius,
5145 const unsigned int n_cells,
5146 const bool colorize)
5147 {
5148 Assert((inner_radius > 0) && (inner_radius < outer_radius),
5149 ExcInvalidRadii());
5150
5151 const double pi = numbers::PI;
5152 // determine the number of cells
5153 // for the grid. if not provided by
5154 // the user determine it such that
5155 // the length of each cell on the
5156 // median (in the middle between
5157 // the two circles) is equal to its
5158 // radial extent (which is the
5159 // difference between the two
5160 // radii)
5161 const unsigned int N =
5162 (n_cells == 0 ? static_cast<unsigned int>(
5163 std::ceil((pi * (outer_radius + inner_radius) / 2) /
5164 (outer_radius - inner_radius))) :
5165 n_cells);
5166
5167 // set up N+1 vertices on the
5168 // outer and N+1 vertices on
5169 // the inner circle. the
5170 // first N+1 ones are on the
5171 // outer one, and all are
5172 // numbered counter-clockwise
5173 std::vector<Point<2>> vertices(2 * (N + 1));
5174 for (unsigned int i = 0; i <= N; ++i)
5175 {
5176 // enforce that the x-coordinates
5177 // of the first and last point of
5178 // each half-circle are exactly
5179 // zero (contrary to what we may
5180 // compute using the imprecise
5181 // value of pi)
5182 vertices[i] =
5183 Point<2>(((i == 0) || (i == N) ? 0 : std::cos(pi * i / N - pi / 2)),
5184 std::sin(pi * i / N - pi / 2)) *
5186 vertices[i + N + 1] = vertices[i] * (inner_radius / outer_radius);
5187
5188 vertices[i] += center;
5189 vertices[i + N + 1] += center;
5190 }
5191
5192
5193 std::vector<CellData<2>> cells(N, CellData<2>());
5194
5195 for (unsigned int i = 0; i < N; ++i)
5196 {
5197 cells[i].vertices[0] = i;
5198 cells[i].vertices[1] = (i + 1) % (N + 1);
5199 cells[i].vertices[2] = N + 1 + i;
5200 cells[i].vertices[3] = N + 1 + ((i + 1) % (N + 1));
5201
5202 cells[i].material_id = 0;
5203 }
5204
5205 tria.create_triangulation(vertices, cells, SubCellData());
5206
5207 if (colorize)
5208 {
5210 for (; cell != tria.end(); ++cell)
5211 {
5212 cell->face(2)->set_boundary_id(1);
5213 }
5214 tria.begin()->face(0)->set_boundary_id(3);
5215
5216 tria.last()->face(1)->set_boundary_id(2);
5217 }
5218 tria.set_all_manifold_ids(0);
5219 tria.set_manifold(0, SphericalManifold<2>(center));
5220 }
5221
5222
5223 template <>
5224 void
5226 const Point<2> &center,
5227 const double inner_radius,
5228 const double outer_radius,
5229 const unsigned int n_cells,
5230 const bool colorize)
5231 {
5232 Assert((inner_radius > 0) && (inner_radius < outer_radius),
5233 ExcInvalidRadii());
5234
5235 const double pi = numbers::PI;
5236 // determine the number of cells
5237 // for the grid. if not provided by
5238 // the user determine it such that
5239 // the length of each cell on the
5240 // median (in the middle between
5241 // the two circles) is equal to its
5242 // radial extent (which is the
5243 // difference between the two
5244 // radii)
5245 const unsigned int N =
5246 (n_cells == 0 ? static_cast<unsigned int>(
5247 std::ceil((pi * (outer_radius + inner_radius) / 4) /
5248 (outer_radius - inner_radius))) :
5249 n_cells);
5250
5251 // set up N+1 vertices on the
5252 // outer and N+1 vertices on
5253 // the inner circle. the
5254 // first N+1 ones are on the
5255 // outer one, and all are
5256 // numbered counter-clockwise
5257 std::vector<Point<2>> vertices(2 * (N + 1));
5258 for (unsigned int i = 0; i <= N; ++i)
5259 {
5260 // enforce that the x-coordinates
5261 // of the last point is exactly
5262 // zero (contrary to what we may
5263 // compute using the imprecise
5264 // value of pi)
5265 vertices[i] = Point<2>(((i == N) ? 0 : std::cos(pi * i / N / 2)),
5266 std::sin(pi * i / N / 2)) *
5268 vertices[i + N + 1] = vertices[i] * (inner_radius / outer_radius);
5269
5270 vertices[i] += center;
5271 vertices[i + N + 1] += center;
5272 }
5273
5274
5275 std::vector<CellData<2>> cells(N, CellData<2>());
5276
5277 for (unsigned int i = 0; i < N; ++i)
5278 {
5279 cells[i].vertices[0] = i;
5280 cells[i].vertices[1] = (i + 1) % (N + 1);
5281 cells[i].vertices[2] = N + 1 + i;
5282 cells[i].vertices[3] = N + 1 + ((i + 1) % (N + 1));
5283
5284 cells[i].material_id = 0;
5285 }
5286
5287 tria.create_triangulation(vertices, cells, SubCellData());
5288
5289 if (colorize)
5290 {
5292 for (; cell != tria.end(); ++cell)
5293 {
5294 cell->face(2)->set_boundary_id(1);
5295 }
5296 tria.begin()->face(0)->set_boundary_id(3);
5297
5298 tria.last()->face(1)->set_boundary_id(2);
5299 }
5300
5301 tria.set_all_manifold_ids(0);
5302 tria.set_manifold(0, SphericalManifold<2>(center));
5303 }
5304
5305
5306
5307 // Implementation for 3d only
5308 template <>
5309 void
5311 const double left,
5312 const double right,
5313 const bool colorize)
5314 {
5315 const double rl2 = (right + left) / 2;
5316 const double len = (right - left) / 2.;
5317
5318 const Point<3> vertices[20] = {
5319 Point<3>(left, left, -len / 2.), Point<3>(rl2, left, -len / 2.),
5320 Point<3>(rl2, rl2, -len / 2.), Point<3>(left, rl2, -len / 2.),
5321 Point<3>(right, left, -len / 2.), Point<3>(right, rl2, -len / 2.),
5322 Point<3>(rl2, right, -len / 2.), Point<3>(left, right, -len / 2.),
5323 Point<3>(right, right, -len / 2.), Point<3>(rl2, left, -len / 2.),
5324 Point<3>(left, left, len / 2.), Point<3>(rl2, left, len / 2.),
5325 Point<3>(rl2, rl2, len / 2.), Point<3>(left, rl2, len / 2.),
5326 Point<3>(right, left, len / 2.), Point<3>(right, rl2, len / 2.),
5327 Point<3>(rl2, right, len / 2.), Point<3>(left, right, len / 2.),
5328 Point<3>(right, right, len / 2.), Point<3>(rl2, left, len / 2.)};
5329 const int cell_vertices[4][8] = {{0, 1, 3, 2, 10, 11, 13, 12},
5330 {9, 4, 2, 5, 19, 14, 12, 15},
5331 {3, 2, 7, 6, 13, 12, 17, 16},
5332 {2, 5, 6, 8, 12, 15, 16, 18}};
5333 std::vector<CellData<3>> cells(4, CellData<3>());
5334 for (unsigned int i = 0; i < 4; ++i)
5335 {
5336 for (unsigned int j = 0; j < 8; ++j)
5337 cells[i].vertices[j] = cell_vertices[i][j];
5338 cells[i].material_id = 0;
5339 }
5340 tria.create_triangulation(std::vector<Point<3>>(std::begin(vertices),
5341 std::end(vertices)),
5342 cells,
5343 SubCellData()); // no boundary information
5344
5345 if (colorize)
5346 {
5348 cell->face(1)->set_boundary_id(1);
5349 ++cell;
5350 cell->face(0)->set_boundary_id(2);
5351 }
5352 }
5353
5354
5355
5356 // Implementation for 3d only
5357 template <>
5358 void
5360 const double left,
5361 const double right,
5362 const double thickness,
5363 const bool colorize)
5364 {
5365 Assert(left < right,
5366 ExcMessage("Invalid left-to-right bounds of enclosed hypercube"));
5367
5368 std::vector<Point<3>> vertices(64);
5369 double coords[4];
5370 coords[0] = left - thickness;
5371 coords[1] = left;
5372 coords[2] = right;
5373 coords[3] = right + thickness;
5374
5375 unsigned int k = 0;
5376 for (const double z : coords)
5377 for (const double y : coords)
5378 for (const double x : coords)
5379 vertices[k++] = Point<3>(x, y, z);
5380
5381 const types::material_id materials[27] = {21, 20, 22, 17, 16, 18, 25,
5382 24, 26, 5, 4, 6, 1, 0,
5383 2, 9, 8, 10, 37, 36, 38,
5384 33, 32, 34, 41, 40, 42};
5385
5386 std::vector<CellData<3>> cells(27);
5387 k = 0;
5388 for (unsigned int z = 0; z < 3; ++z)
5389 for (unsigned int y = 0; y < 3; ++y)
5390 for (unsigned int x = 0; x < 3; ++x)
5391 {
5392 cells[k].vertices[0] = x + 4 * y + 16 * z;
5393 cells[k].vertices[1] = x + 4 * y + 16 * z + 1;
5394 cells[k].vertices[2] = x + 4 * y + 16 * z + 4;
5395 cells[k].vertices[3] = x + 4 * y + 16 * z + 5;
5396 cells[k].vertices[4] = x + 4 * y + 16 * z + 16;
5397 cells[k].vertices[5] = x + 4 * y + 16 * z + 17;
5398 cells[k].vertices[6] = x + 4 * y + 16 * z + 20;
5399 cells[k].vertices[7] = x + 4 * y + 16 * z + 21;
5400 if (colorize)
5401 cells[k].material_id = materials[k];
5402 ++k;
5403 }
5404 tria.create_triangulation(vertices,
5405 cells,
5406 SubCellData()); // no boundary information
5407 }
5408
5409
5410
5411 template <>
5412 void
5413 truncated_cone(Triangulation<3> &triangulation,
5414 const double radius_0,
5415 const double radius_1,
5416 const double half_length)
5417 {
5418 Assert(triangulation.n_cells() == 0,
5419 ExcMessage("The output triangulation object needs to be empty."));
5420 Assert(0 < radius_0, ExcMessage("The radii must be positive."));
5421 Assert(0 < radius_1, ExcMessage("The radii must be positive."));
5422 Assert(0 < half_length, ExcMessage("The half length must be positive."));
5423
5424 const auto n_slices = 1 + static_cast<unsigned int>(std::ceil(
5425 half_length / std::max(radius_0, radius_1)));
5426
5427 Triangulation<2> triangulation_2;
5428 GridGenerator::hyper_ball(triangulation_2, Point<2>(), radius_0);
5430 n_slices,
5431 2 * half_length,
5432 triangulation);
5433 GridTools::rotate(Tensor<1, 3>({0., 1., 0.}), numbers::PI_2, triangulation);
5434 GridTools::shift(Tensor<1, 3>({-half_length, 0.0, 0.0}), triangulation);
5435 // At this point we have a cylinder. Multiply the y and z coordinates by a
5436 // factor that scales (with x) linearly between radius_0 and radius_1 to fix
5437 // the circle radii and interior points:
5438 auto shift_radii = [=](const Point<3> &p) {
5439 const double slope = (radius_1 / radius_0 - 1.0) / (2.0 * half_length);
5440 const double factor = slope * (p[0] - -half_length) + 1.0;
5441 return Point<3>(p[0], factor * p[1], factor * p[2]);
5442 };
5443 GridTools::transform(shift_radii, triangulation);
5444
5445 // Set boundary ids at -half_length to 1 and at half_length to 2. Set the
5446 // manifold id on hull faces (i.e., faces not on either end) to 0.
5447 for (const auto &face : triangulation.active_face_iterators())
5448 if (face->at_boundary())
5449 {
5450 if (std::abs(face->center()[0] - -half_length) < 1e-8 * half_length)
5451 face->set_boundary_id(1);
5452 else if (std::abs(face->center()[0] - half_length) <
5453 1e-8 * half_length)
5454 face->set_boundary_id(2);
5455 else
5456 face->set_all_manifold_ids(0);
5457 }
5458
5459 triangulation.set_manifold(0, CylindricalManifold<3>());
5460 }
5461
5462
5463 // Implementation for 3d only
5464 template <>
5465 void
5467 const double a,
5468 const double b,
5469 const bool colorize)
5470 {
5471 // we slice out the top back right
5472 // part of the cube
5473 const Point<3> vertices[26] = {
5474 // front face of the big cube
5475 Point<3>(a, a, a),
5476 Point<3>((a + b) / 2, a, a),
5477 Point<3>(b, a, a),
5478 Point<3>(a, a, (a + b) / 2),
5479 Point<3>((a + b) / 2, a, (a + b) / 2),
5480 Point<3>(b, a, (a + b) / 2),
5481 Point<3>(a, a, b),
5482 Point<3>((a + b) / 2, a, b),
5483 Point<3>(b, a, b),
5484 // middle face of the big cube
5485 Point<3>(a, (a + b) / 2, a),
5486 Point<3>((a + b) / 2, (a + b) / 2, a),
5487 Point<3>(b, (a + b) / 2, a),
5488 Point<3>(a, (a + b) / 2, (a + b) / 2),
5489 Point<3>((a + b) / 2, (a + b) / 2, (a + b) / 2),
5490 Point<3>(b, (a + b) / 2, (a + b) / 2),
5491 Point<3>(a, (a + b) / 2, b),
5492 Point<3>((a + b) / 2, (a + b) / 2, b),
5493 Point<3>(b, (a + b) / 2, b),
5494 // back face of the big cube
5495 // last (top right) point is missing
5496 Point<3>(a, b, a),
5497 Point<3>((a + b) / 2, b, a),
5498 Point<3>(b, b, a),
5499 Point<3>(a, b, (a + b) / 2),
5500 Point<3>((a + b) / 2, b, (a + b) / 2),
5501 Point<3>(b, b, (a + b) / 2),
5502 Point<3>(a, b, b),
5503 Point<3>((a + b) / 2, b, b)};
5504 const int cell_vertices[7][8] = {{0, 1, 9, 10, 3, 4, 12, 13},
5505 {1, 2, 10, 11, 4, 5, 13, 14},
5506 {3, 4, 12, 13, 6, 7, 15, 16},
5507 {4, 5, 13, 14, 7, 8, 16, 17},
5508 {9, 10, 18, 19, 12, 13, 21, 22},
5509 {10, 11, 19, 20, 13, 14, 22, 23},
5510 {12, 13, 21, 22, 15, 16, 24, 25}};
5511
5512 std::vector<CellData<3>> cells(7, CellData<3>());
5513
5514 for (unsigned int i = 0; i < 7; ++i)
5515 {
5516 for (unsigned int j = 0; j < 8; ++j)
5517 cells[i].vertices[j] = cell_vertices[i][j];
5518 cells[i].material_id = 0;
5519 }
5520
5521 tria.create_triangulation(std::vector<Point<3>>(std::begin(vertices),
5522 std::end(vertices)),
5523 cells,
5524 SubCellData()); // no boundary information
5525
5526 if (colorize)
5527 {
5529 }
5530 }
5531
5532
5533
5534 void
5536 const unsigned int n_rotate_middle_square)
5537 {
5538 AssertThrow(n_rotate_middle_square < 4,
5539 ExcMessage("The number of rotation by pi/2 of the right square "
5540 "must be in the half-open range [0,4)."));
5541
5542 constexpr unsigned int dim = 2;
5543
5544 const unsigned int n_cells = 5;
5545 std::vector<CellData<dim>> cells(n_cells);
5546
5547 // Corner points of the cube [0,1]^2
5548 const std::vector<Point<dim>> vertices = {Point<dim>(0, 0), // 0
5549 Point<dim>(1, 0), // 1
5550 Point<dim>(0, 1), // 2
5551 Point<dim>(1, 1), // 3
5552 Point<dim>(2, 0), // 4
5553 Point<dim>(2, 1), // 5
5554 Point<dim>(3, 0), // 6
5555 Point<dim>(3, 1), // 7
5556 Point<dim>(1, -1), // 8
5557 Point<dim>(2, -1), // 9
5558 Point<dim>(1, 2), // 10
5559 Point<dim>(2, 2)}; // 11
5560
5561
5562 // consistent orientation
5563 unsigned int cell_vertices[n_cells][4] = {{0, 1, 2, 3},
5564 {1, 4, 3, 5}, // rotating cube
5565 {8, 9, 1, 4},
5566 {4, 6, 5, 7},
5567 {3, 5, 10, 11}};
5568
5569 switch (n_rotate_middle_square)
5570 {
5571 case /* rotate right square */ 1:
5572 {
5573 cell_vertices[1][0] = 4;
5574 cell_vertices[1][1] = 5;
5575 cell_vertices[1][2] = 1;
5576 cell_vertices[1][3] = 3;
5577 break;
5578 }
5579
5580 case /* rotate right square */ 2:
5581 {
5582 cell_vertices[1][0] = 5;
5583 cell_vertices[1][1] = 3;
5584 cell_vertices[1][2] = 4;
5585 cell_vertices[1][3] = 1;
5586 break;
5587 }
5588
5589 case /* rotate right square */ 3:
5590 {
5591 cell_vertices[1][0] = 3;
5592 cell_vertices[1][1] = 1;
5593 cell_vertices[1][2] = 5;
5594 cell_vertices[1][3] = 4;
5595 break;
5596 }
5597
5598 default /* 0 */:
5599 break;
5600 } // switch
5601
5602 cells.resize(n_cells, CellData<dim>());
5603
5604 for (unsigned int cell_index = 0; cell_index < n_cells; ++cell_index)
5605 {
5606 for (const unsigned int vertex_index :
5608 {
5609 cells[cell_index].vertices[vertex_index] =
5610 cell_vertices[cell_index][vertex_index];
5611 cells[cell_index].material_id = 0;
5612 }
5613 }
5614
5615 tria.create_triangulation(vertices, cells, SubCellData());
5616 }
5617
5618
5619 void
5621 const bool face_orientation,
5622 const bool face_flip,
5623 const bool face_rotation,
5624 const bool manipulate_left_cube)
5625 {
5626 constexpr unsigned int dim = 3;
5627
5628 const unsigned int n_cells = 2;
5629 std::vector<CellData<dim>> cells(n_cells);
5630
5631 // Corner points of the cube [0,1]^3
5632 const std::vector<Point<dim>> vertices = {Point<dim>(0, 0, 0), // 0
5633 Point<dim>(1, 0, 0), // 1
5634 Point<dim>(0, 1, 0), // 2
5635 Point<dim>(1, 1, 0), // 3
5636 Point<dim>(0, 0, 1), // 4
5637 Point<dim>(1, 0, 1), // 5
5638 Point<dim>(0, 1, 1), // 6
5639 Point<dim>(1, 1, 1), // 7
5640 Point<dim>(2, 0, 0), // 8
5641 Point<dim>(2, 1, 0), // 9
5642 Point<dim>(2, 0, 1), // 10
5643 Point<dim>(2, 1, 1)}; // 11
5644
5645 unsigned int cell_vertices[n_cells][8] = {
5646 {0, 1, 2, 3, 4, 5, 6, 7}, // unit cube
5647 {1, 8, 3, 9, 5, 10, 7, 11}}; // shifted cube
5648
5649 // binary to case number
5650 const unsigned int this_case = 4 * static_cast<int>(face_orientation) +
5651 2 * static_cast<int>(face_flip) +
5652 static_cast<int>(face_rotation);
5653
5654 if (manipulate_left_cube)
5655 {
5656 switch (this_case)
5657 {
5658 case 0:
5659 {
5660 cell_vertices[0][0] = 1;
5661 cell_vertices[0][1] = 0;
5662 cell_vertices[0][2] = 5;
5663 cell_vertices[0][3] = 4;
5664 cell_vertices[0][4] = 3;
5665 cell_vertices[0][5] = 2;
5666 cell_vertices[0][6] = 7;
5667 cell_vertices[0][7] = 6;
5668 break;
5669 }
5670
5671 case 1:
5672 {
5673 cell_vertices[0][0] = 5;
5674 cell_vertices[0][1] = 4;
5675 cell_vertices[0][2] = 7;
5676 cell_vertices[0][3] = 6;
5677 cell_vertices[0][4] = 1;
5678 cell_vertices[0][5] = 0;
5679 cell_vertices[0][6] = 3;
5680 cell_vertices[0][7] = 2;
5681 break;
5682 }
5683
5684 case 2:
5685 {
5686 cell_vertices[0][0] = 7;
5687 cell_vertices[0][1] = 6;
5688 cell_vertices[0][2] = 3;
5689 cell_vertices[0][3] = 2;
5690 cell_vertices[0][4] = 5;
5691 cell_vertices[0][5] = 4;
5692 cell_vertices[0][6] = 1;
5693 cell_vertices[0][7] = 0;
5694 break;
5695 }
5696 case 3:
5697 {
5698 cell_vertices[0][0] = 3;
5699 cell_vertices[0][1] = 2;
5700 cell_vertices[0][2] = 1;
5701 cell_vertices[0][3] = 0;
5702 cell_vertices[0][4] = 7;
5703 cell_vertices[0][5] = 6;
5704 cell_vertices[0][6] = 5;
5705 cell_vertices[0][7] = 4;
5706 break;
5707 }
5708
5709 case 4:
5710 {
5711 cell_vertices[0][0] = 0;
5712 cell_vertices[0][1] = 1;
5713 cell_vertices[0][2] = 2;
5714 cell_vertices[0][3] = 3;
5715 cell_vertices[0][4] = 4;
5716 cell_vertices[0][5] = 5;
5717 cell_vertices[0][6] = 6;
5718 cell_vertices[0][7] = 7;
5719 break;
5720 }
5721
5722 case 5:
5723 {
5724 cell_vertices[0][0] = 2;
5725 cell_vertices[0][1] = 3;
5726 cell_vertices[0][2] = 6;
5727 cell_vertices[0][3] = 7;
5728 cell_vertices[0][4] = 0;
5729 cell_vertices[0][5] = 1;
5730 cell_vertices[0][6] = 4;
5731 cell_vertices[0][7] = 5;
5732 break;
5733 }
5734
5735 case 6:
5736 {
5737 cell_vertices[0][0] = 6;
5738 cell_vertices[0][1] = 7;
5739 cell_vertices[0][2] = 4;
5740 cell_vertices[0][3] = 5;
5741 cell_vertices[0][4] = 2;
5742 cell_vertices[0][5] = 3;
5743 cell_vertices[0][6] = 0;
5744 cell_vertices[0][7] = 1;
5745 break;
5746 }
5747
5748 case 7:
5749 {
5750 cell_vertices[0][0] = 4;
5751 cell_vertices[0][1] = 5;
5752 cell_vertices[0][2] = 0;
5753 cell_vertices[0][3] = 1;
5754 cell_vertices[0][4] = 6;
5755 cell_vertices[0][5] = 7;
5756 cell_vertices[0][6] = 2;
5757 cell_vertices[0][7] = 3;
5758 break;
5759 }
5760 } // switch
5761 }
5762 else
5763 {
5764 switch (this_case)
5765 {
5766 case 0:
5767 {
5768 cell_vertices[1][0] = 8;
5769 cell_vertices[1][1] = 1;
5770 cell_vertices[1][2] = 10;
5771 cell_vertices[1][3] = 5;
5772 cell_vertices[1][4] = 9;
5773 cell_vertices[1][5] = 3;
5774 cell_vertices[1][6] = 11;
5775 cell_vertices[1][7] = 7;
5776 break;
5777 }
5778
5779 case 1:
5780 {
5781 cell_vertices[1][0] = 10;
5782 cell_vertices[1][1] = 5;
5783 cell_vertices[1][2] = 11;
5784 cell_vertices[1][3] = 7;
5785 cell_vertices[1][4] = 8;
5786 cell_vertices[1][5] = 1;
5787 cell_vertices[1][6] = 9;
5788 cell_vertices[1][7] = 3;
5789 break;
5790 }
5791
5792 case 2:
5793 {
5794 cell_vertices[1][0] = 11;
5795 cell_vertices[1][1] = 7;
5796 cell_vertices[1][2] = 9;
5797 cell_vertices[1][3] = 3;
5798 cell_vertices[1][4] = 10;
5799 cell_vertices[1][5] = 5;
5800 cell_vertices[1][6] = 8;
5801 cell_vertices[1][7] = 1;
5802 break;
5803 }
5804
5805 case 3:
5806 {
5807 cell_vertices[1][0] = 9;
5808 cell_vertices[1][1] = 3;
5809 cell_vertices[1][2] = 8;
5810 cell_vertices[1][3] = 1;
5811 cell_vertices[1][4] = 11;
5812 cell_vertices[1][5] = 7;
5813 cell_vertices[1][6] = 10;
5814 cell_vertices[1][7] = 5;
5815 break;
5816 }
5817
5818 case 4:
5819 {
5820 cell_vertices[1][0] = 1;
5821 cell_vertices[1][1] = 8;
5822 cell_vertices[1][2] = 3;
5823 cell_vertices[1][3] = 9;
5824 cell_vertices[1][4] = 5;
5825 cell_vertices[1][5] = 10;
5826 cell_vertices[1][6] = 7;
5827 cell_vertices[1][7] = 11;
5828 break;
5829 }
5830
5831 case 5:
5832 {
5833 cell_vertices[1][0] = 5;
5834 cell_vertices[1][1] = 10;
5835 cell_vertices[1][2] = 1;
5836 cell_vertices[1][3] = 8;
5837 cell_vertices[1][4] = 7;
5838 cell_vertices[1][5] = 11;
5839 cell_vertices[1][6] = 3;
5840 cell_vertices[1][7] = 9;
5841 break;
5842 }
5843
5844 case 6:
5845 {
5846 cell_vertices[1][0] = 7;
5847 cell_vertices[1][1] = 11;
5848 cell_vertices[1][2] = 5;
5849 cell_vertices[1][3] = 10;
5850 cell_vertices[1][4] = 3;
5851 cell_vertices[1][5] = 9;
5852 cell_vertices[1][6] = 1;
5853 cell_vertices[1][7] = 8;
5854 break;
5855 }
5856
5857 case 7:
5858 {
5859 cell_vertices[1][0] = 3;
5860 cell_vertices[1][1] = 9;
5861 cell_vertices[1][2] = 7;
5862 cell_vertices[1][3] = 11;
5863 cell_vertices[1][4] = 1;
5864 cell_vertices[1][5] = 8;
5865 cell_vertices[1][6] = 5;
5866 cell_vertices[1][7] = 10;
5867 break;
5868 }
5869 } // switch
5870 }
5871
5872 cells.resize(n_cells, CellData<dim>());
5873
5874 for (unsigned int cell_index = 0; cell_index < n_cells; ++cell_index)
5875 {
5876 for (const unsigned int vertex_index :
5878 {
5879 cells[cell_index].vertices[vertex_index] =
5880 cell_vertices[cell_index][vertex_index];
5881 cells[cell_index].material_id = 0;
5882 }
5883 }
5884
5885 tria.create_triangulation(vertices, cells, SubCellData());
5886 }
5887
5888
5889
5890 template <int spacedim>
5891 void
5893 const Point<spacedim> &p,
5894 const double radius)
5895 {
5896 Triangulation<spacedim> volume_mesh;
5897 GridGenerator::hyper_ball(volume_mesh, p, radius);
5898 const std::set<types::boundary_id> boundary_ids = {0};
5899 GridGenerator::extract_boundary_mesh(volume_mesh, tria, boundary_ids);
5900 tria.set_all_manifold_ids(0);
5902 }
5903
5904
5905
5906 // Implementation for 3d only
5907 template <>
5908 void
5910 const unsigned int x_subdivisions,
5911 const double radius,
5912 const double half_length)
5913 {
5914 // Copy the base from hyper_ball<3>
5915 // and transform it to yz
5916 const double d = radius / std::sqrt(2.0);
5917 const double a = d / (1 + std::sqrt(2.0));
5918
5919 std::vector<Point<3>> vertices;
5920 const double initial_height = -half_length;
5921 const double height_increment = 2. * half_length / x_subdivisions;
5922
5923 for (unsigned int rep = 0; rep < (x_subdivisions + 1); ++rep)
5924 {
5925 const double height = initial_height + height_increment * rep;
5926
5927 vertices.emplace_back(-d, height, -d);
5928 vertices.emplace_back(d, height, -d);
5929 vertices.emplace_back(-a, height, -a);
5930 vertices.emplace_back(a, height, -a);
5931 vertices.emplace_back(-a, height, a);
5932 vertices.emplace_back(a, height, a);
5933 vertices.emplace_back(-d, height, d);
5934 vertices.emplace_back(d, height, d);
5935 }
5936
5937 // Turn cylinder such that y->x
5938 for (auto &vertex : vertices)
5939 {
5940 const double h = vertex[1];
5941 vertex[1] = -vertex[0];
5942 vertex[0] = h;
5943 }
5944
5945 std::vector<std::vector<int>> cell_vertices;
5946 cell_vertices.push_back({0, 1, 8, 9, 2, 3, 10, 11});
5947 cell_vertices.push_back({0, 2, 8, 10, 6, 4, 14, 12});
5948 cell_vertices.push_back({2, 3, 10, 11, 4, 5, 12, 13});
5949 cell_vertices.push_back({1, 7, 9, 15, 3, 5, 11, 13});
5950 cell_vertices.push_back({6, 4, 14, 12, 7, 5, 15, 13});
5951
5952 for (unsigned int rep = 1; rep < x_subdivisions; ++rep)
5953 {
5954 for (unsigned int i = 0; i < 5; ++i)
5955 {
5956 std::vector<int> new_cell_vertices(8);
5957 for (unsigned int j = 0; j < 8; ++j)
5958 new_cell_vertices[j] = cell_vertices[i][j] + 8 * rep;
5959 cell_vertices.push_back(new_cell_vertices);
5960 }
5961 }
5962
5963 unsigned int n_cells = x_subdivisions * 5;
5964
5965 std::vector<CellData<3>> cells(n_cells, CellData<3>());
5966
5967 for (unsigned int i = 0; i < n_cells; ++i)
5968 {
5969 for (unsigned int j = 0; j < 8; ++j)
5970 cells[i].vertices[j] = cell_vertices[i][j];
5971 cells[i].material_id = 0;
5972 }
5973
5974 tria.create_triangulation(std::vector<Point<3>>(std::begin(vertices),
5975 std::end(vertices)),
5976 cells,
5977 SubCellData()); // no boundary information
5978
5979 // set boundary indicators for the
5980 // faces at the ends to 1 and 2,
5981 // respectively. note that we also
5982 // have to deal with those lines
5983 // that are purely in the interior
5984 // of the ends. we determine whether
5985 // an edge is purely in the
5986 // interior if one of its vertices
5987 // is at coordinates '+-a' as set
5988 // above
5990
5991 // Tolerance is calculated using the minimal length defining
5992 // the cylinder
5993 const double tolerance = 1e-5 * std::min(radius, half_length);
5994
5995 for (const auto &cell : tria.cell_iterators())
5996 for (const unsigned int i : GeometryInfo<3>::face_indices())
5997 if (cell->at_boundary(i))
5998 {
5999 if (cell->face(i)->center()[0] > half_length - tolerance)
6000 {
6001 cell->face(i)->set_boundary_id(2);
6002 cell->face(i)->set_manifold_id(numbers::flat_manifold_id);
6003
6004 for (unsigned int e = 0; e < GeometryInfo<3>::lines_per_face;
6005 ++e)
6006 if ((std::fabs(cell->face(i)->line(e)->vertex(0)[1]) == a) ||
6007 (std::fabs(cell->face(i)->line(e)->vertex(0)[2]) == a) ||
6008 (std::fabs(cell->face(i)->line(e)->vertex(1)[1]) == a) ||
6009 (std::fabs(cell->face(i)->line(e)->vertex(1)[2]) == a))
6010 {
6011 cell->face(i)->line(e)->set_boundary_id(2);
6012 cell->face(i)->line(e)->set_manifold_id(
6014 }
6015 }
6016 else if (cell->face(i)->center()[0] < -half_length + tolerance)
6017 {
6018 cell->face(i)->set_boundary_id(1);
6019 cell->face(i)->set_manifold_id(numbers::flat_manifold_id);
6020
6021 for (unsigned int e = 0; e < GeometryInfo<3>::lines_per_face;
6022 ++e)
6023 if ((std::fabs(cell->face(i)->line(e)->vertex(0)[1]) == a) ||
6024 (std::fabs(cell->face(i)->line(e)->vertex(0)[2]) == a) ||
6025 (std::fabs(cell->face(i)->line(e)->vertex(1)[1]) == a) ||
6026 (std::fabs(cell->face(i)->line(e)->vertex(1)[2]) == a))
6027 {
6028 cell->face(i)->line(e)->set_boundary_id(1);
6029 cell->face(i)->line(e)->set_manifold_id(
6031 }
6032 }
6033 }
6035 }
6036
6037 // Implementation for 3d only
6038 template <>
6039 void
6041 const double radius,
6042 const double half_length)
6043 {
6044 subdivided_cylinder(tria, 2, radius, half_length);
6045 }
6046
6047 template <>
6048 void
6050 const Point<3> &center,
6051 const double radius)
6052 {
6053 const unsigned int dim = 3;
6054
6055 // the parameters a (intersection on the octant lines from center), b
6056 // (intersection within the octant faces) and c (position inside the
6057 // octant) have been derived by equilibrating the minimal singular value
6058 // of the Jacobian of the four cells around the center point c and, as a
6059 // secondary measure, to minimize the aspect ratios defined as the maximal
6060 // divided by the minimal singular values throughout cells
6061 const double a = 0.528;
6062 const double b = 0.4533;
6063 const double c = 0.3752;
6064 const Point<dim> vertices[15] = {
6065 center + Point<dim>(0, 0, 0) * radius,
6066 center + Point<dim>(+1, 0, 0) * radius,
6067 center + Point<dim>(+1, 0, 0) * (radius * a),
6068 center + Point<dim>(0, +1, 0) * (radius * a),
6069 center + Point<dim>(+1, +1, 0) * (radius * b),
6070 center + Point<dim>(0, +1, 0) * radius,
6071 center + Point<dim>(+1, +1, 0) * radius / std::sqrt(2.0),
6072 center + Point<dim>(0, 0, 1) * radius * a,
6073 center + Point<dim>(+1, 0, 1) * radius / std::sqrt(2.0),
6074 center + Point<dim>(+1, 0, 1) * (radius * b),
6075 center + Point<dim>(0, +1, 1) * (radius * b),
6076 center + Point<dim>(+1, +1, 1) * (radius * c),
6077 center + Point<dim>(0, +1, 1) * radius / std::sqrt(2.0),
6078 center + Point<dim>(+1, +1, 1) * (radius / (std::sqrt(3.0))),
6079 center + Point<dim>(0, 0, 1) * radius};
6080 const int cell_vertices[4][8] = {{0, 2, 3, 4, 7, 9, 10, 11},
6081 {1, 6, 2, 4, 8, 13, 9, 11},
6082 {5, 3, 6, 4, 12, 10, 13, 11},
6083 {7, 9, 10, 11, 14, 8, 12, 13}};
6084
6085 std::vector<CellData<dim>> cells(4, CellData<dim>());
6086
6087 for (unsigned int i = 0; i < 4; ++i)
6088 {
6089 for (unsigned int j = 0; j < 8; ++j)
6090 cells[i].vertices[j] = cell_vertices[i][j];
6091 cells[i].material_id = 0;
6092 }
6093
6094 tria.create_triangulation(std::vector<Point<dim>>(std::begin(vertices),
6095 std::end(vertices)),
6096 cells,
6097 SubCellData()); // no boundary information
6098
6101
6103 while (cell != end)
6104 {
6105 for (const unsigned int i : GeometryInfo<dim>::face_indices())
6106 {
6107 if (cell->face(i)->boundary_id() ==
6109 continue;
6110
6111 // If x,y or z is zero, then this is part of the plane
6112 if (cell->face(i)->center()[0] < center[0] + 1.e-5 * radius ||
6113 cell->face(i)->center()[1] < center[1] + 1.e-5 * radius ||
6114 cell->face(i)->center()[2] < center[2] + 1.e-5 * radius)
6115 {
6116 cell->face(i)->set_boundary_id(1);
6117 cell->face(i)->set_manifold_id(numbers::flat_manifold_id);
6118 // also set the boundary indicators of the bounding lines,
6119 // unless both vertices are on the perimeter
6120 for (unsigned int j = 0; j < GeometryInfo<3>::lines_per_face;
6121 ++j)
6122 {
6123 const Point<3> line_vertices[2] = {
6124 cell->face(i)->line(j)->vertex(0),
6125 cell->face(i)->line(j)->vertex(1)};
6126 if ((std::fabs(line_vertices[0].distance(center) - radius) >
6127 1e-5 * radius) ||
6128 (std::fabs(line_vertices[1].distance(center) - radius) >
6129 1e-5 * radius))
6130 {
6131 cell->face(i)->line(j)->set_boundary_id(1);
6132 cell->face(i)->line(j)->set_manifold_id(
6134 }
6135 }
6136 }
6137 }
6138 ++cell;
6139 }
6140 tria.set_manifold(0, SphericalManifold<3>(center));
6141 }
6142
6143
6144
6145 // Implementation for 3d only
6146 template <>
6147 void
6149 const Point<3> &center,
6150 const double radius)
6151 {
6152 // These are for the two lower squares
6153 const double d = radius / std::sqrt(2.0);
6154 const double a = d / (1 + std::sqrt(2.0));
6155 // These are for the two upper square
6156 const double b = a / 2.0;
6157 const double c = d / 2.0;
6158 // And so are these
6159 const double hb = radius * std::sqrt(3.0) / 4.0;
6160 const double hc = radius * std::sqrt(3.0) / 2.0;
6161
6162 Point<3> vertices[16] = {
6163 center + Point<3>(0, d, -d),
6164 center + Point<3>(0, -d, -d),
6165 center + Point<3>(0, a, -a),
6166 center + Point<3>(0, -a, -a),
6167 center + Point<3>(0, a, a),
6168 center + Point<3>(0, -a, a),
6169 center + Point<3>(0, d, d),
6170 center + Point<3>(0, -d, d),
6171
6172 center + Point<3>(hc, c, -c),
6173 center + Point<3>(hc, -c, -c),
6174 center + Point<3>(hb, b, -b),
6175 center + Point<3>(hb, -b, -b),
6176 center + Point<3>(hb, b, b),
6177 center + Point<3>(hb, -b, b),
6178 center + Point<3>(hc, c, c),
6179 center + Point<3>(hc, -c, c),
6180 };
6181
6182 int cell_vertices[6][8] = {{0, 1, 8, 9, 2, 3, 10, 11},
6183 {0, 2, 8, 10, 6, 4, 14, 12},
6184 {2, 3, 10, 11, 4, 5, 12, 13},
6185 {1, 7, 9, 15, 3, 5, 11, 13},
6186 {6, 4, 14, 12, 7, 5, 15, 13},
6187 {8, 10, 9, 11, 14, 12, 15, 13}};
6188
6189 std::vector<CellData<3>> cells(6, CellData<3>());
6190
6191 for (unsigned int i = 0; i < 6; ++i)
6192 {
6193 for (unsigned int j = 0; j < 8; ++j)
6194 cells[i].vertices[j] = cell_vertices[i][j];
6195 cells[i].material_id = 0;
6196 }
6197
6198 tria.create_triangulation(std::vector<Point<3>>(std::begin(vertices),
6199 std::end(vertices)),
6200 cells,
6201 SubCellData()); // no boundary information
6202
6205
6207
6208 // go over all faces. for the ones on the flat face, set boundary
6209 // indicator for face and edges to one; the rest will remain at
6210 // zero but we have to pay attention to those edges that are
6211 // at the perimeter of the flat face since they should not be
6212 // set to one
6213 while (cell != end)
6214 {
6215 for (const unsigned int i : GeometryInfo<3>::face_indices())
6216 {
6217 if (!cell->at_boundary(i))
6218 continue;
6219
6220 // If the center is on the plane x=0, this is a planar element. set
6221 // its boundary indicator. also set the boundary indicators of the
6222 // bounding faces unless both vertices are on the perimeter
6223 if (cell->face(i)->center()[0] < center[0] + 1.e-5 * radius)
6224 {
6225 cell->face(i)->set_boundary_id(1);
6226 cell->face(i)->set_manifold_id(numbers::flat_manifold_id);
6227 for (unsigned int j = 0; j < GeometryInfo<3>::lines_per_face;
6228 ++j)
6229 {
6230 const Point<3> line_vertices[2] = {
6231 cell->face(i)->line(j)->vertex(0),
6232 cell->face(i)->line(j)->vertex(1)};
6233 if ((std::fabs(line_vertices[0].distance(center) - radius) >
6234 1e-5 * radius) ||
6235 (std::fabs(line_vertices[1].distance(center) - radius) >
6236 1e-5 * radius))
6237 {
6238 cell->face(i)->line(j)->set_boundary_id(1);
6239 cell->face(i)->line(j)->set_manifold_id(
6241 }
6242 }
6243 }
6244 }
6245 ++cell;
6246 }
6247 tria.set_manifold(0, SphericalManifold<3>(center));
6248 }
6249
6250
6251
6252 template <int dim>
6253 void
6255 const Point<dim> &p,
6256 const double radius)
6257 {
6258 // We create the ball by duplicating the information in each dimension at
6259 // a time by appropriate rotations, starting from the quarter ball. The
6260 // rotations make sure we do not generate inverted cells that would appear
6261 // if we tried the slightly simpler approach to simply mirror the cells.
6262 //
6263 // Make the rotations easy by centering at the origin now and shifting by p
6264 // later.
6265
6266 Triangulation<dim> tria_piece;
6268
6269 for (unsigned int round = 0; round < dim; ++round)
6270 {
6271 Triangulation<dim> tria_copy;
6272 tria_copy.copy_triangulation(tria_piece);
6273 tria_piece.clear();
6274 std::vector<Point<dim>> new_points(tria_copy.n_vertices());
6275 if (round == 0)
6276 for (unsigned int v = 0; v < tria_copy.n_vertices(); ++v)
6277 {
6278 // rotate by 90 degrees counterclockwise
6279 new_points[v][0] = -tria_copy.get_vertices()[v][1];
6280 new_points[v][1] = tria_copy.get_vertices()[v][0];
6281 if (dim == 3)
6282 new_points[v][2] = tria_copy.get_vertices()[v][2];
6283 }
6284 else if (round == 1)
6285 {
6286 for (unsigned int v = 0; v < tria_copy.n_vertices(); ++v)
6287 {
6288 // rotate by 180 degrees along the xy plane
6289 new_points[v][0] = -tria_copy.get_vertices()[v][0];
6290 new_points[v][1] = -tria_copy.get_vertices()[v][1];
6291 if (dim == 3)
6292 new_points[v][2] = tria_copy.get_vertices()[v][2];
6293 }
6294 }
6295 else if (round == 2)
6296 for (unsigned int v = 0; v < tria_copy.n_vertices(); ++v)
6297 {
6298 // rotate by 180 degrees along the xz plane
6299 Assert(dim == 3, ExcInternalError());
6300 new_points[v][0] = -tria_copy.get_vertices()[v][0];
6301 new_points[v][1] = tria_copy.get_vertices()[v][1];
6302 new_points[v][2] = -tria_copy.get_vertices()[v][2];
6303 }
6304 else
6306
6307
6308 // the cell data is exactly the same as before
6309 std::vector<CellData<dim>> cells;
6310 cells.reserve(tria_copy.n_cells());
6311 for (const auto &cell : tria_copy.cell_iterators())
6312 {
6313 CellData<dim> data;
6314 for (const unsigned int v : GeometryInfo<dim>::vertex_indices())
6315 data.vertices[v] = cell->vertex_index(v);
6316 data.material_id = cell->material_id();
6317 data.manifold_id = cell->manifold_id();
6318 cells.push_back(data);
6319 }
6320
6321 Triangulation<dim> rotated_tria;
6322 rotated_tria.create_triangulation(new_points, cells, SubCellData());
6323
6324 // merge the triangulations - this will make sure that the duplicate
6325 // vertices in the interior are absorbed
6326 if (round == dim - 1)
6327 merge_triangulations(tria_copy, rotated_tria, tria, 1e-12 * radius);
6328 else
6329 merge_triangulations(tria_copy,
6330 rotated_tria,
6331 tria_piece,
6332 1e-12 * radius);
6333 }
6334
6335 for (const auto &cell : tria.cell_iterators())
6336 if (cell->center().norm_square() > 0.4 * radius)
6337 cell->set_manifold_id(1);
6338 else
6340 GridTools::shift(p, tria);
6341
6343
6346 }
6347
6348 // To work around an internal clang-13 error we need to split up the
6349 // individual hyper shell functions. This has the added bonus of making the
6350 // control flow easier to follow - some hyper shell functions call others.
6351 namespace internal
6352 {
6353 namespace
6354 {
6355 void
6356 hyper_shell_6(Triangulation<3> &tria,
6357 const Point<3> &p,
6358 const double inner_radius,
6359 const double outer_radius)
6360 {
6361 std::vector<Point<3>> vertices;
6362 std::vector<CellData<3>> cells;
6363
6364 const double irad = inner_radius / std::sqrt(3.0);
6365 const double orad = outer_radius / std::sqrt(3.0);
6366
6367 // Corner points of the cube [-1,1]^3
6368 static const std::array<Point<3>, 8> hexahedron = {{{-1, -1, -1}, //
6369 {+1, -1, -1}, //
6370 {-1, +1, -1}, //
6371 {+1, +1, -1}, //
6372 {-1, -1, +1}, //
6373 {+1, -1, +1}, //
6374 {-1, +1, +1}, //
6375 {+1, +1, +1}}};
6376
6377 // Start with the shell bounded by two nested cubes
6378 vertices.reserve(8);
6379 for (unsigned int i = 0; i < 8; ++i)
6380 vertices.push_back(p + hexahedron[i] * irad);
6381 for (unsigned int i = 0; i < 8; ++i)
6382 vertices.push_back(p + hexahedron[i] * orad);
6383
6384 const unsigned int n_cells = 6;
6385 const int cell_vertices[n_cells][8] = {
6386 {8, 9, 10, 11, 0, 1, 2, 3}, // bottom
6387 {9, 11, 1, 3, 13, 15, 5, 7}, // right
6388 {12, 13, 4, 5, 14, 15, 6, 7}, // top
6389 {8, 0, 10, 2, 12, 4, 14, 6}, // left
6390 {8, 9, 0, 1, 12, 13, 4, 5}, // front
6391 {10, 2, 11, 3, 14, 6, 15, 7}}; // back
6392
6393 cells.resize(n_cells, CellData<3>());
6394
6395 for (unsigned int i = 0; i < n_cells; ++i)
6396 {
6397 for (const unsigned int j : GeometryInfo<3>::vertex_indices())
6398 cells[i].vertices[j] = cell_vertices[i][j];
6399 cells[i].material_id = 0;
6400 }
6401
6402 tria.create_triangulation(vertices, cells, SubCellData());
6403 tria.set_all_manifold_ids(0);
6405 }
6406
6407 void
6408 hyper_shell_12(Triangulation<3> &tria,
6409 const Point<3> &p,
6410 const double inner_radius,
6411 const double outer_radius)
6412 {
6413 std::vector<Point<3>> vertices;
6414 std::vector<CellData<3>> cells;
6415
6416 const double irad = inner_radius / std::sqrt(3.0);
6417 const double orad = outer_radius / std::sqrt(3.0);
6418
6419 // A more regular subdivision can be obtained by two nested rhombic
6420 // dodecahedra
6421 //
6422 // Octahedron inscribed in the cube [-1,1]^3
6423 static const std::array<Point<3>, 6> octahedron = {{{-1, 0, 0}, //
6424 {1, 0, 0}, //
6425 {0, -1, 0}, //
6426 {0, 1, 0}, //
6427 {0, 0, -1}, //
6428 {0, 0, 1}}};
6429
6430 // Corner points of the cube [-1,1]^3
6431 static const std::array<Point<3>, 8> hexahedron = {{{-1, -1, -1}, //
6432 {+1, -1, -1}, //
6433 {-1, +1, -1}, //
6434 {+1, +1, -1}, //
6435 {-1, -1, +1}, //
6436 {+1, -1, +1}, //
6437 {-1, +1, +1}, //
6438 {+1, +1, +1}}};
6439
6440 vertices.reserve(8);
6441 for (unsigned int i = 0; i < 8; ++i)
6442 vertices.push_back(p + hexahedron[i] * irad);
6443 for (unsigned int i = 0; i < 6; ++i)
6444 vertices.push_back(p + octahedron[i] * inner_radius);
6445 for (unsigned int i = 0; i < 8; ++i)
6446 vertices.push_back(p + hexahedron[i] * orad);
6447 for (unsigned int i = 0; i < 6; ++i)
6448 vertices.push_back(p + octahedron[i] * outer_radius);
6449
6450 const unsigned int n_cells = 12;
6451 const unsigned int rhombi[n_cells][4] = {{10, 4, 0, 8},
6452 {4, 13, 8, 6},
6453 {10, 5, 4, 13},
6454 {1, 9, 10, 5},
6455 {9, 7, 5, 13},
6456 {7, 11, 13, 6},
6457 {9, 3, 7, 11},
6458 {1, 12, 9, 3},
6459 {12, 2, 3, 11},
6460 {2, 8, 11, 6},
6461 {12, 0, 2, 8},
6462 {1, 10, 12, 0}};
6463
6464 cells.resize(n_cells, CellData<3>());
6465
6466 for (unsigned int i = 0; i < n_cells; ++i)
6467 {
6468 for (unsigned int j = 0; j < 4; ++j)
6469 {
6470 cells[i].vertices[j] = rhombi[i][j];
6471 cells[i].vertices[j + 4] = rhombi[i][j] + 14;
6472 }
6473 cells[i].material_id = 0;
6474 }
6475
6476 tria.create_triangulation(vertices, cells, SubCellData());
6477 tria.set_all_manifold_ids(0);
6479 }
6480
6481 void
6482 hyper_shell_24_48(Triangulation<3> &tria,
6483 const unsigned int n,
6484 const unsigned int n_refinement_steps,
6485 const Point<3> &p,
6486 const double inner_radius,
6487 const double outer_radius)
6488 {
6489 // These two meshes are created by first creating a mesh of the
6490 // 6-cell/12-cell version, refining globally, and removing the outer
6491 // half of the cells. For 192 and more cells, we do this iteratively
6492 // several times, always refining and removing the outer half. Thus, the
6493 // outer radius for the start is larger and set as 2^n_refinement_steps
6494 // such that it exactly gives the desired radius in the end. It would
6495 // have been slightly less code to treat refinement steps recursively
6496 // for 192 cells or beyond, but unfortunately we could end up with the
6497 // 96 cell case which is not what we want. Thus, we need to implement a
6498 // loop manually here.
6499 Triangulation<3> tmp;
6500 const unsigned int outer_radius_factor = 1 << n_refinement_steps;
6501 if (n == 24)
6502 hyper_shell_6(tmp,
6503 p,
6504 inner_radius,
6505 outer_radius_factor * outer_radius -
6506 (outer_radius_factor - 1) * inner_radius);
6507 else if (n == 48)
6508 hyper_shell_12(tmp,
6509 p,
6510 inner_radius,
6511 outer_radius_factor * outer_radius -
6512 (outer_radius_factor - 1) * inner_radius);
6513 else
6514 Assert(n == 24 || n == 48, ExcInternalError());
6515 for (unsigned int r = 0; r < n_refinement_steps; ++r)
6516 {
6517 tmp.refine_global(1);
6518 std::set<Triangulation<3>::active_cell_iterator> cells_to_remove;
6519
6520 // We remove all cells which do not have exactly four vertices
6521 // at the inner radius (plus some tolerance).
6522 for (const auto &cell : tmp.active_cell_iterators())
6523 {
6524 unsigned int n_vertices_inside = 0;
6525 for (const auto v : GeometryInfo<3>::vertex_indices())
6526 if ((cell->vertex(v) - p).norm_square() <
6527 inner_radius * inner_radius * (1 + 1e-12))
6528 ++n_vertices_inside;
6529 if (n_vertices_inside < 4)
6530 cells_to_remove.insert(cell);
6531 }
6532
6533 AssertDimension(cells_to_remove.size(), tmp.n_active_cells() / 2);
6534 if (r == n_refinement_steps - 1)
6536 cells_to_remove,
6537 tria);
6538 else
6539 {
6540 Triangulation<3> copy;
6542 cells_to_remove,
6543 copy);
6544 tmp = std::move(copy);
6545 tmp.set_all_manifold_ids(0);
6547 }
6548 }
6549 tria.set_all_manifold_ids(0);
6551 }
6552
6553 } // namespace
6554 } // namespace internal
6555
6556
6557
6558 template <>
6559 void
6561 const Point<3> &p,
6562 const double inner_radius,
6563 const double outer_radius,
6564 const unsigned int n_cells,
6565 const bool colorize)
6566 {
6567 Assert((inner_radius > 0) && (inner_radius < outer_radius),
6568 ExcInvalidRadii());
6569
6570 unsigned int n_refinement_steps = 0;
6571 unsigned int n_cells_coarsened = n_cells;
6572 if (n_cells != 96 && n_cells > 12)
6573 while (n_cells_coarsened > 12 && n_cells_coarsened % 4 == 0)
6574 {
6575 ++n_refinement_steps;
6576 n_cells_coarsened /= 4;
6577 }
6578 Assert(n_cells == 0 || n_cells == 6 || n_cells == 12 || n_cells == 96 ||
6579 (n_refinement_steps > 0 &&
6580 (n_cells_coarsened == 6 || n_cells_coarsened == 12)),
6581 ExcMessage("Invalid number of coarse mesh cells"));
6582
6583 const unsigned int n = n_refinement_steps > 0 ?
6584 4 * n_cells_coarsened :
6585 ((n_cells == 0) ? 6 : n_cells);
6586
6587 switch (n)
6588 {
6589 case 6:
6590 internal::hyper_shell_6(tria, p, inner_radius, outer_radius);
6591 break;
6592 case 12:
6593 internal::hyper_shell_12(tria, p, inner_radius, outer_radius);
6594 break;
6595 case 24:
6596 case 48:
6597 internal::hyper_shell_24_48(
6598 tria, n, n_refinement_steps, p, inner_radius, outer_radius);
6599 break;
6600 case 96:
6601 {
6602 // create a triangulation based on the 12-cell version. This
6603 // function was needed before SphericalManifold was written: it
6604 // manually adjusted the interior vertices to lie along concentric
6605 // spheres. Nowadays we can just refine globally:
6606 Triangulation<3> tmp;
6607 internal::hyper_shell_12(tmp, p, inner_radius, outer_radius);
6608 tmp.refine_global(1);
6609 flatten_triangulation(tmp, tria);
6610 tria.set_all_manifold_ids(0);
6612 break;
6613 }
6614 default:
6615 {
6616 Assert(false, ExcMessage("Invalid number of coarse mesh cells."));
6617 }
6618 }
6619
6620 if (n_cells > 0)
6621 AssertDimension(tria.n_global_active_cells(), n_cells);
6622
6623 if (colorize)
6624 colorize_hyper_shell(tria, p, inner_radius, outer_radius);
6625 }
6626
6627
6628
6629 // Implementation for 3d only
6630 template <>
6631 void
6633 const Point<3> &center,
6634 const double inner_radius,
6635 const double outer_radius,
6636 const unsigned int /*n_cells*/,
6637 const bool colorize)
6638 {
6639 Assert((inner_radius > 0) && (inner_radius < outer_radius),
6640 ExcInvalidRadii());
6641
6642 // These are for the two lower squares
6643 const double d = outer_radius / std::sqrt(2.0);
6644 const double a = inner_radius / std::sqrt(2.0);
6645 // These are for the two upper square
6646 const double b = a / 2.0;
6647 const double c = d / 2.0;
6648 // And so are these
6649 const double hb = inner_radius * std::sqrt(3.0) / 2.0;
6650 const double hc = outer_radius * std::sqrt(3.0) / 2.0;
6651
6652 Point<3> vertices[16] = {
6653 center + Point<3>(0, d, -d),
6654 center + Point<3>(0, -d, -d),
6655 center + Point<3>(0, a, -a),
6656 center + Point<3>(0, -a, -a),
6657 center + Point<3>(0, a, a),
6658 center + Point<3>(0, -a, a),
6659 center + Point<3>(0, d, d),
6660 center + Point<3>(0, -d, d),
6661
6662 center + Point<3>(hc, c, -c),
6663 center + Point<3>(hc, -c, -c),
6664 center + Point<3>(hb, b, -b),
6665 center + Point<3>(hb, -b, -b),
6666 center + Point<3>(hb, b, b),
6667 center + Point<3>(hb, -b, b),
6668 center + Point<3>(hc, c, c),
6669 center + Point<3>(hc, -c, c),
6670 };
6671
6672 int cell_vertices[5][8] = {{0, 1, 8, 9, 2, 3, 10, 11},
6673 {0, 2, 8, 10, 6, 4, 14, 12},
6674 {1, 7, 9, 15, 3, 5, 11, 13},
6675 {6, 4, 14, 12, 7, 5, 15, 13},
6676 {8, 10, 9, 11, 14, 12, 15, 13}};
6677
6678 std::vector<CellData<3>> cells(5, CellData<3>());
6679
6680 for (unsigned int i = 0; i < 5; ++i)
6681 {
6682 for (unsigned int j = 0; j < 8; ++j)
6683 cells[i].vertices[j] = cell_vertices[i][j];
6684 cells[i].material_id = 0;
6685 }
6686
6687 tria.create_triangulation(std::vector<Point<3>>(std::begin(vertices),
6688 std::end(vertices)),
6689 cells,
6690 SubCellData()); // no boundary information
6691
6692 tria.set_all_manifold_ids(0);
6693 tria.set_manifold(0, SphericalManifold<3>(center));
6694
6695 if (colorize)
6696 {
6697 // We want to use a flat boundary description where
6698 // the boundary is not curved. Hence set boundary id 2
6699 // to all faces in a first step.
6700 for (const auto &cell : tria.cell_iterators())
6701 for (const unsigned int f : cell->face_indices())
6702 if (cell->at_boundary(f))
6703 cell->face(f)->set_all_boundary_ids(2);
6704
6705 // Next look for the curved boundaries. If the x value of the
6706 // center of the face is not equal to center(0), we're on a curved
6707 // boundary. Then decide whether the center is nearer to the inner
6708 // or outer boundary to set the correct boundary id.
6709 for (const auto &cell : tria.cell_iterators())
6710 for (const unsigned int f : cell->face_indices())
6711 if (cell->at_boundary(f))
6712 {
6713 const Triangulation<3>::face_iterator face = cell->face(f);
6714 const Point<3> face_center(face->center(true));
6715
6716 if (std::abs(face_center[0] - center[0]) >
6717 1.e-6 * face_center.norm())
6718 {
6719 if (std::abs((face_center - center).norm() - inner_radius) <
6720 std::abs((face_center - center).norm() - outer_radius))
6721 face->set_all_boundary_ids(0);
6722 else
6723 face->set_all_boundary_ids(1);
6724 }
6725 }
6726 }
6727 }
6728
6729
6730 // Implementation for 3d only
6731 template <>
6732 void
6734 const Point<3> &center,
6735 const double inner_radius,
6736 const double outer_radius,
6737 const unsigned int n,
6738 const bool colorize)
6739 {
6740 Assert((inner_radius > 0) && (inner_radius < outer_radius),
6741 ExcInvalidRadii());
6742 if (n == 0 || n == 3)
6743 {
6744 const double a = inner_radius * std::sqrt(2.0) / 2e0;
6745 const double b = outer_radius * std::sqrt(2.0) / 2e0;
6746 const double c = a * std::sqrt(3.0) / 2e0;
6747 const double d = b * std::sqrt(3.0) / 2e0;
6748 const double e = outer_radius / 2e0;
6749 const double h = inner_radius / 2e0;
6750
6751 std::vector<Point<3>> vertices;
6752
6753 vertices.push_back(center + Point<3>(0, inner_radius, 0)); // 0
6754 vertices.push_back(center + Point<3>(a, a, 0)); // 1
6755 vertices.push_back(center + Point<3>(b, b, 0)); // 2
6756 vertices.push_back(center + Point<3>(0, outer_radius, 0)); // 3
6757 vertices.push_back(center + Point<3>(0, a, a)); // 4
6758 vertices.push_back(center + Point<3>(c, c, h)); // 5
6759 vertices.push_back(center + Point<3>(d, d, e)); // 6
6760 vertices.push_back(center + Point<3>(0, b, b)); // 7
6761 vertices.push_back(center + Point<3>(inner_radius, 0, 0)); // 8
6762 vertices.push_back(center + Point<3>(outer_radius, 0, 0)); // 9
6763 vertices.push_back(center + Point<3>(a, 0, a)); // 10
6764 vertices.push_back(center + Point<3>(b, 0, b)); // 11
6765 vertices.push_back(center + Point<3>(0, 0, inner_radius)); // 12
6766 vertices.push_back(center + Point<3>(0, 0, outer_radius)); // 13
6767
6768 const int cell_vertices[3][8] = {
6769 {0, 1, 3, 2, 4, 5, 7, 6},
6770 {1, 8, 2, 9, 5, 10, 6, 11},
6771 {4, 5, 7, 6, 12, 10, 13, 11},
6772 };
6773 std::vector<CellData<3>> cells(3);
6774
6775 for (unsigned int i = 0; i < 3; ++i)
6776 {
6777 for (unsigned int j = 0; j < 8; ++j)
6778 cells[i].vertices[j] = cell_vertices[i][j];
6779 cells[i].material_id = 0;
6780 }
6781
6782 tria.create_triangulation(vertices,
6783 cells,
6784 SubCellData()); // no boundary information
6785 }
6786 else
6787 {
6789 }
6790
6791 tria.set_all_manifold_ids(0);
6792 tria.set_manifold(0, SphericalManifold<3>(center));
6793
6794 if (colorize)
6795 colorize_quarter_hyper_shell(tria, center, inner_radius, outer_radius);
6796 }
6797
6798
6799 // Implementation for 3d only
6800 template <>
6801 void
6803 const double length,
6804 const double inner_radius,
6805 const double outer_radius,
6806 const unsigned int n_radial_cells,
6807 const unsigned int n_axial_cells,
6808 const bool colorize)
6809 {
6810 Assert((inner_radius > 0) && (inner_radius < outer_radius),
6811 ExcInvalidRadii());
6812
6813 const double pi = numbers::PI;
6814
6815 // determine the number of cells
6816 // for the grid. if not provided by
6817 // the user determine it such that
6818 // the length of each cell on the
6819 // median (in the middle between
6820 // the two circles) is equal to its
6821 // radial extent (which is the
6822 // difference between the two
6823 // radii)
6824 const unsigned int N_r =
6825 (n_radial_cells == 0 ? static_cast<unsigned int>(std::ceil(
6826 (2 * pi * (outer_radius + inner_radius) / 2) /
6827 (outer_radius - inner_radius))) :
6828 n_radial_cells);
6829 const unsigned int N_z =
6830 (n_axial_cells == 0 ?
6831 static_cast<unsigned int>(std::ceil(
6832 length / (2 * pi * (outer_radius + inner_radius) / 2 / N_r))) :
6833 n_axial_cells);
6834
6835 // set up N vertices on the
6836 // outer and N vertices on
6837 // the inner circle. the
6838 // first N ones are on the
6839 // outer one, and all are
6840 // numbered counter-clockwise
6841 std::vector<Point<2>> vertices_2d(2 * N_r);
6842 for (unsigned int i = 0; i < N_r; ++i)
6843 {
6844 vertices_2d[i] =
6845 Point<2>(std::cos(2 * pi * i / N_r), std::sin(2 * pi * i / N_r)) *
6847 vertices_2d[i + N_r] = vertices_2d[i] * (inner_radius / outer_radius);
6848 }
6849
6850 std::vector<Point<3>> vertices_3d;
6851 vertices_3d.reserve(2 * N_r * (N_z + 1));
6852 for (unsigned int j = 0; j <= N_z; ++j)
6853 for (unsigned int i = 0; i < 2 * N_r; ++i)
6854 {
6855 const Point<3> v(vertices_2d[i][0],
6856 vertices_2d[i][1],
6857 j * length / N_z);
6858 vertices_3d.push_back(v);
6859 }
6860
6861 std::vector<CellData<3>> cells(N_r * N_z, CellData<3>());
6862
6863 for (unsigned int j = 0; j < N_z; ++j)
6864 for (unsigned int i = 0; i < N_r; ++i)
6865 {
6866 cells[i + j * N_r].vertices[0] = i + (j + 1) * 2 * N_r;
6867 cells[i + j * N_r].vertices[1] = (i + 1) % N_r + (j + 1) * 2 * N_r;
6868 cells[i + j * N_r].vertices[2] = i + j * 2 * N_r;
6869 cells[i + j * N_r].vertices[3] = (i + 1) % N_r + j * 2 * N_r;
6870
6871 cells[i + j * N_r].vertices[4] = N_r + i + (j + 1) * 2 * N_r;
6872 cells[i + j * N_r].vertices[5] =
6873 N_r + ((i + 1) % N_r) + (j + 1) * 2 * N_r;
6874 cells[i + j * N_r].vertices[6] = N_r + i + j * 2 * N_r;
6875 cells[i + j * N_r].vertices[7] = N_r + ((i + 1) % N_r) + j * 2 * N_r;
6876
6877 cells[i + j * N_r].material_id = 0;
6878 }
6879
6880 tria.create_triangulation(vertices_3d, cells, SubCellData());
6881 tria.set_all_manifold_ids(0);
6883
6884 if (!colorize)
6885 return;
6886
6887 // If colorize, set boundary id on the triangualtion.
6888 // Inner cylinder has boundary id 0
6889 // Outer cylinder has boundary id 1
6890 // Bottom (Z-) part of the cylinder has boundary id 2
6891 // Top (Z+) part of the cylinder has boundary id 3
6892
6893 // Define tolerance to help detect boundary conditions
6894 // First we define the tolerance along the z axis to identify
6895 // bottom and top cells.
6896 double eps_z = 1e-6 * length;
6897
6898 // Gather the inner radius from the faces instead of the argument, this is
6899 // more robust for some aspect ratios. First initialize the outer to 0 and
6900 // the inner to a large value
6901 double face_inner_radius = std::numeric_limits<double>::max();
6902 double face_outer_radius = 0.;
6903
6904 // Loop over the cells once to acquire the min and max radius at the face
6905 // centers. Otherwise, for some cell ratio, the center of the faces can be
6906 // at a radius which is significantly different from the one prescribed.
6907 for (const auto &cell : tria.active_cell_iterators())
6908 for (const unsigned int f : GeometryInfo<3>::face_indices())
6909 {
6910 if (!cell->face(f)->at_boundary())
6911 continue;
6912
6913 const auto face_center = cell->face(f)->center();
6914 const double z = face_center[2];
6915
6916 if ((std::fabs(z) > eps_z) &&
6917 (std::fabs(z - length) > eps_z)) // Not a zmin or zmax boundary
6918 {
6919 const double radius = std::sqrt(face_center[0] * face_center[0] +
6920 face_center[1] * face_center[1]);
6921 face_inner_radius = std::min(face_inner_radius, radius);
6922 face_outer_radius = std::max(face_outer_radius, radius);
6923 }
6924 }
6925
6926 double mid_radial_distance = 0.5 * (face_outer_radius - face_inner_radius);
6927
6928 for (const auto &cell : tria.active_cell_iterators())
6929 for (const unsigned int f : GeometryInfo<3>::face_indices())
6930 {
6931 if (cell->face(f)->at_boundary())
6932 {
6933 const auto face_center = cell->face(f)->center();
6934
6935 const double radius = std::sqrt(face_center[0] * face_center[0] +
6936 face_center[1] * face_center[1]);
6937
6938 const double z = face_center[2];
6939
6940 if (std::fabs(z) < eps_z) // z = 0 set boundary 2
6941 {
6942 cell->face(f)->set_boundary_id(2);
6943 }
6944 else if (std::fabs(z - length) <
6945 eps_z) // z = length set boundary 3
6946 {
6947 cell->face(f)->set_boundary_id(3);
6948 }
6949 else if (std::fabs(radius - face_inner_radius) >
6950 mid_radial_distance) // r = outer_radius set boundary 1
6951 {
6952 cell->face(f)->set_boundary_id(1);
6953 }
6954 else if (std::fabs(radius - face_inner_radius) <
6955 mid_radial_distance) // r = inner_radius set boundary 0
6956 {
6957 cell->face(f)->set_boundary_id(0);
6958 }
6959 else
6961 }
6962 }
6963 }
6964
6965
6966 template <int dim, int spacedim>
6967 void
6969 const std::vector<const Triangulation<dim, spacedim> *> &triangulations,
6971 const double duplicated_vertex_tolerance,
6972 const bool copy_manifold_ids,
6973 const bool copy_boundary_ids)
6974 {
6975 std::vector<Point<spacedim>> vertices;
6976 std::vector<CellData<dim>> cells;
6977 SubCellData subcell_data;
6978
6979 unsigned int n_accumulated_vertices = 0;
6980 for (const auto triangulation : triangulations)
6981 {
6982 Assert(triangulation->n_levels() == 1,
6983 ExcMessage("The input triangulations must be non-empty "
6984 "and must not be refined."));
6985
6986 auto [tria_vertices, tria_cells, tria_subcell_data] =
6988 Assert(tria_vertices.size() == triangulation->n_vertices(),
6990 Assert(tria_cells.size() == triangulation->n_cells(),
6992
6993 // Copy the vertices of the current triangulation into the merged list,
6994 // and then let the vertex indices of the cells refer to those in
6995 // the merged list:
6996 vertices.insert(vertices.end(),
6997 tria_vertices.begin(),
6998 tria_vertices.end());
6999 for (CellData<dim> &cell_data : tria_cells)
7000 {
7001 for (unsigned int &vertex_n : cell_data.vertices)
7002 vertex_n += n_accumulated_vertices;
7003 cells.push_back(cell_data);
7004 }
7005
7006 // Skip copying lines with no manifold information.
7007 if (copy_manifold_ids)
7008 {
7009 for (CellData<1> &line_data : tria_subcell_data.boundary_lines)
7010 {
7011 if (line_data.manifold_id == numbers::flat_manifold_id)
7012 continue;
7013 for (unsigned int &vertex_n : line_data.vertices)
7014 vertex_n += n_accumulated_vertices;
7015 line_data.boundary_id =
7017 subcell_data.boundary_lines.push_back(line_data);
7018 }
7019
7020 for (CellData<2> &quad_data : tria_subcell_data.boundary_quads)
7021 {
7022 if (quad_data.manifold_id == numbers::flat_manifold_id)
7023 continue;
7024 for (unsigned int &vertex_n : quad_data.vertices)
7025 vertex_n += n_accumulated_vertices;
7026 quad_data.boundary_id =
7028 subcell_data.boundary_quads.push_back(quad_data);
7029 }
7030 }
7031
7032 n_accumulated_vertices += triangulation->n_vertices();
7033 }
7034
7035 // throw out duplicated vertices
7036 std::vector<unsigned int> considered_vertices;
7038 cells,
7039 subcell_data,
7040 considered_vertices,
7041 duplicated_vertex_tolerance);
7042
7043 // reorder the cells to ensure that they satisfy the convention for
7044 // edge and face directions
7045 if (std::all_of(cells.begin(), cells.end(), [](const auto &cell) {
7046 return cell.vertices.size() ==
7047 ReferenceCells::get_hypercube<dim>().n_vertices();
7048 }))
7050 result.clear();
7051 result.create_triangulation(vertices, cells, subcell_data);
7052
7053 if (!copy_manifold_ids)
7055
7056 if (copy_boundary_ids)
7057 {
7058 auto result_cell = result.begin();
7059 for (const auto &tria : triangulations)
7060 {
7061 for (const auto &cell : tria->cell_iterators())
7062 {
7063 for (const auto &f : cell->face_indices())
7064 if (result_cell->face(f)->at_boundary())
7065 result_cell->face(f)->set_boundary_id(
7066 cell->face(f)->boundary_id());
7067 ++result_cell;
7068 }
7069 }
7070 }
7071
7072 Assert(duplicated_vertex_tolerance > 0.0 ||
7073 n_accumulated_vertices == result.n_vertices(),
7075 }
7076
7077
7078
7079 template <int dim, int spacedim>
7080 void
7082 const Triangulation<dim, spacedim> &triangulation_2,
7084 const double duplicated_vertex_tolerance,
7085 const bool copy_manifold_ids,
7086 const bool copy_boundary_ids)
7087 {
7088 // if either Triangulation is empty then merging is just a copy.
7089 if (triangulation_1.n_cells() == 0)
7090 {
7091 if (&result != &triangulation_2)
7092 result.copy_triangulation(triangulation_2);
7093 }
7094 else if (triangulation_2.n_cells() == 0)
7095 {
7096 if (&result != &triangulation_1)
7097 result.copy_triangulation(triangulation_1);
7098 }
7099 else
7100 merge_triangulations({&triangulation_1, &triangulation_2},
7101 result,
7102 duplicated_vertex_tolerance,
7103 copy_manifold_ids,
7104 copy_boundary_ids);
7105 }
7106
7107
7108
7109 namespace
7110 {
7132 template <int structdim>
7133 void
7134 delete_duplicated_objects(std::vector<CellData<structdim>> &subcell_data)
7135 {
7136 static_assert(structdim == 1 || structdim == 2,
7137 "This function is only implemented for lines and "
7138 "quadrilaterals.");
7139 // start by making sure that all objects representing the same vertices
7140 // are numbered in the same way by canonicalizing the numberings. This
7141 // makes it possible to detect duplicates.
7142 for (CellData<structdim> &cell_data : subcell_data)
7143 {
7144 if (structdim == 1)
7145 std::sort(std::begin(cell_data.vertices),
7146 std::end(cell_data.vertices));
7147 else if (structdim == 2)
7148 {
7149 // rotate the vertex numbers so that the lowest one is first
7150 std::array<unsigned int, 4> renumbering{};
7151 std::copy(std::begin(cell_data.vertices),
7152 std::end(cell_data.vertices),
7153 renumbering.begin());
7154
7155 // convert to old style vertex numbering. This makes the
7156 // permutations easy since the valid configurations are
7157 //
7158 // 3 2 2 1 1 0 0 3
7159 // 0 1 3 0 2 3 1 2
7160 // (0123) (3012) (2310) (1230)
7161 //
7162 // rather than the lexical ordering which is harder to permute
7163 // by rotation.
7164 std::swap(renumbering[2], renumbering[3]);
7165 std::rotate(renumbering.begin(),
7166 std::min_element(renumbering.begin(),
7167 renumbering.end()),
7168 renumbering.end());
7169 // convert to new style
7170 std::swap(renumbering[2], renumbering[3]);
7171 // deal with cases where we might have
7172 //
7173 // 3 2 1 2
7174 // 0 1 0 3
7175 //
7176 // by forcing the second vertex (in lexical ordering) to be
7177 // smaller than the third
7178 if (renumbering[1] > renumbering[2])
7179 std::swap(renumbering[1], renumbering[2]);
7180 std::copy(renumbering.begin(),
7181 renumbering.end(),
7182 std::begin(cell_data.vertices));
7183 }
7184 }
7185
7186 // Now that all cell objects have been canonicalized they can be sorted:
7187 auto compare = [](const CellData<structdim> &a,
7188 const CellData<structdim> &b) {
7189 return std::lexicographical_compare(std::begin(a.vertices),
7190 std::end(a.vertices),
7191 std::begin(b.vertices),
7192 std::end(b.vertices));
7193 };
7194 std::sort(subcell_data.begin(), subcell_data.end(), compare);
7195
7196 // Finally, determine which objects are duplicates. Duplicates are
7197 // assumed to be interior objects, so delete all but one and change the
7198 // boundary id:
7199 auto left = subcell_data.begin();
7200 while (left != subcell_data.end())
7201 {
7202 const auto right =
7203 std::upper_bound(left, subcell_data.end(), *left, compare);
7204 // if the range has more than one item, then there are duplicates -
7205 // set all boundary ids in the range to the internal boundary id
7206 if (left + 1 != right)
7207 for (auto it = left; it != right; ++it)
7208 {
7209 it->boundary_id = numbers::internal_face_boundary_id;
7210 Assert(it->manifold_id == left->manifold_id,
7211 ExcMessage(
7212 "In the process of grid generation a single "
7213 "line or quadrilateral has been assigned two "
7214 "different manifold ids. This can happen when "
7215 "a Triangulation is copied, e.g., via "
7216 "GridGenerator::replicate_triangulation() and "
7217 "not all external boundary faces have the same "
7218 "manifold id. Double check that all faces "
7219 "which you expect to be merged together have "
7220 "the same manifold id."));
7221 }
7222 left = right;
7223 }
7224
7225 subcell_data.erase(std::unique(subcell_data.begin(), subcell_data.end()),
7226 subcell_data.end());
7227 }
7228 } // namespace
7229
7230
7231
7232 template <int dim, int spacedim>
7233 void
7235 const std::vector<unsigned int> &extents,
7237 {
7238 AssertDimension(dim, extents.size());
7239 if constexpr (running_in_debug_mode())
7240 {
7241 for (const auto &extent : extents)
7242 Assert(0 < extent,
7243 ExcMessage(
7244 "The Triangulation must be copied at least one time in "
7245 "each coordinate dimension."));
7246 }
7247 const BoundingBox<spacedim> bbox(input.get_vertices());
7248 const auto &min = bbox.get_boundary_points().first;
7249 const auto &max = bbox.get_boundary_points().second;
7250
7251 std::array<Tensor<1, spacedim>, dim> offsets;
7252 for (unsigned int d = 0; d < dim; ++d)
7253 offsets[d][d] = max[d] - min[d];
7254
7255 Triangulation<dim, spacedim> tria_to_replicate;
7256 tria_to_replicate.copy_triangulation(input);
7257 for (unsigned int d = 0; d < dim; ++d)
7258 {
7259 auto [input_vertices, input_cell_data, input_subcell_data] =
7260 GridTools::get_coarse_mesh_description(tria_to_replicate);
7261
7262 std::vector<Point<spacedim>> output_vertices = input_vertices;
7263 std::vector<CellData<dim>> output_cell_data = input_cell_data;
7264 SubCellData output_subcell_data = input_subcell_data;
7265
7266 for (unsigned int k = 1; k < extents[d]; ++k)
7267 {
7268 const std::size_t vertex_offset = k * input_vertices.size();
7269 // vertices
7270 for (const Point<spacedim> &point : input_vertices)
7271 output_vertices.push_back(point + double(k) * offsets[d]);
7272 // cell data
7273 for (const CellData<dim> &cell_data : input_cell_data)
7274 {
7275 output_cell_data.push_back(cell_data);
7276 for (unsigned int &vertex : output_cell_data.back().vertices)
7277 vertex += vertex_offset;
7278 }
7279 // subcell data
7280 for (const CellData<1> &boundary_line :
7281 input_subcell_data.boundary_lines)
7282 {
7283 output_subcell_data.boundary_lines.push_back(boundary_line);
7284 for (unsigned int &vertex :
7285 output_subcell_data.boundary_lines.back().vertices)
7286 vertex += vertex_offset;
7287 }
7288 for (const CellData<2> &boundary_quad :
7289 input_subcell_data.boundary_quads)
7290 {
7291 output_subcell_data.boundary_quads.push_back(boundary_quad);
7292 for (unsigned int &vertex :
7293 output_subcell_data.boundary_quads.back().vertices)
7294 vertex += vertex_offset;
7295 }
7296 }
7297 // check all vertices: since the grid is coarse, most will be on the
7298 // boundary anyway
7299 std::vector<unsigned int> boundary_vertices;
7301 output_vertices,
7302 output_cell_data,
7303 output_subcell_data,
7304 boundary_vertices,
7305 1e-6 * input.begin_active()->diameter());
7306 // delete_duplicated_vertices also deletes any unused vertices
7307 // deal with any reordering issues created by delete_duplicated_vertices
7308 GridTools::consistently_order_cells(output_cell_data);
7309 // clean up the boundary ids of the boundary objects: note that we
7310 // have to do this after delete_duplicated_vertices so that boundary
7311 // objects are actually duplicated at this point
7312 if (dim == 2)
7313 delete_duplicated_objects(output_subcell_data.boundary_lines);
7314 else if (dim == 3)
7315 {
7316 delete_duplicated_objects(output_subcell_data.boundary_quads);
7317 for (CellData<1> &boundary_line :
7318 output_subcell_data.boundary_lines)
7319 // set boundary lines to the default value - let
7320 // create_triangulation figure out the rest.
7322 }
7323
7324 tria_to_replicate.clear();
7325 tria_to_replicate.create_triangulation(output_vertices,
7326 output_cell_data,
7327 output_subcell_data);
7328 }
7329
7330 result.copy_triangulation(tria_to_replicate);
7331 }
7332
7333
7334
7335 template <int dim, int spacedim>
7336 void
7338 const Triangulation<dim, spacedim> &triangulation_1,
7339 const Triangulation<dim, spacedim> &triangulation_2,
7341 {
7342 Assert(GridTools::have_same_coarse_mesh(triangulation_1, triangulation_2),
7343 ExcMessage("The two input triangulations are not derived from "
7344 "the same coarse mesh as required."));
7345 Assert((dynamic_cast<
7347 &triangulation_1) == nullptr) &&
7348 (dynamic_cast<
7350 &triangulation_2) == nullptr),
7351 ExcMessage("The source triangulations for this function must both "
7352 "be available entirely locally, and not be distributed "
7353 "triangulations."));
7354
7355 // first copy triangulation_1, and
7356 // then do as many iterations as
7357 // there are levels in
7358 // triangulation_2 to refine
7359 // additional cells. since this is
7360 // the maximum number of
7361 // refinements to get from the
7362 // coarse grid to triangulation_2,
7363 // it is clear that this is also
7364 // the maximum number of
7365 // refinements to get from any cell
7366 // on triangulation_1 to
7367 // triangulation_2
7368 result.clear();
7369 result.copy_triangulation(triangulation_1);
7370 for (unsigned int iteration = 0; iteration < triangulation_2.n_levels();
7371 ++iteration)
7372 {
7374 intergrid_map.make_mapping(result, triangulation_2);
7375
7376 bool any_cell_flagged = false;
7377 for (const auto &result_cell : result.active_cell_iterators())
7378 if (intergrid_map[result_cell]->has_children())
7379 {
7380 any_cell_flagged = true;
7381 result_cell->set_refine_flag();
7382 }
7383
7384 if (any_cell_flagged == false)
7385 break;
7386 else
7388 }
7389 }
7390
7391
7392
7393 template <int dim, int spacedim>
7394 void
7396 const Triangulation<dim, spacedim> &input_triangulation,
7398 &cells_to_remove,
7400 {
7401 // simply copy the vertices; we will later strip those
7402 // that turn out to be unused
7403 std::vector<Point<spacedim>> vertices = input_triangulation.get_vertices();
7404
7405 // the loop through the cells and copy stuff, excluding
7406 // the ones we are to remove
7407 std::vector<CellData<dim>> cells;
7408 for (const auto &cell : input_triangulation.active_cell_iterators())
7409 if (cells_to_remove.find(cell) == cells_to_remove.end())
7410 {
7411 Assert(static_cast<unsigned int>(cell->level()) ==
7412 input_triangulation.n_levels() - 1,
7413 ExcMessage(
7414 "Your input triangulation appears to have "
7415 "adaptively refined cells. This is not allowed. You can "
7416 "only call this function on a triangulation in which "
7417 "all cells are on the same refinement level."));
7418
7419 CellData<dim> this_cell;
7420 for (const unsigned int v : GeometryInfo<dim>::vertex_indices())
7421 this_cell.vertices[v] = cell->vertex_index(v);
7422 this_cell.material_id = cell->material_id();
7423 cells.push_back(this_cell);
7424 }
7425
7426 // throw out duplicated vertices from the two meshes, reorder vertices as
7427 // necessary and create the triangulation
7428 SubCellData subcell_data;
7429 std::vector<unsigned int> considered_vertices;
7431 cells,
7432 subcell_data,
7433 considered_vertices);
7434
7435 // then clear the old triangulation and create the new one
7436 result.clear();
7437 result.create_triangulation(vertices, cells, subcell_data);
7438 }
7439
7440
7441
7442 void
7444 const Triangulation<2, 2> &input,
7445 const unsigned int n_slices,
7446 const double height,
7447 Triangulation<3, 3> &result,
7448 const bool copy_manifold_ids,
7449 const std::vector<types::manifold_id> &manifold_priorities)
7450 {
7451 Assert(input.n_levels() == 1,
7452 ExcMessage(
7453 "The input triangulation must be a coarse mesh, i.e., it must "
7454 "not have been refined."));
7455 Assert(result.n_cells() == 0,
7456 ExcMessage("The output triangulation object needs to be empty."));
7457 Assert(height > 0,
7458 ExcMessage("The given height for extrusion must be positive."));
7459 Assert(n_slices >= 2,
7460 ExcMessage(
7461 "The number of slices for extrusion must be at least 2."));
7462
7463 const double delta_h = height / (n_slices - 1);
7464 std::vector<double> slices_z_values;
7465 slices_z_values.reserve(n_slices);
7466 for (unsigned int i = 0; i < n_slices; ++i)
7467 slices_z_values.push_back(i * delta_h);
7469 input, slices_z_values, result, copy_manifold_ids, manifold_priorities);
7470 }
7471
7472
7473
7474 void
7476 const Triangulation<2, 2> &input,
7477 const unsigned int n_slices,
7478 const double height,
7479 Triangulation<2, 2> &result,
7480 const bool copy_manifold_ids,
7481 const std::vector<types::manifold_id> &manifold_priorities)
7482 {
7483 (void)input;
7484 (void)n_slices;
7485 (void)height;
7486 (void)result;
7487 (void)copy_manifold_ids;
7488 (void)manifold_priorities;
7489
7490 AssertThrow(false,
7491 ExcMessage(
7492 "GridTools::extrude_triangulation() is only available "
7493 "for Triangulation<3, 3> as output triangulation."));
7494 }
7495
7496
7497
7498 void
7500 const Triangulation<2, 2> &input,
7501 const std::vector<double> &slice_coordinates,
7502 Triangulation<3, 3> &result,
7503 const bool copy_manifold_ids,
7504 const std::vector<types::manifold_id> &manifold_priorities)
7505 {
7506 Assert(input.n_levels() == 1,
7507 ExcMessage(
7508 "The input triangulation must be a coarse mesh, i.e., it must "
7509 "not have been refined."));
7510 Assert(result.n_cells() == 0,
7511 ExcMessage("The output triangulation object needs to be empty."));
7512 Assert(slice_coordinates.size() >= 2,
7513 ExcMessage(
7514 "The number of slices for extrusion must be at least 2."));
7515 Assert(std::is_sorted(slice_coordinates.begin(), slice_coordinates.end()),
7516 ExcMessage("Slice z-coordinates should be in ascending order"));
7518 ExcMessage(
7519 "This function is only implemented for quadrilateral meshes."));
7520
7521 const auto priorities = [&]() -> std::vector<types::manifold_id> {
7522 // if a non-empty (i.e., not the default) vector is given for
7523 // manifold_priorities then use it (but check its validity in debug
7524 // mode)
7525 if (0 < manifold_priorities.size())
7526 {
7527 if constexpr (running_in_debug_mode())
7528 {
7529 // check that the provided manifold_priorities is valid
7530 std::vector<types::manifold_id> sorted_manifold_priorities =
7531 manifold_priorities;
7532 std::sort(sorted_manifold_priorities.begin(),
7533 sorted_manifold_priorities.end());
7534 Assert(std::unique(sorted_manifold_priorities.begin(),
7535 sorted_manifold_priorities.end()) ==
7536 sorted_manifold_priorities.end(),
7537 ExcMessage(
7538 "The given vector of manifold ids may not contain any "
7539 "duplicated entries."));
7540 std::vector<types::manifold_id> sorted_manifold_ids =
7541 input.get_manifold_ids();
7542 std::sort(sorted_manifold_ids.begin(), sorted_manifold_ids.end());
7543 if (sorted_manifold_priorities != sorted_manifold_ids)
7544 {
7545 std::ostringstream message;
7546 message << "The given triangulation has manifold ids {";
7547 for (const types::manifold_id manifold_id :
7548 sorted_manifold_ids)
7549 if (manifold_id != sorted_manifold_ids.back())
7550 message << manifold_id << ", ";
7551 message << sorted_manifold_ids.back() << "}, but \n"
7552 << " the given vector of manifold ids is {";
7553 for (const types::manifold_id manifold_id :
7554 manifold_priorities)
7555 if (manifold_id != manifold_priorities.back())
7556 message << manifold_id << ", ";
7557 message
7558 << manifold_priorities.back() << "}.\n"
7559 << " These vectors should contain the same elements.\n";
7560 const std::string m = message.str();
7561 Assert(false, ExcMessage(m));
7562 }
7563 }
7564 return manifold_priorities;
7565 }
7566 // otherwise use the default ranking: ascending order, but TFI manifolds
7567 // are at the end.
7568 std::vector<types::manifold_id> default_priorities =
7569 input.get_manifold_ids();
7570 const auto first_tfi_it = std::partition(
7571 default_priorities.begin(),
7572 default_priorities.end(),
7573 [&input](const types::manifold_id &id) {
7574 return dynamic_cast<const TransfiniteInterpolationManifold<2, 2> *>(
7575 &input.get_manifold(id)) == nullptr;
7576 });
7577 std::sort(default_priorities.begin(), first_tfi_it);
7578 std::sort(first_tfi_it, default_priorities.end());
7579
7580 return default_priorities;
7581 }();
7582
7583 const std::size_t n_slices = slice_coordinates.size();
7584 std::vector<Point<3>> points(n_slices * input.n_vertices());
7585 std::vector<CellData<3>> cells;
7586 cells.reserve((n_slices - 1) * input.n_active_cells());
7587
7588 // copy the array of points as many times as there will be slices,
7589 // one slice at a time. The z-axis value are defined in slices_coordinates
7590 for (std::size_t slice_n = 0; slice_n < n_slices; ++slice_n)
7591 {
7592 for (std::size_t vertex_n = 0; vertex_n < input.n_vertices();
7593 ++vertex_n)
7594 {
7595 const Point<2> vertex = input.get_vertices()[vertex_n];
7596 points[slice_n * input.n_vertices() + vertex_n] =
7597 Point<3>(vertex[0], vertex[1], slice_coordinates[slice_n]);
7598 }
7599 }
7600
7601 // then create the cells of each of the slices, one stack at a
7602 // time
7603 for (const auto &cell : input.active_cell_iterators())
7604 {
7605 for (std::size_t slice_n = 0; slice_n < n_slices - 1; ++slice_n)
7606 {
7607 CellData<3> this_cell;
7608 for (const unsigned int vertex_n :
7610 {
7611 this_cell.vertices[vertex_n] =
7612 cell->vertex_index(vertex_n) + slice_n * input.n_vertices();
7613 this_cell
7615 cell->vertex_index(vertex_n) +
7616 (slice_n + 1) * input.n_vertices();
7617 }
7618
7619 this_cell.material_id = cell->material_id();
7620 if (copy_manifold_ids)
7621 this_cell.manifold_id = cell->manifold_id();
7622 cells.push_back(this_cell);
7623 }
7624 }
7625
7626 // Next, create face data for all faces that are orthogonal to the x-y
7627 // plane
7628 SubCellData subcell_data;
7629 std::vector<CellData<2>> &quads = subcell_data.boundary_quads;
7630 types::boundary_id max_boundary_id = 0;
7631 quads.reserve(input.n_active_lines() * (n_slices - 1) +
7632 input.n_active_cells() * 2);
7633 for (const auto &face : input.active_face_iterators())
7634 {
7635 CellData<2> quad;
7636 quad.boundary_id = face->boundary_id();
7637 if (face->at_boundary())
7638 max_boundary_id = std::max(max_boundary_id, quad.boundary_id);
7639 if (copy_manifold_ids)
7640 quad.manifold_id = face->manifold_id();
7641 for (std::size_t slice_n = 0; slice_n < n_slices - 1; ++slice_n)
7642 {
7643 quad.vertices[0] =
7644 face->vertex_index(0) + slice_n * input.n_vertices();
7645 quad.vertices[1] =
7646 face->vertex_index(1) + slice_n * input.n_vertices();
7647 quad.vertices[2] =
7648 face->vertex_index(0) + (slice_n + 1) * input.n_vertices();
7649 quad.vertices[3] =
7650 face->vertex_index(1) + (slice_n + 1) * input.n_vertices();
7651 quads.push_back(quad);
7652 }
7653 }
7654
7655 // if necessary, create face data for faces parallel to the x-y
7656 // plane. This is only necessary if we need to set manifolds.
7657 if (copy_manifold_ids)
7658 for (const auto &cell : input.active_cell_iterators())
7659 {
7660 CellData<2> quad;
7662 quad.manifold_id = cell->manifold_id(); // check is outside loop
7663 for (std::size_t slice_n = 1; slice_n < n_slices - 1; ++slice_n)
7664 {
7665 quad.vertices[0] =
7666 cell->vertex_index(0) + slice_n * input.n_vertices();
7667 quad.vertices[1] =
7668 cell->vertex_index(1) + slice_n * input.n_vertices();
7669 quad.vertices[2] =
7670 cell->vertex_index(2) + slice_n * input.n_vertices();
7671 quad.vertices[3] =
7672 cell->vertex_index(3) + slice_n * input.n_vertices();
7673 quads.push_back(quad);
7674 }
7675 }
7676
7677 // then mark the bottom and top boundaries of the extruded mesh
7678 // with max_boundary_id+1 and max_boundary_id+2. check that this
7679 // remains valid
7680 Assert((max_boundary_id != numbers::invalid_boundary_id) &&
7681 (max_boundary_id + 1 != numbers::invalid_boundary_id) &&
7682 (max_boundary_id + 2 != numbers::invalid_boundary_id),
7683 ExcMessage(
7684 "The input triangulation to this function is using boundary "
7685 "indicators in a range that do not allow using "
7686 "max_boundary_id+1 and max_boundary_id+2 as boundary "
7687 "indicators for the bottom and top faces of the "
7688 "extruded triangulation."));
7689 const types::boundary_id bottom_boundary_id = max_boundary_id + 1;
7690 const types::boundary_id top_boundary_id = max_boundary_id + 2;
7691 for (const auto &cell : input.active_cell_iterators())
7692 {
7693 CellData<2> quad;
7694 quad.boundary_id = bottom_boundary_id;
7695 quad.vertices[0] = cell->vertex_index(0);
7696 quad.vertices[1] = cell->vertex_index(1);
7697 quad.vertices[2] = cell->vertex_index(2);
7698 quad.vertices[3] = cell->vertex_index(3);
7699 if (copy_manifold_ids)
7700 quad.manifold_id = cell->manifold_id();
7701 quads.push_back(quad);
7702
7703 quad.boundary_id = top_boundary_id;
7704 for (unsigned int &vertex : quad.vertices)
7705 vertex += (n_slices - 1) * input.n_vertices();
7706 if (copy_manifold_ids)
7707 quad.manifold_id = cell->manifold_id();
7708 quads.push_back(quad);
7709 }
7710
7711 // use all of this to finally create the extruded 3d
7712 // triangulation. it is not necessary to call
7713 // GridTools::consistently_order_cells() because the cells we have
7714 // constructed above are automatically correctly oriented. this is
7715 // because the 2d base mesh is always correctly oriented, and
7716 // extruding it automatically yields a correctly oriented 3d mesh,
7717 // as discussed in the edge orientation paper mentioned in the
7718 // introduction to the @ref reordering "reordering module".
7719 result.create_triangulation(points, cells, subcell_data);
7720
7721 for (auto manifold_id_it = priorities.rbegin();
7722 manifold_id_it != priorities.rend();
7723 ++manifold_id_it)
7724 for (const auto &face : result.active_face_iterators())
7725 if (face->manifold_id() == *manifold_id_it)
7726 for (unsigned int line_n = 0;
7727 line_n < GeometryInfo<3>::lines_per_face;
7728 ++line_n)
7729 face->line(line_n)->set_manifold_id(*manifold_id_it);
7730 }
7731
7732
7733
7734 void
7736 const Triangulation<2, 2> &input,
7737 const std::vector<double> &slice_coordinates,
7738 Triangulation<2, 2> &result,
7739 const bool copy_manifold_ids,
7740 const std::vector<types::manifold_id> &manifold_priorities)
7741 {
7742 (void)input;
7743 (void)slice_coordinates;
7744 (void)result;
7745 (void)copy_manifold_ids;
7746 (void)manifold_priorities;
7747
7748 AssertThrow(false,
7749 ExcMessage(
7750 "GridTools::extrude_triangulation() is only available "
7751 "for Triangulation<3, 3> as output triangulation."));
7752 }
7753
7754
7755
7756 template <>
7757 void
7759 const double,
7760 const double,
7761 const double,
7762 const unsigned int,
7763 const bool)
7764 {
7766 }
7767
7768
7769
7770 template <int spacedim>
7771 void
7772 hyper_cube_with_cylindrical_hole_2D(Triangulation<2, spacedim> &triangulation,
7773 const double inner_radius,
7774 const double outer_radius,
7775 const double, // width,
7776 const unsigned int, // width_repetition,
7777 const bool colorize)
7778 {
7779 const int dim = 2;
7780
7781 Assert(inner_radius < outer_radius,
7782 ExcMessage("outer_radius has to be bigger than inner_radius."));
7783
7784 const Point<spacedim> center;
7785
7786 // We create a hyper_shell (i.e., an annulus) in two dimensions, and then we
7787 // modify it by pulling the vertices on the diagonals out to where the
7788 // corners of a square would be:
7789 hyper_shell(triangulation, center, inner_radius, outer_radius, 8);
7791 std::vector<bool> treated_vertices(triangulation.n_vertices(), false);
7792 for (const auto &cell : triangulation.active_cell_iterators())
7793 {
7794 for (auto f : GeometryInfo<dim>::face_indices())
7795 if (cell->face(f)->at_boundary())
7796 for (const unsigned int v : cell->face(f)->vertex_indices())
7797 if (/* is the vertex on the outer ring? */
7798 (std::fabs(cell->face(f)->vertex(v).norm() - outer_radius) <
7799 1e-12 * outer_radius)
7800 /* and */
7801 &&
7802 /* is the vertex on one of the two diagonals? */
7803 (std::fabs(std::fabs(cell->face(f)->vertex(v)[0]) -
7804 std::fabs(cell->face(f)->vertex(v)[1])) <
7805 1e-12 * outer_radius))
7806 cell->face(f)->vertex(v) *= std::sqrt(2.);
7807 }
7808 const double eps = 1e-3 * outer_radius;
7809 for (const auto &cell : triangulation.active_cell_iterators())
7810 {
7811 for (const unsigned int f : cell->face_indices())
7812 if (cell->face(f)->at_boundary())
7813 {
7814 const double dx = cell->face(f)->center()[0] - center[0];
7815 const double dy = cell->face(f)->center()[1] - center[1];
7816 if (colorize)
7817 {
7818 if (std::abs(dx + outer_radius) < eps)
7819 cell->face(f)->set_boundary_id(0);
7820 else if (std::abs(dx - outer_radius) < eps)
7821 cell->face(f)->set_boundary_id(1);
7822 else if (std::abs(dy + outer_radius) < eps)
7823 cell->face(f)->set_boundary_id(2);
7824 else if (std::abs(dy - outer_radius) < eps)
7825 cell->face(f)->set_boundary_id(3);
7826 else
7827 {
7828 cell->face(f)->set_boundary_id(4);
7829 cell->face(f)->set_manifold_id(0);
7830 }
7831 }
7832 else
7833 {
7834 const double d = (cell->face(f)->center() - center).norm();
7835 if (d - inner_radius < 0)
7836 {
7837 cell->face(f)->set_boundary_id(1);
7838 cell->face(f)->set_manifold_id(0);
7839 }
7840 else
7841 cell->face(f)->set_boundary_id(0);
7842 }
7843 }
7844 }
7845 triangulation.set_manifold(0, PolarManifold<2, spacedim>(center));
7846 }
7847
7848
7849
7850 template <>
7851 void
7853 const double inner_radius,
7854 const double outer_radius,
7855 const double width,
7856 const unsigned int width_repetition,
7857 const bool colorize)
7858 {
7859 hyper_cube_with_cylindrical_hole_2D(triangulation,
7860 inner_radius,
7862 width,
7863 width_repetition,
7864 colorize);
7865 }
7866
7867
7868
7869 template <>
7870 void
7872 const double inner_radius,
7873 const double outer_radius,
7874 const double width,
7875 const unsigned int width_repetition,
7876 const bool colorize)
7877 {
7878 hyper_cube_with_cylindrical_hole_2D(triangulation,
7879 inner_radius,
7881 width,
7882 width_repetition,
7883 colorize);
7884 }
7885
7886
7887
7888 template <int dim>
7889 void
7891 const Point<dim> &center,
7892 const double inner_radius,
7893 const double outer_radius,
7894 const unsigned int n_shells,
7895 const double skewness,
7896 const unsigned int n_cells,
7897 const bool colorize)
7898 {
7899 Assert(dim == 2 || dim == 3, ExcNotImplemented());
7900 Assert(inner_radius < outer_radius,
7901 ExcMessage("outer_radius has to be bigger than inner_radius."));
7902 if (n_shells == 0)
7903 return; // empty Triangulation
7904
7905 std::vector<double> radii;
7906 radii.push_back(inner_radius);
7907 for (unsigned int shell_n = 1; shell_n < n_shells; ++shell_n)
7908 if (skewness == 0.0)
7909 // same as below, but works in the limiting case of zero skewness
7910 radii.push_back(inner_radius +
7911 (outer_radius - inner_radius) *
7912 (1.0 - (1.0 - double(shell_n) / n_shells)));
7913 else
7914 radii.push_back(
7915 inner_radius +
7916 (outer_radius - inner_radius) *
7917 (1.0 - std::tanh(skewness * (1.0 - double(shell_n) / n_shells)) /
7918 std::tanh(skewness)));
7919 radii.push_back(outer_radius);
7920
7921 double grid_vertex_tolerance = 0.0;
7922 for (unsigned int shell_n = 0; shell_n < radii.size() - 1; ++shell_n)
7923 {
7924 Triangulation<dim> current_shell;
7925 GridGenerator::hyper_shell(current_shell,
7926 center,
7927 radii[shell_n],
7928 radii[shell_n + 1],
7929 n_cells == 0 ? (dim == 2 ? 8 : 12) :
7930 n_cells);
7931
7932 // The innermost shell has the smallest cells: use that to set the
7933 // vertex merging tolerance
7934 if (grid_vertex_tolerance == 0.0)
7935 grid_vertex_tolerance =
7936 0.5 * internal::minimal_vertex_distance(current_shell);
7937
7938 Triangulation<dim> temp(std::move(triangulation));
7939 triangulation.clear();
7941 temp,
7942 triangulation,
7943 grid_vertex_tolerance);
7944 }
7945
7947 triangulation.set_all_manifold_ids(manifold_id);
7948 if (dim == 2)
7949 triangulation.set_manifold(manifold_id, PolarManifold<dim>(center));
7950 else if (dim == 3)
7951 triangulation.set_manifold(manifold_id, SphericalManifold<dim>(center));
7952
7953 // We use boundary vertex positions to see if things are on the inner or
7954 // outer boundary.
7955 constexpr double radial_vertex_tolerance =
7956 100.0 * std::numeric_limits<double>::epsilon();
7957 auto assert_vertex_distance_within_tolerance =
7958 [center, radial_vertex_tolerance](
7959 const TriaIterator<TriaAccessor<dim - 1, dim, dim>> &face,
7960 const double radius) {
7961 (void)center;
7962 (void)radial_vertex_tolerance;
7963 for (unsigned int vertex_n = 0;
7964 vertex_n < GeometryInfo<dim>::vertices_per_face;
7965 ++vertex_n)
7966 {
7967 Assert(std::abs((face->vertex(vertex_n) - center).norm() - radius) <
7968 (center.norm() + radius) * radial_vertex_tolerance,
7970 }
7971 };
7972 if (colorize)
7973 for (const auto &cell : triangulation.active_cell_iterators())
7974 for (const unsigned int face_n : GeometryInfo<dim>::face_indices())
7975 {
7976 auto face = cell->face(face_n);
7977 if (face->at_boundary())
7978 {
7979 if (((face->vertex(0) - center).norm() - inner_radius) <
7980 (center.norm() + inner_radius) * radial_vertex_tolerance)
7981 {
7982 // we must be at an inner face, but check
7983 assert_vertex_distance_within_tolerance(face, inner_radius);
7984 face->set_all_boundary_ids(0);
7985 }
7986 else
7987 {
7988 // we must be at an outer face, but check
7989 assert_vertex_distance_within_tolerance(face, outer_radius);
7990 face->set_all_boundary_ids(1);
7991 }
7992 }
7993 }
7994 }
7995
7996
7997
7998 template <>
7999 void
8001 const double inner_radius,
8002 const double outer_radius,
8003 const double L,
8004 const unsigned int Nz,
8005 const bool colorize)
8006 {
8007 const int dim = 3;
8008
8009 Assert(inner_radius < outer_radius,
8010 ExcMessage("outer_radius has to be bigger than inner_radius."));
8011 Assert(L > 0, ExcMessage("Must give positive extension L"));
8012 Assert(Nz >= 1, ExcLowerRange(1, Nz));
8013
8014 // Start with a cylinder shell with the correct inner and outer radius
8015 // and as many layers as requested
8016 cylinder_shell(triangulation, L, inner_radius, outer_radius, 8, Nz, false);
8018
8019 // Then loop over all vertices that are at the boundary (by looping
8020 // over all cells, their faces, and if the face is at the boundary,
8021 // their vertices. If we haven't touched that vertex yet, see if
8022 // we need to move it from its cylinder mantle position to the
8023 // outer boundary of the box.
8024 std::vector<bool> treated_vertices(triangulation.n_vertices(), false);
8025 for (const auto &cell : triangulation.active_cell_iterators())
8026 {
8027 for (const auto f : cell->face_indices())
8028 if (cell->face(f)->at_boundary())
8029 {
8030 for (const unsigned int v : cell->face(f)->vertex_indices())
8031 {
8032 const unsigned int vv = cell->face(f)->vertex_index(v);
8033 if (treated_vertices[vv] == false)
8034 {
8035 treated_vertices[vv] = true;
8036
8037 // The vertices we have to treat are the ones that
8038 // have x=y or x=-y and are at the outer ring -- that is,
8039 // they are on the diagonal in the x-y plane and radius
8040 // equal to outer_radius. These need to be pulled out to
8041 // the corner point of the square, i.e., their x and y
8042 // coordinates need to be multiplied by sqrt(2),
8043 // whereas the z coordinate remains unchanged:
8044 const Point<dim> vertex_location =
8045 cell->face(f)->vertex(v);
8046 if ((std::fabs(std::fabs(vertex_location[0]) -
8047 std::fabs(vertex_location[1])) <
8048 1e-12 * outer_radius) &&
8049 (std::fabs(vertex_location[0] * vertex_location[0] +
8050 vertex_location[1] * vertex_location[1] -
8052 1e-12 * outer_radius))
8053 cell->face(f)->vertex(v) =
8054 Point<3>(vertex_location[0] * std::sqrt(2.0),
8055 vertex_location[1] * std::sqrt(2.0),
8056 vertex_location[2]);
8057 }
8058 }
8059 }
8060 }
8061 double eps = 1e-3 * outer_radius;
8062 for (const auto &cell : triangulation.active_cell_iterators())
8063 {
8064 for (const unsigned int f : cell->face_indices())
8065 if (cell->face(f)->at_boundary())
8066 {
8067 const double dx = cell->face(f)->center()[0];
8068 const double dy = cell->face(f)->center()[1];
8069 const double dz = cell->face(f)->center()[2];
8070
8071 if (colorize)
8072 {
8073 if (std::abs(dx + outer_radius) < eps)
8074 cell->face(f)->set_boundary_id(0);
8075
8076 else if (std::abs(dx - outer_radius) < eps)
8077 cell->face(f)->set_boundary_id(1);
8078
8079 else if (std::abs(dy + outer_radius) < eps)
8080 cell->face(f)->set_boundary_id(2);
8081
8082 else if (std::abs(dy - outer_radius) < eps)
8083 cell->face(f)->set_boundary_id(3);
8084
8085 else if (std::abs(dz) < eps)
8086 cell->face(f)->set_boundary_id(4);
8087
8088 else if (std::abs(dz - L) < eps)
8089 cell->face(f)->set_boundary_id(5);
8090
8091 else
8092 {
8093 cell->face(f)->set_all_boundary_ids(6);
8094 cell->face(f)->set_all_manifold_ids(0);
8095 }
8096 }
8097 else
8098 {
8099 Point<dim> c = cell->face(f)->center();
8100 c[2] = 0;
8101 const double d = c.norm();
8102 if (d - inner_radius < 0)
8103 {
8104 cell->face(f)->set_all_boundary_ids(1);
8105 cell->face(f)->set_all_manifold_ids(0);
8106 }
8107 else
8108 cell->face(f)->set_boundary_id(0);
8109 }
8110 }
8111 }
8112 triangulation.set_manifold(0, CylindricalManifold<3>(2));
8113 }
8114
8115
8116
8117 template <int dim, int spacedim1, int spacedim2>
8118 void
8121 {
8122 Assert((dynamic_cast<
8124 &in_tria) == nullptr),
8125 ExcMessage(
8126 "This function cannot be used on "
8127 "parallel::distributed::Triangulation objects as inputs."));
8128 Assert(in_tria.has_hanging_nodes() == false,
8129 ExcMessage("This function does not work for meshes that have "
8130 "hanging nodes."));
8131
8132
8133 const unsigned int spacedim = std::min(spacedim1, spacedim2);
8134 const std::vector<Point<spacedim1>> &in_vertices = in_tria.get_vertices();
8135
8136 // Create an array of vertices, with components either truncated
8137 // or extended by zeroes.
8138 std::vector<Point<spacedim2>> v(in_vertices.size());
8139 for (unsigned int i = 0; i < in_vertices.size(); ++i)
8140 for (unsigned int d = 0; d < spacedim; ++d)
8141 v[i][d] = in_vertices[i][d];
8142
8143 std::vector<CellData<dim>> cells(in_tria.n_active_cells());
8144 for (const auto &cell : in_tria.active_cell_iterators())
8145 {
8146 const unsigned int id = cell->active_cell_index();
8147
8148 cells[id].vertices.resize(cell->n_vertices());
8149 for (const auto i : cell->vertex_indices())
8150 cells[id].vertices[i] = cell->vertex_index(i);
8151 cells[id].material_id = cell->material_id();
8152 cells[id].manifold_id = cell->manifold_id();
8153 }
8154
8155 SubCellData subcelldata;
8156 switch (dim)
8157 {
8158 case 1:
8159 {
8160 // Nothing to do in 1d
8161 break;
8162 }
8163
8164 case 2:
8165 {
8166 std::vector<bool> user_flags_line;
8167 in_tria.save_user_flags_line(user_flags_line);
8168 const_cast<Triangulation<dim, spacedim1> &>(in_tria)
8169 .clear_user_flags_line();
8170
8171 // Loop over all the faces of the triangulation and create
8172 // objects that describe their boundary and manifold ids.
8173 for (const auto &face : in_tria.active_face_iterators())
8174 {
8175 if (face->at_boundary())
8176 {
8177 CellData<1> boundary_line;
8178
8179 boundary_line.vertices.resize(face->n_vertices());
8180 for (const auto i : face->vertex_indices())
8181 boundary_line.vertices[i] = face->vertex_index(i);
8182 boundary_line.boundary_id = face->boundary_id();
8183 boundary_line.manifold_id = face->manifold_id();
8184
8185 subcelldata.boundary_lines.emplace_back(
8186 std::move(boundary_line));
8187 }
8188 else
8189 // The face is not at the boundary. We won't have to set
8190 // boundary_ids (that is not possible for interior faces), but
8191 // we need to do something if the manifold-id is not the
8192 // default.
8193 //
8194 // We keep track via the user flags whether we have already
8195 // dealt with a face or not. (We need to do that here because
8196 // we will return to interior faces twice, once for each
8197 // neighbor, whereas we only touch each of the boundary faces
8198 // above once.)
8199 if ((face->user_flag_set() == false) &&
8200 (face->manifold_id() != numbers::flat_manifold_id))
8201 {
8202 CellData<1> boundary_line;
8203
8204 boundary_line.vertices.resize(face->n_vertices());
8205 for (const auto i : face->vertex_indices())
8206 boundary_line.vertices[i] = face->vertex_index(i);
8207 boundary_line.boundary_id =
8209 boundary_line.manifold_id = face->manifold_id();
8210
8211 subcelldata.boundary_lines.emplace_back(
8212 std::move(boundary_line));
8213
8214 face->set_user_flag();
8215 }
8216 }
8217
8218 // Reset the user flags to their previous values:
8219 const_cast<Triangulation<dim, spacedim1> &>(in_tria)
8220 .load_user_flags_line(user_flags_line);
8221
8222 break;
8223 }
8224
8225 case 3:
8226 {
8227 std::vector<bool> user_flags_line;
8228 in_tria.save_user_flags_line(user_flags_line);
8229 const_cast<Triangulation<dim, spacedim1> &>(in_tria)
8230 .clear_user_flags_line();
8231
8232 std::vector<bool> user_flags_quad;
8233 in_tria.save_user_flags_quad(user_flags_quad);
8234 const_cast<Triangulation<dim, spacedim1> &>(in_tria)
8235 .clear_user_flags_quad();
8236
8237 // Loop over all the faces of the triangulation and create
8238 // objects that describe their boundary and manifold ids.
8239 for (const auto &face : in_tria.active_face_iterators())
8240 {
8241 if (face->at_boundary())
8242 {
8243 CellData<2> boundary_face;
8244
8245 boundary_face.vertices.resize(face->n_vertices());
8246 for (const auto i : face->vertex_indices())
8247 boundary_face.vertices[i] = face->vertex_index(i);
8248 boundary_face.boundary_id = face->boundary_id();
8249 boundary_face.manifold_id = face->manifold_id();
8250
8251 subcelldata.boundary_quads.emplace_back(
8252 std::move(boundary_face));
8253
8254 // Then also loop over the edges and do the same. We would
8255 // accidentally create duplicates for edges that are part of
8256 // two boundary faces. To avoid this, use the user_flag on
8257 // edges to mark those that we have already visited. (Note
8258 // how we save and restore those above and below.)
8259 for (unsigned int e = 0; e < face->n_lines(); ++e)
8260 if (face->line(e)->user_flag_set() == false)
8261 {
8262 const typename Triangulation<dim,
8263 spacedim1>::line_iterator
8264 edge = face->line(e);
8265 CellData<1> boundary_edge;
8266
8267 boundary_edge.vertices.resize(edge->n_vertices());
8268 for (const auto i : edge->vertex_indices())
8269 boundary_edge.vertices[i] = edge->vertex_index(i);
8270 boundary_edge.boundary_id = edge->boundary_id();
8271 boundary_edge.manifold_id = edge->manifold_id();
8272
8273 subcelldata.boundary_lines.emplace_back(
8274 std::move(boundary_edge));
8275
8276 edge->set_user_flag();
8277 }
8278 }
8279 else
8280 // The face is not at the boundary. We won't have to set
8281 // boundary_ids (that is not possible for interior faces), but
8282 // we need to do something if the manifold-id is not the
8283 // default.
8284 //
8285 // We keep track via the user flags whether we have already
8286 // dealt with a face or not. (We need to do that here because
8287 // we will return to interior faces twice, once for each
8288 // neighbor, whereas we only touch each of the boundary faces
8289 // above once.)
8290 //
8291 // Note that if we have already dealt with a face, then we
8292 // have also already dealt with the edges and don't have
8293 // to worry about that any more separately.
8294 if (face->user_flag_set() == false)
8295 {
8296 if (face->manifold_id() != numbers::flat_manifold_id)
8297 {
8298 CellData<2> boundary_face;
8299
8300 boundary_face.vertices.resize(face->n_vertices());
8301 for (const auto i : face->vertex_indices())
8302 boundary_face.vertices[i] = face->vertex_index(i);
8303 boundary_face.boundary_id =
8305 boundary_face.manifold_id = face->manifold_id();
8306
8307 subcelldata.boundary_quads.emplace_back(
8308 std::move(boundary_face));
8309
8310 face->set_user_flag();
8311 }
8312
8313 // Then also loop over the edges of this face. Because
8314 // every boundary edge must also be a part of a boundary
8315 // face, we can ignore these. But it is possible that we
8316 // have already encountered an interior edge through a
8317 // previous face, and in that case we have to just ignore
8318 // it
8319 for (unsigned int e = 0; e < face->n_lines(); ++e)
8320 if (face->line(e)->at_boundary() == false)
8321 if (face->line(e)->user_flag_set() == false)
8322 {
8323 const typename Triangulation<dim, spacedim1>::
8324 line_iterator edge = face->line(e);
8325 CellData<1> boundary_edge;
8326
8327 boundary_edge.vertices.resize(edge->n_vertices());
8328 for (const auto i : edge->vertex_indices())
8329 boundary_edge.vertices[i] =
8330 edge->vertex_index(i);
8331 boundary_edge.boundary_id =
8333 boundary_edge.manifold_id = edge->manifold_id();
8334
8335 subcelldata.boundary_lines.emplace_back(
8336 std::move(boundary_edge));
8337
8338 edge->set_user_flag();
8339 }
8340 }
8341 }
8342
8343 // Reset the user flags to their previous values:
8344 const_cast<Triangulation<dim, spacedim1> &>(in_tria)
8345 .load_user_flags_line(user_flags_line);
8346 const_cast<Triangulation<dim, spacedim1> &>(in_tria)
8347 .load_user_flags_quad(user_flags_quad);
8348
8349 break;
8350 }
8351 default:
8353 }
8354
8355 out_tria.create_triangulation(v, cells, subcelldata);
8356
8357 for (const auto i : out_tria.get_manifold_ids())
8360 }
8361
8362
8363
8364 template <int dim, int spacedim>
8365 void
8368 const unsigned int n_divisions)
8369 {
8370 if constexpr (dim == 1)
8371 {
8372 out_tria.copy_triangulation(in_tria);
8373 return;
8374 }
8375 else if constexpr (dim == 2)
8377 n_divisions == 2 || n_divisions == 8,
8378 ExcMessage(
8379 "Quadrilaterals must be split into either 2 or 8 triangles."));
8380 else if constexpr (dim == 3)
8381 AssertThrow(n_divisions == 6 || n_divisions == 24,
8382 ExcMessage(
8383 "Hexahedra must be split into either 6 or 24 tetrahedra."));
8384
8386 ExcMessage(
8387 "GridGenerator::convert_hypercube_to_simplex_mesh() expects "
8388 "a Triangulation that consists only of quads/hexes."));
8389
8391 if (in_tria.n_global_levels() > 1)
8392 {
8394 flatten_triangulation(in_tria, temp_tria);
8395 }
8396 const Triangulation<dim, spacedim> &ref_tria =
8397 in_tria.n_global_levels() > 1 ? temp_tria : in_tria;
8398
8399 // static tables with the definitions of cells, faces and edges by its
8400 // vertices for 2d and 3d. For the inheritance of the manifold_id,
8401 // definitions of inner-faces and boundary-faces are required. In case of
8402 // 3d, also inner-edges and boundary-edges need to be defined.
8403
8404 // Cell definition 2d:
8405 static const ndarray<unsigned int, 2, 3> vertex_ids_for_cells_2d_2 = {
8406 {{{0, 1, 2}}, {{3, 2, 1}}}};
8407 static const ndarray<unsigned int, 8, 3> vertex_ids_for_cells_2d_8 = {
8408 {{{0, 6, 4}},
8409 {{8, 4, 6}},
8410 {{8, 6, 5}},
8411 {{1, 5, 6}},
8412 {{2, 4, 7}},
8413 {{8, 7, 4}},
8414 {{8, 5, 7}},
8415 {{3, 7, 5}}}};
8416 [[maybe_unused]] const auto vertex_ids_for_cells_2d =
8417 n_divisions == 2 ? make_array_view(vertex_ids_for_cells_2d_2) :
8418 make_array_view(vertex_ids_for_cells_2d_8);
8419
8420 // Cell definition 3d:
8421 static const ndarray<unsigned int, 6, 4> vertex_ids_for_cells_3d_6 = {
8422 {{{0, 1, 3, 7}},
8423 {{0, 1, 7, 5}},
8424 {{0, 7, 3, 2}},
8425 {{2, 6, 0, 7}},
8426 {{4, 7, 5, 0}},
8427 {{4, 6, 7, 0}}}};
8428 static const ndarray<unsigned int, 24, 4> vertex_ids_for_cells_3d_24 = {
8429 {{{0, 1, 12, 10}}, {{2, 3, 11, 12}}, {{7, 6, 11, 13}},
8430 {{5, 4, 13, 10}}, {{0, 2, 8, 12}}, {{4, 6, 13, 8}},
8431 {{5, 13, 7, 9}}, {{1, 9, 3, 12}}, {{0, 8, 4, 10}},
8432 {{1, 5, 9, 10}}, {{3, 7, 11, 9}}, {{2, 6, 8, 11}},
8433 {{12, 13, 10, 9}}, {{12, 13, 9, 11}}, {{12, 13, 11, 8}},
8434 {{12, 13, 8, 10}}, {{13, 8, 10, 4}}, {{13, 10, 9, 5}},
8435 {{13, 9, 11, 7}}, {{13, 11, 8, 6}}, {{10, 12, 9, 1}},
8436 {{9, 12, 11, 3}}, {{11, 12, 8, 2}}, {{8, 12, 10, 0}}}};
8437
8438 [[maybe_unused]] const auto vertex_ids_for_cells_3d =
8439 n_divisions == 6 ? make_array_view(vertex_ids_for_cells_3d_6) :
8440 make_array_view(vertex_ids_for_cells_3d_24);
8441
8442 // Boundary-faces 2d:
8443 // For 2 new Triangles the lines are identical to the original.
8444 static const std::
8445 array<std::pair<unsigned int, std::array<unsigned int, 2>>, 4>
8446 vertex_ids_for_boundary_faces_2d_2 = {
8447 {{0, {{0, 2}}}, {1, {{1, 3}}}, {2, {{0, 1}}}, {3, {{2, 3}}}}};
8448 // After converting, each of the 4 quadrilateral faces is defined by faces
8449 // of 2 different triangles, i.e., lines. The first value in each pair is
8450 // the original face index and the second is the new line.
8451 static const std::
8452 array<std::pair<unsigned int, std::array<unsigned int, 2>>, 8>
8453 vertex_ids_for_boundary_faces_2d_8 = {{{0, {{0, 4}}},
8454 {0, {{4, 2}}},
8455 {1, {{1, 5}}},
8456 {1, {{5, 3}}},
8457 {2, {{0, 6}}},
8458 {2, {{6, 1}}},
8459 {3, {{2, 7}}},
8460 {3, {{7, 3}}}}};
8461 [[maybe_unused]] const auto vertex_ids_for_boundary_faces_2d =
8462 n_divisions == 2 ? make_array_view(vertex_ids_for_boundary_faces_2d_2) :
8463 make_array_view(vertex_ids_for_boundary_faces_2d_8);
8464
8465 // Boundary-faces 3d:
8466 // The minimal split creates two new triangles on each face.
8467 static const std::
8468 array<std::pair<unsigned int, std::array<unsigned int, 3>>, 12>
8469 vertex_ids_for_boundary_faces_3d_6 = {{{0, {{2, 6, 0}}},
8470 {0, {{6, 4, 0}}},
8471 {1, {{3, 1, 7}}},
8472 {1, {{7, 1, 5}}},
8473 {2, {{1, 0, 5}}},
8474 {2, {{4, 5, 0}}},
8475 {3, {{6, 2, 7}}},
8476 {3, {{3, 7, 2}}},
8477 {4, {{0, 1, 3}}},
8478 {4, {{0, 3, 2}}},
8479 {5, {{4, 6, 7}}},
8480 {5, {{4, 7, 5}}}}};
8481 // After converting, each of the 6 hexahedron faces corresponds to faces of
8482 // 4 different tetrahedron faces, i.e., triangles. Note that a triangle is
8483 // defined by 3 vertices.
8484 static const std::
8485 array<std::pair<unsigned int, std::array<unsigned int, 3>>, 24>
8486 vertex_ids_for_boundary_faces_3d_24 = {
8487 {{0, {{0, 4, 8}}}, {0, {{4, 8, 6}}}, {0, {{8, 6, 2}}},
8488 {0, {{0, 2, 8}}}, {1, {{1, 3, 9}}}, {1, {{3, 9, 7}}},
8489 {1, {{9, 7, 5}}}, {1, {{1, 9, 5}}}, {2, {{0, 1, 10}}},
8490 {2, {{1, 10, 5}}}, {2, {{10, 5, 4}}}, {2, {{0, 10, 4}}},
8491 {3, {{2, 3, 11}}}, {3, {{3, 11, 7}}}, {3, {{11, 7, 6}}},
8492 {3, {{2, 11, 6}}}, {4, {{0, 1, 12}}}, {4, {{1, 12, 3}}},
8493 {4, {{12, 3, 2}}}, {4, {{0, 12, 2}}}, {5, {{4, 5, 13}}},
8494 {5, {{5, 13, 7}}}, {5, {{13, 7, 6}}}, {5, {{4, 13, 6}}}}};
8495 [[maybe_unused]] const auto vertex_ids_for_boundary_faces_3d =
8496 n_divisions == 6 ? make_array_view(vertex_ids_for_boundary_faces_3d_6) :
8497 make_array_view(vertex_ids_for_boundary_faces_3d_24);
8498
8499 // Inner-faces 2d:
8500 // With a single split there is only one new internal face.
8501 static const ndarray<unsigned int, 1, 2> vertex_ids_for_inner_faces_2d_2 = {
8502 {{{1, 2}}}};
8503 // The converted triangulation based on simplices has 8 faces that do not
8504 // form the boundary, i.e. inner-faces, each defined by 2 vertices.
8505 static const ndarray<unsigned int, 8, 2> vertex_ids_for_inner_faces_2d_8 = {
8506 {{{6, 4}},
8507 {{6, 8}},
8508 {{6, 5}},
8509 {{4, 8}},
8510 {{8, 5}},
8511 {{7, 4}},
8512 {{7, 8}},
8513 {{7, 5}}}};
8514 [[maybe_unused]] const auto vertex_ids_for_inner_faces_2d =
8515 n_divisions == 2 ? make_array_view(vertex_ids_for_inner_faces_2d_2) :
8516 make_array_view(vertex_ids_for_inner_faces_2d_8);
8517
8518 // Inner-faces 3d:
8519 // Note that all inner faces include vertices 0 and 7.
8520 static const ndarray<unsigned int, 6, 3> vertex_ids_for_inner_faces_3d_6 = {
8521 {
8522 {{1, 0, 7}},
8523 {{7, 0, 2}},
8524 {{0, 7, 5}},
8525 {{0, 3, 7}},
8526 {{7, 4, 0}},
8527 {{0, 6, 7}},
8528 }};
8529 // The converted triangulation based on simplices has 72 faces that do not
8530 // form the boundary, i.e. inner-faces, each defined by 3 vertices.
8531 static const ndarray<unsigned int, 72, 3> vertex_ids_for_inner_faces_3d_24 =
8532 {{
8533 {{0, 12, 10}}, {{12, 1, 10}}, {{12, 1, 9}}, {{12, 3, 9}},
8534 {{12, 2, 11}}, {{12, 3, 11}}, {{12, 0, 8}}, {{12, 2, 8}},
8535 {{9, 13, 5}}, {{13, 7, 9}}, {{11, 7, 13}}, {{11, 6, 13}},
8536 {{4, 8, 13}}, {{6, 8, 13}}, {{4, 13, 10}}, {{13, 5, 10}},
8537 {{10, 9, 5}}, {{10, 9, 1}}, {{11, 9, 7}}, {{11, 9, 3}},
8538 {{8, 11, 2}}, {{8, 11, 6}}, {{8, 10, 0}}, {{8, 10, 4}},
8539 {{12, 3, 9}}, {{12, 9, 11}}, {{12, 3, 11}}, {{3, 9, 11}},
8540 {{2, 12, 8}}, {{2, 12, 11}}, {{2, 11, 8}}, {{8, 12, 11}},
8541 {{0, 12, 10}}, {{0, 12, 8}}, {{0, 8, 10}}, {{8, 10, 12}},
8542 {{12, 1, 10}}, {{12, 1, 9}}, {{1, 10, 9}}, {{10, 9, 12}},
8543 {{10, 8, 4}}, {{10, 8, 13}}, {{4, 13, 8}}, {{4, 13, 10}},
8544 {{10, 9, 13}}, {{10, 9, 5}}, {{13, 5, 10}}, {{13, 5, 9}},
8545 {{13, 7, 9}}, {{13, 7, 11}}, {{9, 11, 13}}, {{9, 11, 7}},
8546 {{8, 11, 13}}, {{8, 11, 6}}, {{6, 13, 8}}, {{6, 13, 11}},
8547 {{12, 13, 10}}, {{12, 13, 8}}, {{8, 10, 13}}, {{8, 10, 12}},
8548 {{12, 13, 10}}, {{12, 13, 9}}, {{10, 9, 13}}, {{10, 9, 12}},
8549 {{12, 13, 9}}, {{12, 13, 11}}, {{9, 11, 13}}, {{9, 11, 12}},
8550 {{12, 13, 11}}, {{12, 13, 8}}, {{8, 11, 13}}, {{8, 11, 12}},
8551 }};
8552 [[maybe_unused]] const auto vertex_ids_for_inner_faces_3d =
8553 n_divisions == 6 ? make_array_view(vertex_ids_for_inner_faces_3d_6) :
8554 make_array_view(vertex_ids_for_inner_faces_3d_24);
8555
8556 // Inner-edges 3d:
8557 // This split only requires a single new internal line.
8558 static const ndarray<unsigned int, 1, 2> vertex_ids_for_inner_edges_3d_6 = {
8559 {{{0, 7}}}};
8560 // The converted triangulation based on simplices has 60 edges that do not
8561 // coincide with the boundary, i.e. inner-edges, each defined by 2 vertices.
8562 static const ndarray<unsigned int, 60, 2> vertex_ids_for_inner_edges_3d_24 =
8563 {{{{12, 10}}, {{12, 9}}, {{12, 11}}, {{12, 8}}, {{9, 13}}, {{11, 13}},
8564 {{8, 13}}, {{10, 13}}, {{10, 9}}, {{9, 11}}, {{11, 8}}, {{8, 10}},
8565 {{12, 9}}, {{12, 11}}, {{11, 9}}, {{12, 8}}, {{12, 11}}, {{11, 8}},
8566 {{12, 8}}, {{12, 10}}, {{10, 8}}, {{12, 10}}, {{12, 9}}, {{9, 10}},
8567 {{13, 10}}, {{13, 8}}, {{8, 10}}, {{13, 10}}, {{13, 9}}, {{9, 10}},
8568 {{13, 11}}, {{13, 9}}, {{11, 9}}, {{13, 11}}, {{13, 8}}, {{11, 8}},
8569 {{12, 13}}, {{8, 10}}, {{8, 13}}, {{10, 13}}, {{8, 12}}, {{10, 12}},
8570 {{12, 13}}, {{10, 9}}, {{10, 13}}, {{9, 13}}, {{10, 12}}, {{9, 12}},
8571 {{12, 13}}, {{9, 11}}, {{9, 13}}, {{11, 13}}, {{9, 12}}, {{11, 12}},
8572 {{12, 13}}, {{11, 8}}, {{11, 13}}, {{8, 13}}, {{11, 12}}, {{8, 12}}}};
8573 [[maybe_unused]] const auto vertex_ids_for_inner_edges_3d =
8574 n_divisions == 6 ? make_array_view(vertex_ids_for_inner_edges_3d_6) :
8575 make_array_view(vertex_ids_for_inner_edges_3d_24);
8576
8577 // Boundary-edges 3d:
8578 //
8579 // All implemented conversions re-use the existing 12 lines of each
8580 // hexahedron so those are not included in these tables.
8581 //
8582 // Since each boundary face has two tetrahedron faces, there is just one new
8583 // line per face.
8584 static const std::
8585 array<std::pair<unsigned int, std::array<unsigned int, 2>>, 6>
8586 vertex_ids_for_new_boundary_edges_3d_6 = {{{0, {{0, 6}}},
8587 {1, {{1, 7}}},
8588 {2, {{0, 5}}},
8589 {3, {{2, 7}}},
8590 {4, {{0, 3}}},
8591 {5, {{4, 7}}}}};
8592 // For each of the 6 boundary-faces of the hexahedron, there are 8 edges (of
8593 // different tetrahedrons) that coincide with the boundary, i.e.
8594 // boundary-edges. Each boundary-edge is defined by 2 vertices. 4 of these
8595 // edges are new (they are placed in the middle of a presently existing
8596 // face); the other 4 coincide with edges present in the hexahedral
8597 // triangulation. The new 4 edges inherit the manifold id of the relevant
8598 // face, but the other 4 need to be copied from the input and thus do not
8599 // require a lookup table.
8600 static const std::
8601 array<std::pair<unsigned int, std::array<unsigned int, 2>>, 24>
8602 vertex_ids_for_new_boundary_edges_3d_24 = {
8603 {{0, {{4, 8}}}, {0, {{6, 8}}}, {0, {{0, 8}}}, {0, {{2, 8}}},
8604 {1, {{5, 9}}}, {1, {{7, 9}}}, {1, {{1, 9}}}, {1, {{3, 9}}},
8605 {2, {{4, 10}}}, {2, {{5, 10}}}, {2, {{0, 10}}}, {2, {{1, 10}}},
8606 {3, {{6, 11}}}, {3, {{7, 11}}}, {3, {{2, 11}}}, {3, {{3, 11}}},
8607 {4, {{2, 12}}}, {4, {{3, 12}}}, {4, {{0, 12}}}, {4, {{1, 12}}},
8608 {5, {{6, 13}}}, {5, {{7, 13}}}, {5, {{4, 13}}}, {5, {{5, 13}}}}};
8609 [[maybe_unused]] const auto vertex_ids_for_new_boundary_edges_3d =
8610 n_divisions == 6 ?
8611 make_array_view(vertex_ids_for_new_boundary_edges_3d_6) :
8612 make_array_view(vertex_ids_for_new_boundary_edges_3d_24);
8613
8614 std::vector<Point<spacedim>> vertices;
8615 std::vector<CellData<dim>> cells;
8616 SubCellData subcell_data;
8617
8618 // store for each vertex and face the assigned index so that we only
8619 // assign them a value once
8620 std::vector<unsigned int> old_to_new_vertex_indices(
8622 std::vector<unsigned int> face_to_new_vertex_indices(
8624
8625 // We first have to create all of the new vertices. To do this, we loop over
8626 // all cells and on each cell
8627 // (i) copy the existing vertex locations (and record their new indices in
8628 // the 'old_to_new_vertex_indices' vector),
8629 // (ii) create new midpoint vertex locations for each face (and record their
8630 // new indices in the 'face_to_new_vertex_indices' vector),
8631 // (iii) create new midpoint vertex locations for each cell (dim = 2 only)
8632 for (const auto &cell : ref_tria.cell_iterators())
8633 {
8634 // temporary array storing the global indices of each cell entity in the
8635 // sequence: vertices, edges/faces, cell
8636 std::array<unsigned int, dim == 2 ? 9 : 14> local_vertex_indices;
8637 local_vertex_indices.fill(numbers::invalid_unsigned_int);
8638
8639 // (i) copy the existing vertex locations
8640 for (const auto v : cell->vertex_indices())
8641 {
8642 const auto v_global = cell->vertex_index(v);
8643
8644 if (old_to_new_vertex_indices[v_global] ==
8646 {
8647 old_to_new_vertex_indices[v_global] = vertices.size();
8648 vertices.push_back(cell->vertex(v));
8649 }
8650
8651 AssertIndexRange(v, local_vertex_indices.size());
8652 local_vertex_indices[v] = old_to_new_vertex_indices[v_global];
8653 }
8654
8655 // (ii) create new midpoint vertex locations for each face
8656 if constexpr (dim > 1)
8657 for (const auto f : cell->face_indices())
8658 {
8659 const auto f_global = cell->face_index(f);
8660
8661 if (face_to_new_vertex_indices[f_global] ==
8663 {
8664 face_to_new_vertex_indices[f_global] = vertices.size();
8665 vertices.push_back(
8666 cell->face(f)->center(/*respect_manifold*/ true));
8667 }
8668
8669 AssertIndexRange(cell->n_vertices() + f,
8670 local_vertex_indices.size());
8671 local_vertex_indices[cell->n_vertices() + f] =
8672 face_to_new_vertex_indices[f_global];
8673 }
8674
8675 // (iii) create new midpoint vertex locations for each cell
8676 if (dim == 2)
8677 {
8678 AssertIndexRange(cell->n_vertices() + cell->n_faces(),
8679 local_vertex_indices.size());
8680 local_vertex_indices[cell->n_vertices() + cell->n_faces()] =
8681 vertices.size();
8682 vertices.push_back(cell->center(/*respect_manifold*/ true));
8683 }
8684
8685 // helper function for creating cells and subcells
8686 [[maybe_unused]] const auto add_cell =
8687 [&](const unsigned int struct_dim,
8688 const auto &index_vertices,
8689 const unsigned int material_or_boundary_id,
8690 const unsigned int manifold_id = 0) {
8691 // sub-cell data only has to be stored if the information differs
8692 // from the default
8693 if (struct_dim < dim && (material_or_boundary_id ==
8695 manifold_id == numbers::flat_manifold_id))
8696 return;
8697
8698 if (struct_dim == dim) // cells
8699 {
8700 AssertDimension(index_vertices.size(), dim + 1);
8701
8702 CellData<dim> cell_data(index_vertices.size());
8703 cell_data.material_id =
8704 material_or_boundary_id; // inherit material id
8705 cell_data.manifold_id = manifold_id; // inherit cell-manifold id
8706 for (unsigned int i = 0; i < cell_data.vertices.size(); ++i)
8707 {
8708 AssertIndexRange(index_vertices[i],
8709 local_vertex_indices.size());
8710 cell_data.vertices[i] =
8711 local_vertex_indices[index_vertices[i]];
8712 }
8713 cells.emplace_back(std::move(cell_data));
8714 }
8715 else if (dim == 2 && struct_dim == 1) // an edge of a simplex
8716 {
8717 Assert(index_vertices.size() == 2, ExcInternalError());
8718
8719 CellData<1> boundary_line(2);
8720 boundary_line.boundary_id = material_or_boundary_id;
8721 boundary_line.manifold_id = manifold_id;
8722 for (unsigned int i = 0; i < boundary_line.vertices.size(); ++i)
8723 {
8724 AssertIndexRange(index_vertices[i],
8725 local_vertex_indices.size());
8726 boundary_line.vertices[i] =
8727 local_vertex_indices[index_vertices[i]];
8728 }
8729 subcell_data.boundary_lines.emplace_back(
8730 std::move(boundary_line));
8731 }
8732 else if (dim == 3 && struct_dim == 2) // a face of a tetrahedron
8733 {
8734 Assert(index_vertices.size() == 3, ExcInternalError());
8735
8736 CellData<2> boundary_quad(3);
8737 boundary_quad.material_id = material_or_boundary_id;
8738 boundary_quad.manifold_id = manifold_id;
8739 for (unsigned int i = 0; i < boundary_quad.vertices.size(); ++i)
8740 {
8741 AssertIndexRange(index_vertices[i],
8742 local_vertex_indices.size());
8743 boundary_quad.vertices[i] =
8744 local_vertex_indices[index_vertices[i]];
8745 }
8746 subcell_data.boundary_quads.emplace_back(
8747 std::move(boundary_quad));
8748 }
8749 else if (dim == 3 && struct_dim == 1) // an edge of a tetrahedron
8750 {
8751 Assert(index_vertices.size() == 2, ExcInternalError());
8752
8753 CellData<1> boundary_line(2);
8754 boundary_line.boundary_id = material_or_boundary_id;
8755 boundary_line.manifold_id = manifold_id;
8756 for (unsigned int i = 0; i < boundary_line.vertices.size(); ++i)
8757 {
8758 AssertIndexRange(index_vertices[i],
8759 local_vertex_indices.size());
8760 boundary_line.vertices[i] =
8761 local_vertex_indices[index_vertices[i]];
8762 }
8763 subcell_data.boundary_lines.emplace_back(
8764 std::move(boundary_line));
8765 }
8766 else
8767 {
8769 }
8770 };
8771
8772 // create cells one by one
8773 if constexpr (dim == 2)
8774 {
8775 // get info from current quad cell:
8776 const auto manifold_id_cell = cell->manifold_id();
8777 const auto material_id_cell = cell->material_id();
8778
8779 // inherit cell manifold
8780 for (const auto &cell_vertices : vertex_ids_for_cells_2d)
8781 add_cell(dim, cell_vertices, material_id_cell, manifold_id_cell);
8782
8783 // inherit inner manifold (faces)
8784 for (const auto &face_vertices : vertex_ids_for_inner_faces_2d)
8785 // set inner tri-faces according to cell-manifold of quad
8786 // element, set invalid b_id, since we do not want to modify
8787 // b_id inside
8788 add_cell(1,
8789 face_vertices,
8791 manifold_id_cell);
8792 }
8793 else if constexpr (dim == 3)
8794 {
8795 // get info from current quad cell:
8796 const auto manifold_id_cell = cell->manifold_id();
8797 const auto material_id_cell = cell->material_id();
8798
8799 for (const auto &cell_vertices : vertex_ids_for_cells_3d)
8800 add_cell(dim, cell_vertices, material_id_cell, manifold_id_cell);
8801
8802 // set manifold of inner FACES of tets according to
8803 // hex-cell-manifold
8804 for (const auto &face_vertices : vertex_ids_for_inner_faces_3d)
8805 add_cell(2,
8806 face_vertices,
8808 manifold_id_cell);
8809
8810 // set manifold of inner EDGES of tets according to
8811 // hex-cell-manifold
8812 for (const auto &edge_vertices : vertex_ids_for_inner_edges_3d)
8813 add_cell(1,
8814 edge_vertices,
8816 manifold_id_cell);
8817 }
8818 else
8820
8821 // Set up sub-cell data.
8822 if constexpr (dim == 2)
8823 {
8824 for (const auto &[quad_face_no, tri_face_vertices] :
8825 vertex_ids_for_boundary_faces_2d)
8826 {
8827 const auto face = cell->face(quad_face_no);
8828 add_cell(1,
8829 tri_face_vertices,
8830 face->boundary_id(),
8831 face->manifold_id());
8832 }
8833 }
8834 else if constexpr (dim == 3)
8835 {
8836 for (const auto &[hex_face_no, tet_face_vertices] :
8837 vertex_ids_for_boundary_faces_3d)
8838 {
8839 const auto face = cell->face(hex_face_no);
8840 add_cell(2,
8841 tet_face_vertices,
8842 face->boundary_id(),
8843 face->manifold_id());
8844 }
8845
8846 for (const auto &[hex_face_no, tet_edge_vertices] :
8847 vertex_ids_for_new_boundary_edges_3d)
8848 {
8849 const auto face = cell->face(hex_face_no);
8850 add_cell(1,
8851 tet_edge_vertices,
8852 face->boundary_id(),
8853 face->manifold_id());
8854 }
8855 }
8856 else
8858
8859 // set manifold ids of edges that were already present in the
8860 // triangulation.
8861 if constexpr (dim == 3)
8862 {
8863 for (const auto e : cell->line_indices())
8864 {
8865 auto edge = cell->line(e);
8866 // Rather than use add_cell(), which does additional index
8867 // translation, just add edges directly into subcell_data since
8868 // we already know the correct global vertex indices.
8869 CellData<1> edge_data;
8870 edge_data.vertices[0] =
8871 old_to_new_vertex_indices[edge->vertex_index(0)];
8872 edge_data.vertices[1] =
8873 old_to_new_vertex_indices[edge->vertex_index(1)];
8874 edge_data.boundary_id = edge->boundary_id();
8875 edge_data.manifold_id = edge->manifold_id();
8876
8877 subcell_data.boundary_lines.emplace_back(std::move(edge_data));
8878 }
8879 }
8880 }
8881
8882 out_tria.clear();
8883 out_tria.create_triangulation(vertices, cells, subcell_data);
8884
8885 for (const auto i : out_tria.get_manifold_ids())
8888 }
8889
8890
8891
8892 template <int dim, int spacedim>
8893 void
8896 {
8897 if constexpr (dim == 1)
8898 {
8899 out_tria.copy_triangulation(in_tria);
8900 return;
8901 }
8902 else
8903 {
8906 ExcMessage(
8907 "GridGenerator::convert_simplex_to_hypercube_mesh() expects "
8908 "a Triangulation that consists only of triangles/tetrahedra."));
8909
8911 if (in_tria.n_global_levels() > 1)
8912 {
8914 flatten_triangulation(in_tria, temp_tria);
8915 }
8916 const Triangulation<dim, spacedim> &ref_tria =
8917 in_tria.n_global_levels() > 1 ? temp_tria : in_tria;
8918
8919 // First set up all of the vertices of the new mesh, along with
8920 // a map that maps from edges/faces/cells to their new midpoints
8921 std::vector<Point<spacedim>> vertices = ref_tria.get_vertices();
8922 vertices.reserve(ref_tria.get_vertices().size() + ref_tria.n_lines() +
8923 (dim > 2 ? ref_tria.n_quads() : 0) +
8924 ref_tria.n_cells());
8925 std::map<typename Triangulation<dim, spacedim>::line_iterator,
8926 unsigned int>
8927 line_to_midpoint_vertex_map;
8928 std::map<typename Triangulation<dim, spacedim>::face_iterator,
8929 unsigned int>
8930 face_to_midpoint_vertex_map;
8931 std::map<typename Triangulation<dim, spacedim>::cell_iterator,
8932 unsigned int>
8933 cell_to_midpoint_vertex_map;
8934 for (const auto &cell : ref_tria.active_cell_iterators())
8935 {
8936 // First the faces that bound the cell. Make sure we touch
8937 // every face only once
8938 for (const auto &face : cell->face_iterators())
8939 if (face_to_midpoint_vertex_map.find(face) ==
8940 face_to_midpoint_vertex_map.end())
8941 {
8942 // Add the new vertex to the list of vertices
8943 {
8944 const Point<spacedim> new_vertex = face->center();
8945 vertices.push_back(new_vertex);
8946 }
8947
8948 // Also keep track of the index given to this vertex:
8949 face_to_midpoint_vertex_map[face] = vertices.size() - 1;
8950
8951 // If we're in 3d, we also need to separately deal with the
8952 // edges:
8953 if constexpr (dim == 3)
8954 {
8955 for (unsigned int l = 0; l < face->n_lines(); ++l)
8956 {
8957 const typename Triangulation<dim,
8958 spacedim>::line_iterator
8959 line = face->line(l);
8960 if (line_to_midpoint_vertex_map.find(line) ==
8961 line_to_midpoint_vertex_map.end())
8962 {
8963 // Add the new vertex to the list of vertices
8964 {
8965 const Point<spacedim> new_vertex =
8966 line->center();
8967 vertices.push_back(new_vertex);
8968 }
8969
8970 // Also keep track of the index given to this
8971 // vertex:
8972 line_to_midpoint_vertex_map[line] =
8973 vertices.size() - 1;
8974 }
8975 }
8976 }
8977 }
8978
8979 // Now for the cell midpoint:
8980 // Add the new vertex to the list of vertices
8981 {
8982 const Point<spacedim> new_vertex = cell->center();
8983 vertices.push_back(new_vertex);
8984 }
8985
8986 // Also keep track of the index given to this vertex:
8987 cell_to_midpoint_vertex_map[cell] = vertices.size() - 1;
8988 }
8989 // Check that we counted correctly:
8990 Assert(vertices.size() ==
8991 ref_tria.get_vertices().size() + ref_tria.n_lines() +
8992 (dim > 2 ? ref_tria.n_quads() : 0) + ref_tria.n_cells(),
8994
8995 // Now create new cells. Each cell will be subdivided into (dim+1)
8996 // new cells (i.e., one new cell adjacent to each vertex of the old
8997 // cell).
8998 std::vector<CellData<dim>> cell_data;
8999 cell_data.reserve(ref_tria.n_cells() * (dim + 1));
9000
9001 for (const auto &cell : ref_tria.active_cell_iterators())
9002 for (unsigned int v = 0; v < cell->n_vertices(); ++v)
9003 {
9004 CellData<dim> new_cell;
9005
9006 if constexpr (dim == 2)
9007 {
9008 // In triangles, lines 'v' and 'v+2' (mod 3) are adjacent
9009 // to vertex v. Make a quad out of the old vertex, the
9010 // line midpoints, and the new cell midpoint
9011 new_cell.vertices = {
9012 cell->vertex_index(v),
9013 face_to_midpoint_vertex_map[cell->line(v)],
9014 face_to_midpoint_vertex_map[cell->line((v + 2) % 3)],
9015 cell_to_midpoint_vertex_map[cell]};
9016 }
9017 else
9018 {
9019 if (v == 0)
9020 {
9021 new_cell.vertices = {
9022 cell->vertex_index(0),
9023 line_to_midpoint_vertex_map[cell->line(0)],
9024 line_to_midpoint_vertex_map[cell->line(2)],
9025 face_to_midpoint_vertex_map[cell->face(0)],
9026 line_to_midpoint_vertex_map[cell->line(3)],
9027 face_to_midpoint_vertex_map[cell->face(1)],
9028 face_to_midpoint_vertex_map[cell->face(2)],
9029 cell_to_midpoint_vertex_map[cell]};
9030 }
9031 else if (v == 1)
9032 {
9033 new_cell.vertices = {
9034 line_to_midpoint_vertex_map[cell->line(0)],
9035 cell->vertex_index(1),
9036 face_to_midpoint_vertex_map[cell->face(0)],
9037 line_to_midpoint_vertex_map[cell->line(1)],
9038 face_to_midpoint_vertex_map[cell->face(1)],
9039 line_to_midpoint_vertex_map[cell->line(4)],
9040 cell_to_midpoint_vertex_map[cell],
9041 face_to_midpoint_vertex_map[cell->face(3)]};
9042 }
9043 else if (v == 2)
9044 {
9045 new_cell.vertices = {
9046 line_to_midpoint_vertex_map[cell->line(2)],
9047 face_to_midpoint_vertex_map[cell->face(0)],
9048 cell->vertex_index(2),
9049 line_to_midpoint_vertex_map[cell->line(1)],
9050 face_to_midpoint_vertex_map[cell->face(2)],
9051 cell_to_midpoint_vertex_map[cell],
9052 line_to_midpoint_vertex_map[cell->line(5)],
9053 face_to_midpoint_vertex_map[cell->face(3)]};
9054 }
9055 else if (v == 3)
9056 {
9057 new_cell.vertices = {
9058 line_to_midpoint_vertex_map[cell->line(3)],
9059 face_to_midpoint_vertex_map[cell->face(1)],
9060 face_to_midpoint_vertex_map[cell->face(2)],
9061 cell_to_midpoint_vertex_map[cell],
9062 cell->vertex_index(3),
9063 line_to_midpoint_vertex_map[cell->line(4)],
9064 line_to_midpoint_vertex_map[cell->line(5)],
9065 face_to_midpoint_vertex_map[cell->face(3)]};
9066 }
9067 else
9069 }
9070
9071 new_cell.material_id = cell->material_id();
9072 new_cell.manifold_id = cell->manifold_id();
9073
9074 cell_data.emplace_back(std::move(new_cell));
9075 }
9076
9077 // Next also collect information about boundary indicators:
9078 SubCellData subcell_data;
9079 for (const auto &cell : ref_tria.active_cell_iterators())
9080 for (const auto &face : cell->face_iterators())
9081 if (face->at_boundary() && (face->boundary_id() != 0))
9082 // The face is at the boundary and doesn't have the default
9083 // boundary id:
9084 {
9085 if constexpr (dim == 2)
9086 {
9087 CellData<dim - 1> child_face_1, child_face_2;
9088 child_face_1.vertices = {face->vertex_index(0),
9089 face_to_midpoint_vertex_map[face]};
9090 child_face_1.boundary_id = face->boundary_id();
9091 child_face_2.vertices = {face_to_midpoint_vertex_map[face],
9092 face->vertex_index(1)};
9093 child_face_2.boundary_id = face->boundary_id();
9094 subcell_data.boundary_lines.emplace_back(
9095 std::move(child_face_1));
9096 subcell_data.boundary_lines.emplace_back(
9097 std::move(child_face_2));
9098 }
9099 else
9100 {
9101 CellData<dim - 1> child_face_1, child_face_2, child_face_3;
9102
9103 child_face_1.vertices = {
9104 face->vertex_index(0),
9105 line_to_midpoint_vertex_map[face->line(0)],
9106 line_to_midpoint_vertex_map[face->line(2)],
9107 face_to_midpoint_vertex_map[face]};
9108 child_face_1.boundary_id = face->boundary_id();
9109
9110 child_face_2.vertices = {
9111 line_to_midpoint_vertex_map[face->line(0)],
9112 face->vertex_index(1),
9113 face_to_midpoint_vertex_map[face],
9114 line_to_midpoint_vertex_map[face->line(1)]};
9115
9116 child_face_2.boundary_id = face->boundary_id();
9117
9118 child_face_3.vertices = {
9119 line_to_midpoint_vertex_map[face->line(2)],
9120 face_to_midpoint_vertex_map[face],
9121 face->vertex_index(2),
9122 line_to_midpoint_vertex_map[face->line(1)]};
9123 child_face_3.boundary_id = face->boundary_id();
9124
9125 subcell_data.boundary_quads.emplace_back(
9126 std::move(child_face_1));
9127 subcell_data.boundary_quads.emplace_back(
9128 std::move(child_face_2));
9129 subcell_data.boundary_quads.emplace_back(
9130 std::move(child_face_3));
9131 }
9132 }
9133
9134 // In a final step, actually create the triangulation and reset manifold
9135 // ids (because manifolds cannot be reliably transferred from hypercube
9136 // meshes to simplex meshes, given that manifolds object are typically
9137 // specific for one cell type.
9138 out_tria.clear();
9139
9140 if constexpr (dim == 3)
9141 {
9144 }
9145
9146 out_tria.create_triangulation(vertices, cell_data, subcell_data);
9147
9148 for (const auto i : out_tria.get_manifold_ids())
9151 }
9152 }
9153
9154
9155 template <int dim, int spacedim>
9156 void
9159 {
9161 if (in_tria.n_global_levels() > 1)
9162 {
9164 GridGenerator::flatten_triangulation(in_tria, temp_tria);
9165 }
9166 const Triangulation<dim, spacedim> &ref_tria =
9167 in_tria.n_global_levels() > 1 ? temp_tria : in_tria;
9168
9169 // Three triangles connecting to barycenter with vertex index 3:
9170 static const ndarray<unsigned int, 3, 3> vertex_ids_for_cells_2d = {
9171 {{{0, 1, 3}}, {{1, 2, 3}}, {{2, 0, 3}}}};
9172
9173 // Boundary-faces 2d:
9174 // Each face of the original simplex is defined by the following vertices:
9176 vertex_ids_for_boundary_faces_2d = {
9177 {{{{{0, 1}}}}, {{{{1, 2}}}}, {{{{2, 0}}}}}};
9178
9179 // Three tetrahedra connecting to barycenter with vertex index 4:
9180 static const ndarray<unsigned int, 4, 4> vertex_ids_for_cells_3d = {
9181 {{{0, 1, 2, 4}}, {{1, 0, 3, 4}}, {{0, 2, 3, 4}}, {{2, 1, 3, 4}}}};
9182
9183 // Boundary-faces 3d:
9184 // Each face of the original simplex is defined by the following vertices:
9186 vertex_ids_for_boundary_faces_3d = {
9187 {{{{{0, 1, 2}}}}, {{{{1, 0, 3}}}}, {{{{0, 2, 3}}}}, {{{{2, 1, 3}}}}}};
9188
9189 // Boundary-lines 3d:
9190 // Each line/edge of the original simplex is defined by the following
9191 // vertices:
9193 vertex_ids_for_boundary_lines_3d = {{{{{{0, 1}}}},
9194 {{{{1, 2}}}},
9195 {{{{2, 0}}}},
9196 {{{{0, 3}}}},
9197 {{{{1, 3}}}},
9198 {{{{2, 3}}}}}};
9199
9200 std::vector<Point<spacedim>> vertices;
9201 std::vector<CellData<dim>> cells;
9202 SubCellData subcell_data;
9203
9204 // for each vertex we store the assigned index so that we only
9205 // assign them a value once
9206 std::vector<unsigned int> old_to_new_vertex_indices(
9208
9209 // We first have to create all of the new vertices. To do this, we loop
9210 // over all cells and on each cell (i) copy the existing vertex locations
9211 // (and record their new indices in the 'old_to_new_vertex_indices'
9212 // vector), (ii) create new barycenter vertex location
9213 for (const auto &cell : ref_tria.cell_iterators())
9214 {
9216 cell->reference_cell().is_simplex(),
9217 ExcMessage(
9218 "Cell with invalid ReferenceCell encountered. GridGenerator::alfeld_split_of_simplex_mesh() "
9219 "only supports simplex meshes as input."));
9220
9221 // temporary array storing the global indices of each cell entity in
9222 // the sequence: vertices, edges/faces, cell
9223 std::array<unsigned int, (dim == 3) ? 5 : 4> local_vertex_indices;
9224
9225 // (i) copy the existing vertex locations
9226 Point<spacedim> barycenter;
9227 for (const auto v : cell->vertex_indices())
9228 {
9229 const auto v_global = cell->vertex_index(v);
9230
9231 if (old_to_new_vertex_indices[v_global] ==
9233 {
9234 old_to_new_vertex_indices[v_global] = vertices.size();
9235 vertices.push_back(cell->vertex(v));
9236 }
9237
9238 AssertIndexRange(v, local_vertex_indices.size());
9239 local_vertex_indices[v] = old_to_new_vertex_indices[v_global];
9240
9241 barycenter += vertices[local_vertex_indices[v]] - Point<spacedim>();
9242 }
9243
9244 // (ii) barycenter:
9245 local_vertex_indices[local_vertex_indices.size() - 1] = vertices.size();
9246 vertices.push_back(barycenter / static_cast<double>(dim + 1));
9247
9248 // helper function for creating cells and subcells
9249 const auto add_cell = [&](const unsigned int struct_dim,
9250 const auto &index_vertices,
9251 const unsigned int material_or_boundary_id,
9252 const unsigned int manifold_id = 0) {
9253 // sub-cell data only has to be stored if the information differs
9254 // from the default
9255 if (struct_dim < dim &&
9256 (material_or_boundary_id == numbers::internal_face_boundary_id &&
9257 manifold_id == numbers::flat_manifold_id))
9258 return;
9259
9260 if (struct_dim == dim) // cells
9261 {
9262 AssertDimension(index_vertices.size(), dim + 1);
9263
9264 CellData<dim> cell_data(index_vertices.size());
9265 for (unsigned int i = 0; i < index_vertices.size(); ++i)
9266 {
9267 AssertIndexRange(index_vertices[i],
9268 local_vertex_indices.size());
9269 cell_data.vertices[i] =
9270 local_vertex_indices[index_vertices[i]];
9271 }
9272 cell_data.material_id =
9273 material_or_boundary_id; // inherit material id
9274 cell_data.manifold_id = manifold_id; // inherit cell-manifold id
9275 cells.emplace_back(std::move(cell_data));
9276 }
9277 else if (dim == 2 && struct_dim == 1) // an edge of a simplex
9278 {
9279 Assert(index_vertices.size() == 2, ExcInternalError());
9280
9281 CellData<1> boundary_line(2);
9282 boundary_line.boundary_id = material_or_boundary_id;
9283 boundary_line.manifold_id = manifold_id;
9284 for (unsigned int i = 0; i < boundary_line.vertices.size(); ++i)
9285 {
9286 AssertIndexRange(index_vertices[i],
9287 local_vertex_indices.size());
9288 boundary_line.vertices[i] =
9289 local_vertex_indices[index_vertices[i]];
9290 }
9291 subcell_data.boundary_lines.emplace_back(
9292 std::move(boundary_line));
9293 }
9294 else if (dim == 3 && struct_dim == 2) // a face of a tetrahedron
9295 {
9296 Assert(index_vertices.size() == 3, ExcInternalError());
9297
9298 CellData<2> boundary_quad(3);
9299 boundary_quad.material_id = material_or_boundary_id;
9300 boundary_quad.manifold_id = manifold_id;
9301 for (unsigned int i = 0; i < boundary_quad.vertices.size(); ++i)
9302 {
9303 AssertIndexRange(index_vertices[i],
9304 local_vertex_indices.size());
9305 boundary_quad.vertices[i] =
9306 local_vertex_indices[index_vertices[i]];
9307 }
9308 subcell_data.boundary_quads.emplace_back(
9309 std::move(boundary_quad));
9310 }
9311 else if (dim == 3 && struct_dim == 1) // an edge of a tetrahedron
9312 {
9313 Assert(index_vertices.size() == 2, ExcInternalError());
9314
9315 CellData<1> boundary_line(2);
9316 boundary_line.boundary_id = material_or_boundary_id;
9317 boundary_line.manifold_id = manifold_id;
9318 for (unsigned int i = 0; i < boundary_line.vertices.size(); ++i)
9319 {
9320 AssertIndexRange(index_vertices[i],
9321 local_vertex_indices.size());
9322 boundary_line.vertices[i] =
9323 local_vertex_indices[index_vertices[i]];
9324 }
9325 subcell_data.boundary_lines.emplace_back(
9326 std::move(boundary_line));
9327 }
9328 else
9329 {
9331 }
9332 };
9333
9334 const auto material_id_cell = cell->material_id();
9335 const auto manifold_id_cell = cell->manifold_id();
9336
9337 // create cells one by one
9338 if (dim == 2)
9339 {
9340 for (const auto &cell_vertices : vertex_ids_for_cells_2d)
9341 add_cell(dim, cell_vertices, material_id_cell, manifold_id_cell);
9342 }
9343 else if (dim == 3)
9344 {
9345 for (const auto &cell_vertices : vertex_ids_for_cells_3d)
9346 add_cell(dim, cell_vertices, material_id_cell, manifold_id_cell);
9347 }
9348 else
9350
9351 // Set up sub-cell data.
9352 for (const auto f : cell->face_indices())
9353 {
9354 const auto bid = cell->face(f)->boundary_id();
9355 const auto mid = cell->face(f)->manifold_id();
9356
9357 // process boundary-faces: set boundary and manifold ids
9358 if (dim == 2) // 2d boundary-faces
9359 {
9360 for (const auto &face_vertices :
9361 vertex_ids_for_boundary_faces_2d[f])
9362 add_cell(1, face_vertices, bid, mid);
9363 }
9364 else if (dim == 3)
9365 {
9366 for (const auto &face_vertices :
9367 vertex_ids_for_boundary_faces_3d[f])
9368 add_cell(2, face_vertices, bid, mid);
9369 }
9370 else
9372 }
9373
9374 // In 3D we need to treat boundary lines separately.
9375 if (dim == 3)
9376 {
9377 for (const auto l : cell->line_indices())
9378 {
9379 const auto bid = cell->line(l)->boundary_id();
9380 const auto mid = cell->line(l)->manifold_id();
9381
9382 for (const auto &line_vertices :
9383 vertex_ids_for_boundary_lines_3d[l])
9384 add_cell(1, line_vertices, bid, mid);
9385 }
9386 }
9387 }
9388
9389 out_tria.clear();
9390 out_tria.create_triangulation(vertices, cells, subcell_data);
9391
9392 for (const auto i : out_tria.get_manifold_ids())
9395 }
9396
9397
9398
9399 template <template <int, int> class MeshType, int dim, int spacedim>
9401 (concepts::is_triangulation_or_dof_handler<MeshType<dim, spacedim>>))
9402# ifndef _MSC_VER
9403 std::map<typename MeshType<dim - 1, spacedim>::cell_iterator,
9404 typename MeshType<dim, spacedim>::face_iterator>
9405# else
9406 typename ExtractBoundaryMesh<MeshType, dim, spacedim>::return_type
9407# endif
9408 extract_boundary_mesh(const MeshType<dim, spacedim> &volume_mesh,
9409 MeshType<dim - 1, spacedim> &surface_mesh,
9410 const std::set<types::boundary_id> &boundary_ids)
9411 {
9412 Assert((dynamic_cast<
9414 &volume_mesh.get_triangulation()) == nullptr),
9416
9417 // This function works using the following assumption:
9418 // Triangulation::create_triangulation(...) will create cells that
9419 // preserve the order of cells passed in using the CellData argument;
9420 // also, that it will not reorder the vertices.
9421
9422 // dimension of the boundary mesh
9423 constexpr unsigned int boundary_dim = dim - 1;
9424
9425 // temporary map for level==0
9426 // iterator to face is stored along with face number
9427 // (this is required by the algorithm to adjust the normals of the
9428 // cells of the boundary mesh)
9429 std::vector<
9430 std::pair<typename MeshType<dim, spacedim>::face_iterator, unsigned int>>
9431 temporary_mapping_level0;
9432
9433 // vector indicating whether a vertex of the volume mesh has
9434 // already been visited (necessary to avoid duplicate vertices in
9435 // boundary mesh)
9436 std::vector<bool> touched(volume_mesh.get_triangulation().n_vertices(),
9437 false);
9438
9439 // data structures required for creation of boundary mesh
9440 std::vector<CellData<boundary_dim>> cells;
9441 SubCellData subcell_data;
9442 std::vector<Point<spacedim>> vertices;
9443
9444 // volume vertex indices to surf ones
9445 std::map<unsigned int, unsigned int> map_vert_index;
9446
9447 // define swapping of vertices to get proper normal orientation of
9448 // boundary mesh; the entry (i,j) of swap_matrix stores the index of the
9449 // vertex of the boundary cell corresponding to the j-th vertex on the
9450 // i-th face of the underlying volume cell if e.g. face 3 of a volume cell
9451 // is considered and vertices 1 and 2 of the corresponding boundary cell
9452 // are swapped to get proper normal orientation, swap_matrix[3]=( 0, 2, 1,
9453 // 3 )
9454 //
9455 // Permutation depends on the volume cell's reference cell. Currently only
9456 // hypercube and simplex meshes are supported.
9458 (volume_mesh.get_triangulation().is_mixed_mesh() == false),
9460 "Boundary extraction for mixed meshes is currently not supported."));
9462 volume_mesh.begin(0)->reference_cell();
9463
9464 // Size the table to the maximum number of vertices that can occur on any
9465 // face of the cell (4 for hypercubes, 3 for simplices). Unused entries
9466 // are left at the identity.
9467 const unsigned int max_n_face_vertices =
9468 reference_cell.face_reference_cell(0).n_vertices();
9469
9470 Table<2, unsigned int> swap_matrix(reference_cell.n_faces(),
9471 max_n_face_vertices);
9472 for (unsigned int f = 0; f < reference_cell.n_faces(); ++f)
9473 for (unsigned int j = 0; j < max_n_face_vertices; ++j)
9474 swap_matrix[f][j] = j;
9475
9476 if (reference_cell.is_hyper_cube())
9477 {
9478 // Hard-coded permutations for hypercube cells
9479 if (dim == 3)
9480 {
9481 std::swap(swap_matrix[0][1], swap_matrix[0][2]);
9482 std::swap(swap_matrix[2][1], swap_matrix[2][2]);
9483 std::swap(swap_matrix[4][1], swap_matrix[4][2]);
9484 }
9485 else if (dim == 2)
9486 {
9487 std::swap(swap_matrix[1][0], swap_matrix[1][1]);
9488 std::swap(swap_matrix[2][0], swap_matrix[2][1]);
9489 }
9490 }
9491 else if (reference_cell == ReferenceCells::get_simplex<dim>())
9492 {
9493 // Hard-coded permutations for simplex cells.
9494 // For both triangles (3 faces) and tetrahedra (4 faces) we swap the
9495 // first two face vertices so that the resulting boundary cell's normal
9496 // points outward.
9497 for (unsigned int f = 0; f < reference_cell.n_faces(); ++f)
9498 std::swap(swap_matrix[f][0], swap_matrix[f][1]);
9499 }
9500 else
9501 {
9502 // Wedges, pyramids, and mixed meshes are not currently supported.
9504 }
9505
9506 // Create boundary mesh and mapping
9507 // from only level(0) cells of volume_mesh
9508 for (typename MeshType<dim, spacedim>::cell_iterator cell =
9509 volume_mesh.begin(0);
9510 cell != volume_mesh.end(0);
9511 ++cell)
9512 for (const unsigned int i : cell->reference_cell().face_indices())
9513 {
9514 const typename MeshType<dim, spacedim>::face_iterator face =
9515 cell->face(i);
9516
9517 if (face->at_boundary() &&
9518 (boundary_ids.empty() ||
9519 (boundary_ids.find(face->boundary_id()) != boundary_ids.end())))
9520 {
9521 CellData<boundary_dim> c_data(face->n_vertices());
9522
9523 for (const unsigned int j : face->vertex_indices())
9524 {
9525 const unsigned int v_index = face->vertex_index(j);
9526
9527 if (!touched[v_index])
9528 {
9529 vertices.push_back(face->vertex(j));
9530 map_vert_index[v_index] = vertices.size() - 1;
9531 touched[v_index] = true;
9532 }
9533
9534 c_data.vertices[swap_matrix[i][j]] = map_vert_index[v_index];
9535 }
9536 c_data.material_id =
9537 static_cast<types::material_id>(face->boundary_id());
9538 c_data.manifold_id = face->manifold_id();
9539
9540
9541 // in 3d, we need to make sure we copy the manifold
9542 // indicators from the edges of the volume mesh to the
9543 // edges of the surface mesh
9544 //
9545 // we set default boundary ids for boundary lines
9546 // and numbers::internal_face_boundary_id for internal lines
9547 if (dim == 3)
9548 for (unsigned int e = 0; e < face->n_lines(); ++e)
9549 {
9550 // see if we already saw this edge from a
9551 // neighboring face, either in this or the reverse
9552 // orientation. if so, skip it.
9553 {
9554 bool edge_found = false;
9555 for (auto &boundary_line : subcell_data.boundary_lines)
9556 if (((boundary_line.vertices[0] ==
9557 map_vert_index[face->line(e)->vertex_index(0)]) &&
9558 (boundary_line.vertices[1] ==
9559 map_vert_index[face->line(e)->vertex_index(
9560 1)])) ||
9561 ((boundary_line.vertices[0] ==
9562 map_vert_index[face->line(e)->vertex_index(1)]) &&
9563 (boundary_line.vertices[1] ==
9564 map_vert_index[face->line(e)->vertex_index(0)])))
9565 {
9566 boundary_line.boundary_id =
9568 edge_found = true;
9569 break;
9570 }
9571 if (edge_found == true)
9572 // try next edge of current face
9573 continue;
9574 }
9575
9576 CellData<1> edge;
9577 edge.vertices[0] =
9578 map_vert_index[face->line(e)->vertex_index(0)];
9579 edge.vertices[1] =
9580 map_vert_index[face->line(e)->vertex_index(1)];
9581 edge.boundary_id = 0;
9582 edge.manifold_id = face->line(e)->manifold_id();
9583
9584 subcell_data.boundary_lines.emplace_back(std::move(edge));
9585 }
9586
9587 cells.emplace_back(std::move(c_data));
9588 temporary_mapping_level0.push_back(std::make_pair(face, i));
9589 }
9590 }
9591
9592 // create level 0 surface triangulation
9593 Assert(cells.size() > 0, ExcMessage("No boundary faces selected"));
9594 const_cast<Triangulation<dim - 1, spacedim> &>(
9595 surface_mesh.get_triangulation())
9596 .create_triangulation(vertices, cells, subcell_data);
9597
9598 // in 2d: set default boundary ids for "boundary vertices"
9599 if (dim == 2)
9600 {
9601 for (const auto &cell : surface_mesh.active_cell_iterators())
9602 for (unsigned int vertex = 0; vertex < 2; ++vertex)
9603 if (cell->face(vertex)->at_boundary())
9604 cell->face(vertex)->set_boundary_id(0);
9605 }
9606
9607 // Make mapping for level 0
9608
9609 // temporary map between cells on the boundary and corresponding faces of
9610 // domain mesh (each face is characterized by an iterator to the face and
9611 // the face number within the underlying cell)
9612 std::vector<std::pair<
9613 const typename MeshType<dim - 1, spacedim>::cell_iterator,
9614 std::pair<typename MeshType<dim, spacedim>::face_iterator, unsigned int>>>
9615 temporary_map_boundary_cell_face;
9616 for (const auto &cell : surface_mesh.active_cell_iterators())
9617 temporary_map_boundary_cell_face.push_back(
9618 std::make_pair(cell, temporary_mapping_level0.at(cell->index())));
9619
9620
9621 // refine the boundary mesh according to the refinement of the underlying
9622 // volume mesh,
9623 // algorithm:
9624 // (1) check which cells on refinement level i need to be refined
9625 // (2) do refinement (yields cells on level i+1)
9626 // (3) repeat for the next level (i+1->i) until refinement is completed
9627
9628 // stores the index into temporary_map_boundary_cell_face at which
9629 // presently deepest refinement level of boundary mesh begins
9630 unsigned int index_cells_deepest_level = 0;
9631 do
9632 {
9633 bool changed = false;
9634
9635 // vector storing cells which have been marked for
9636 // refinement
9637 std::vector<unsigned int> cells_refined;
9638
9639 // loop over cells of presently deepest level of boundary
9640 // triangulation
9641 for (unsigned int cell_n = index_cells_deepest_level;
9642 cell_n < temporary_map_boundary_cell_face.size();
9643 cell_n++)
9644 {
9645 // mark boundary cell for refinement if underlying volume face has
9646 // children
9647 if (temporary_map_boundary_cell_face[cell_n]
9648 .second.first->has_children())
9649 {
9650 // algorithm only works for
9651 // isotropic refinement!
9652 Assert(temporary_map_boundary_cell_face[cell_n]
9653 .second.first->refinement_case() ==
9656 temporary_map_boundary_cell_face[cell_n]
9657 .first->set_refine_flag();
9658 cells_refined.push_back(cell_n);
9659 changed = true;
9660 }
9661 }
9662
9663 // if cells have been marked for refinement (i.e., presently deepest
9664 // level is not the deepest level of the volume mesh)
9665 if (changed)
9666 {
9667 // do actual refinement
9668 const_cast<Triangulation<dim - 1, spacedim> &>(
9669 surface_mesh.get_triangulation())
9671
9672 // add new level of cells to temporary_map_boundary_cell_face
9673 index_cells_deepest_level = temporary_map_boundary_cell_face.size();
9674 for (const auto &refined_cell_n : cells_refined)
9675 {
9676 const typename MeshType<dim - 1, spacedim>::cell_iterator
9677 refined_cell =
9678 temporary_map_boundary_cell_face[refined_cell_n].first;
9679 const typename MeshType<dim,
9680 spacedim>::face_iterator refined_face =
9681 temporary_map_boundary_cell_face[refined_cell_n].second.first;
9682 const unsigned int refined_face_number =
9683 temporary_map_boundary_cell_face[refined_cell_n]
9684 .second.second;
9685 for (unsigned int child_n = 0;
9686 child_n < refined_cell->n_children();
9687 ++child_n)
9688 // at this point, the swapping of vertices done earlier must
9689 // be taken into account to get the right association
9690 // between volume faces and boundary cells!
9691 temporary_map_boundary_cell_face.push_back(
9692 std::make_pair(refined_cell->child(
9693 swap_matrix[refined_face_number][child_n]),
9694 std::make_pair(refined_face->child(child_n),
9695 refined_face_number)));
9696 }
9697 }
9698 // we are at the deepest level of refinement of the volume mesh
9699 else
9700 break;
9701 }
9702 while (true);
9703
9704 // generate the final mapping from the temporary mapping
9705 std::map<typename MeshType<dim - 1, spacedim>::cell_iterator,
9706 typename MeshType<dim, spacedim>::face_iterator>
9707 surface_to_volume_mapping;
9708 for (unsigned int i = 0; i < temporary_map_boundary_cell_face.size(); ++i)
9709 surface_to_volume_mapping[temporary_map_boundary_cell_face[i].first] =
9710 temporary_map_boundary_cell_face[i].second.first;
9711
9712 // TODO: we attach flat manifolds here; one should attach submanifolds
9713 // here
9714 const auto attached_mids =
9715 surface_mesh.get_triangulation().get_manifold_ids();
9716 for (const auto i : volume_mesh.get_triangulation().get_manifold_ids())
9717 if (i != numbers::flat_manifold_id &&
9718 std::find(attached_mids.begin(), attached_mids.end(), i) ==
9719 attached_mids.end())
9720 const_cast<Triangulation<dim - 1, spacedim> &>(
9721 surface_mesh.get_triangulation())
9723
9724 return surface_to_volume_mapping;
9725 }
9726
9727
9728
9729 template <int dim, int spacedim>
9730 void
9733 const std::vector<unsigned int> &repetitions,
9734 const Point<dim> &p1,
9735 const Point<dim> &p2,
9736 const bool colorize)
9737 {
9738 AssertDimension(dim, spacedim);
9739
9740 std::vector<Point<spacedim>> vertices;
9741 std::vector<CellData<dim>> cells;
9742
9743 if (dim == 2)
9744 {
9745 // determine cell sizes
9746 const Point<dim> dx((p2[0] - p1[0]) / repetitions[0],
9747 (p2[1] - p1[1]) / repetitions[1]);
9748
9749 // create vertices
9750 for (unsigned int j = 0; j <= repetitions[1]; ++j)
9751 for (unsigned int i = 0; i <= repetitions[0]; ++i)
9752 vertices.push_back(
9753 Point<spacedim>(p1[0] + dx[0] * i, p1[1] + dx[1] * j));
9754
9755 // create cells
9756 for (unsigned int j = 0; j < repetitions[1]; ++j)
9757 for (unsigned int i = 0; i < repetitions[0]; ++i)
9758 {
9759 // create reference QUAD cell
9760 std::array<unsigned int, 4> quad{{
9761 (j + 0) * (repetitions[0] + 1) + i + 0, //
9762 (j + 0) * (repetitions[0] + 1) + i + 1, //
9763 (j + 1) * (repetitions[0] + 1) + i + 0, //
9764 (j + 1) * (repetitions[0] + 1) + i + 1 //
9765 }}; //
9766
9767 // TRI cell 0
9768 {
9769 CellData<dim> tri;
9770 tri.vertices = {quad[0], quad[1], quad[2]};
9771 cells.emplace_back(std::move(tri));
9772 }
9773
9774 // TRI cell 1
9775 {
9776 CellData<dim> tri;
9777 tri.vertices = {quad[3], quad[2], quad[1]};
9778 cells.emplace_back(std::move(tri));
9779 }
9780 }
9781 }
9782 else if (dim == 3)
9783 {
9784 // determine cell sizes
9785 const Point<dim> dx((p2[0] - p1[0]) / repetitions[0],
9786 (p2[1] - p1[1]) / repetitions[1],
9787 (p2[2] - p1[2]) / repetitions[2]);
9788
9789 // create vertices
9790 for (unsigned int k = 0; k <= repetitions[2]; ++k)
9791 for (unsigned int j = 0; j <= repetitions[1]; ++j)
9792 for (unsigned int i = 0; i <= repetitions[0]; ++i)
9793 vertices.push_back(Point<spacedim>(p1[0] + dx[0] * i,
9794 p1[1] + dx[1] * j,
9795 p1[2] + dx[2] * k));
9796
9797 // create cells
9798 for (unsigned int k = 0; k < repetitions[2]; ++k)
9799 for (unsigned int j = 0; j < repetitions[1]; ++j)
9800 for (unsigned int i = 0; i < repetitions[0]; ++i)
9801 {
9802 // create reference HEX cell
9803 std::array<unsigned int, 8> quad{
9804 {(k + 0) * (repetitions[0] + 1) * (repetitions[1] + 1) +
9805 (j + 0) * (repetitions[0] + 1) + i + 0,
9806 (k + 0) * (repetitions[0] + 1) * (repetitions[1] + 1) +
9807 (j + 0) * (repetitions[0] + 1) + i + 1,
9808 (k + 0) * (repetitions[0] + 1) * (repetitions[1] + 1) +
9809 (j + 1) * (repetitions[0] + 1) + i + 0,
9810 (k + 0) * (repetitions[0] + 1) * (repetitions[1] + 1) +
9811 (j + 1) * (repetitions[0] + 1) + i + 1,
9812 (k + 1) * (repetitions[0] + 1) * (repetitions[1] + 1) +
9813 (j + 0) * (repetitions[0] + 1) + i + 0,
9814 (k + 1) * (repetitions[0] + 1) * (repetitions[1] + 1) +
9815 (j + 0) * (repetitions[0] + 1) + i + 1,
9816 (k + 1) * (repetitions[0] + 1) * (repetitions[1] + 1) +
9817 (j + 1) * (repetitions[0] + 1) + i + 0,
9818 (k + 1) * (repetitions[0] + 1) * (repetitions[1] + 1) +
9819 (j + 1) * (repetitions[0] + 1) + i + 1}};
9820
9821 // TET cell 0
9822 {
9823 CellData<dim> cell;
9824 if (((i % 2) + (j % 2) + (k % 2)) % 2 == 0)
9825 cell.vertices = {{quad[0], quad[1], quad[2], quad[4]}};
9826 else
9827 cell.vertices = {{quad[0], quad[1], quad[3], quad[5]}};
9828
9829 cells.emplace_back(std::move(cell));
9830 }
9831
9832 // TET cell 1
9833 {
9834 CellData<dim> cell;
9835 if (((i % 2) + (j % 2) + (k % 2)) % 2 == 0)
9836 cell.vertices = {{quad[2], quad[1], quad[3], quad[7]}};
9837 else
9838 cell.vertices = {{quad[0], quad[3], quad[2], quad[6]}};
9839 cells.emplace_back(std::move(cell));
9840 }
9841
9842 // TET cell 2
9843 {
9844 CellData<dim> cell;
9845 if (((i % 2) + (j % 2) + (k % 2)) % 2 == 0)
9846 cell.vertices = {{quad[1], quad[4], quad[5], quad[7]}};
9847 else
9848 cell.vertices = {{quad[0], quad[4], quad[5], quad[6]}};
9849 cells.emplace_back(std::move(cell));
9850 }
9851
9852 // TET cell 3
9853 {
9854 CellData<dim> cell;
9855 if (((i % 2) + (j % 2) + (k % 2)) % 2 == 0)
9856 cell.vertices = {{quad[2], quad[4], quad[7], quad[6]}};
9857 else
9858 cell.vertices = {{quad[3], quad[5], quad[7], quad[6]}};
9859 cells.emplace_back(std::move(cell));
9860 }
9861
9862 // TET cell 4
9863 {
9864 CellData<dim> cell;
9865 if (((i % 2) + (j % 2) + (k % 2)) % 2 == 0)
9866 cell.vertices = {{quad[1], quad[2], quad[4], quad[7]}};
9867 else
9868 cell.vertices = {{quad[0], quad[3], quad[6], quad[5]}};
9869 cells.emplace_back(std::move(cell));
9870 }
9871 }
9872 }
9873 else
9874 {
9876 }
9877
9878 // actually create triangulation
9879 tria.create_triangulation(vertices, cells, SubCellData());
9880
9881 if (colorize)
9882 {
9883 // to colorize, run through all
9884 // faces of all cells and set
9885 // boundary indicator to the
9886 // correct value if it was 0.
9887
9888 // use a large epsilon to
9889 // compare numbers to avoid
9890 // roundoff problems.
9891 double epsilon = std::numeric_limits<double>::max();
9892 for (unsigned int i = 0; i < dim; ++i)
9893 epsilon = std::min(epsilon,
9894 0.01 * (std::abs(p2[i] - p1[i]) / repetitions[i]));
9895 Assert(epsilon > 0,
9896 ExcMessage(
9897 "The distance between corner points must be positive."));
9898
9899 // actual code is external since
9900 // 1-D is different from 2/3d.
9901 colorize_subdivided_hyper_rectangle(tria, p1, p2, epsilon);
9902 }
9903 }
9904
9905
9906
9907 template <int dim, int spacedim>
9908 void
9910 const unsigned int repetitions,
9911 const double left,
9912 const double right,
9913 const bool colorize)
9914 {
9915 if (dim == 2)
9916 {
9918 {{repetitions, repetitions}},
9919 {left, left},
9920 {right, right},
9921 colorize);
9922 }
9923 else if (dim == 3)
9924 {
9926 tria,
9927 {{repetitions, repetitions, repetitions}},
9928 {left, left, left},
9929 {right, right, right},
9930 colorize);
9931 }
9932 else
9933 {
9935 }
9936 }
9937
9938
9939
9940 template <>
9941 void
9943 const double inner_radius,
9944 const double outer_radius,
9945 const bool colorize)
9946 {
9947 AssertThrow(inner_radius > 0, ExcMessage("inner_radius must be positive."));
9948 AssertThrow(inner_radius < outer_radius,
9949 ExcMessage("outer_radius has to be bigger than inner_radius."));
9950
9951 const Point<3> center;
9952
9953 hyper_shell(tria, center, inner_radius, outer_radius, 6);
9954 for (const auto &cell : tria.active_cell_iterators())
9955 {
9956 for (auto f : GeometryInfo<3>::face_indices())
9957 if (cell->face(f)->at_boundary())
9958 for (const unsigned int v : cell->face(f)->vertex_indices())
9959 if (std::fabs(cell->face(f)->vertex(v).norm() - outer_radius) <
9960 1e-12 * outer_radius)
9961 {
9962 const double ax = std::fabs(cell->face(f)->vertex(v)[0]);
9963 const double ay = std::fabs(cell->face(f)->vertex(v)[1]);
9964 const double az = std::fabs(cell->face(f)->vertex(v)[2]);
9965 const double max_coord = std::max({ax, ay, az});
9966 cell->face(f)->vertex(v) *= outer_radius / max_coord;
9967 }
9968 }
9969
9970 // Set boundary and manifold IDs
9971 const double eps = 1e-6 * outer_radius;
9973
9974 for (const auto &cell : tria.active_cell_iterators())
9975 for (const unsigned int f : cell->face_indices())
9976 if (cell->face(f)->at_boundary())
9977 {
9978 bool vertex_on_sphere = true;
9979 for (const unsigned int v : cell->face(f)->vertex_indices())
9980 if (std::abs(cell->face(f)->vertex(v).norm() - inner_radius) >
9981 eps)
9982 {
9983 vertex_on_sphere = false;
9984 break;
9985 }
9986
9987 if (vertex_on_sphere)
9988 {
9989 cell->face(f)->set_all_manifold_ids(0);
9990 cell->face(f)->set_boundary_id(colorize ? 6 : 1);
9991 }
9992 else if (colorize)
9993 {
9994 const Point<3> fc = cell->face(f)->center();
9995 if (std::abs(fc[0] + outer_radius) < eps)
9996 cell->face(f)->set_boundary_id(0);
9997 else if (std::abs(fc[0] - outer_radius) < eps)
9998 cell->face(f)->set_boundary_id(1);
9999 else if (std::abs(fc[1] + outer_radius) < eps)
10000 cell->face(f)->set_boundary_id(2);
10001 else if (std::abs(fc[1] - outer_radius) < eps)
10002 cell->face(f)->set_boundary_id(3);
10003 else if (std::abs(fc[2] + outer_radius) < eps)
10004 cell->face(f)->set_boundary_id(4);
10005 else if (std::abs(fc[2] - outer_radius) < eps)
10006 cell->face(f)->set_boundary_id(5);
10007 }
10008 }
10010 }
10011
10012
10013
10014 template <>
10015 void
10017 const double,
10018 const double,
10019 const bool)
10020 {
10022 }
10023
10024
10025 template <>
10026 void
10028 const double,
10029 const double,
10030 const bool)
10031 {
10033 }
10034
10035
10036 template <>
10037 void
10039 Triangulation<3> &tria,
10040 const std::vector<unsigned int> &lengths_heights_widths,
10041 const double inner_radius,
10042 const double outer_radius,
10043 const bool use_transfinite_region,
10044 const bool colorize)
10045 {
10046 AssertThrow(inner_radius > 0, ExcMessage("inner_radius must be positive."));
10047 AssertThrow(inner_radius < outer_radius,
10048 ExcMessage("outer_radius has to be bigger than inner_radius."));
10049
10050 // Distances
10051 const double length_pre = lengths_heights_widths[0] * 2 * outer_radius;
10052 const double length_post = lengths_heights_widths[1] * 2 * outer_radius;
10053 const double height_below = lengths_heights_widths[2] * 2 * outer_radius;
10054 const double height_above = lengths_heights_widths[3] * 2 * outer_radius;
10055 const double width_front = lengths_heights_widths[4] * 2 * outer_radius;
10056 const double width_back = lengths_heights_widths[5] * 2 * outer_radius;
10057
10058 // Repetitions for resp. length, height and width
10059 const unsigned int length_pre_repetitions = lengths_heights_widths[0];
10060 const unsigned int length_post_repetitions = lengths_heights_widths[1];
10061 const unsigned int height_below_repetitions = lengths_heights_widths[2];
10062 const unsigned int height_above_repetitions = lengths_heights_widths[3];
10063 const unsigned int width_front_repetitions = lengths_heights_widths[4];
10064 const unsigned int width_back_repetitions = lengths_heights_widths[5];
10065
10066 Triangulation<3> bulk_tria;
10068 bulk_tria,
10069 {length_pre_repetitions + length_post_repetitions + 1,
10070 height_below_repetitions + height_above_repetitions + 1,
10071 width_front_repetitions + width_back_repetitions + 1},
10072 Point<3>(-double(length_pre + outer_radius),
10073 -double(height_below + outer_radius),
10074 -double(width_front + outer_radius)),
10075 Point<3>(double(length_post + outer_radius),
10076 double(height_above + outer_radius),
10077 double(width_back + outer_radius)));
10078
10079 Triangulation<3> tria_without_cylinder;
10080 std::set<Triangulation<3>::active_cell_iterator> cells_to_remove;
10081 for (const auto &cell : bulk_tria.active_cell_iterators())
10082 {
10083 const Point<3> center = cell->center();
10084 if (std::abs(center[0]) < outer_radius &&
10085 std::abs(center[1]) < outer_radius &&
10086 std::abs(center[2]) < outer_radius)
10087 cells_to_remove.insert(cell);
10088 }
10089
10090 Triangulation<3> tria_without_sphere;
10092 cells_to_remove,
10093 tria_without_sphere);
10094
10095 Triangulation<3> sphere_tria;
10096 const double shell_radius = 0.5 * (inner_radius + outer_radius);
10098 shell_radius,
10100 false);
10101
10102 Triangulation<3> shell_tria;
10104 shell_tria, Point<3>(), inner_radius, shell_radius, 1, 0.1, 6, false);
10105
10106 const double vertex_tol =
10107 std::min(internal::minimal_vertex_distance(shell_tria),
10108 internal::minimal_vertex_distance(sphere_tria)) *
10109 0.5;
10110
10111 Triangulation<3> temp;
10113 shell_tria, sphere_tria, temp, vertex_tol, true);
10114 sphere_tria = std::move(temp);
10115
10116 const double vertex_tolerance =
10117 std::min(internal::minimal_vertex_distance(tria_without_sphere),
10118 internal::minimal_vertex_distance(sphere_tria)) /
10119 10;
10120
10122 tria_without_sphere, sphere_tria, tria, vertex_tolerance, true);
10123
10124 tria.reset_all_manifolds();
10125 tria.set_all_manifold_ids(0);
10126
10127 for (const auto &cell : tria.cell_iterators())
10128 {
10129 for (const auto &face : cell->face_iterators())
10130 {
10131 bool face_spherical = true;
10132 for (const auto v : face->vertex_indices())
10133 {
10134 if (face->vertex(v).norm_square() > std::sqrt(inner_radius))
10135 face_spherical = false;
10136 }
10137 if (face_spherical)
10138 face->set_all_manifold_ids(1);
10139 }
10140 }
10141
10143
10144 if (use_transfinite_region)
10145 {
10146 TransfiniteInterpolationManifold<3> transfinite_manifold;
10147 transfinite_manifold.initialize(tria);
10148 tria.set_manifold(0, transfinite_manifold);
10149 }
10150 else
10151 tria.set_manifold(0, FlatManifold<3>());
10152
10153 // Set boundary IDs
10154 if (colorize)
10155 {
10156 const double tol = 1e-6;
10157 for (const auto &face : tria.active_face_iterators())
10158 if (face->at_boundary())
10159 {
10160 const Point<3> center = face->center();
10161 // Left
10162 if (std::abs(center[0] - (-static_cast<double>(
10163 length_pre + outer_radius))) < tol)
10164 face->set_boundary_id(0);
10165 // Right
10166 else if (std::abs(center[0] - static_cast<double>(length_post +
10167 outer_radius)) <
10168 tol)
10169 face->set_boundary_id(1);
10170 // Sphere boundary
10171 else if (face->manifold_id() == 1)
10172 face->set_boundary_id(2);
10173 // Bottom
10174 else if (std::abs(center[1] - (-static_cast<double>(
10175 height_below + outer_radius))) <
10176 tol)
10177 face->set_boundary_id(3);
10178 // Top
10179 else if (std::abs(center[1] - static_cast<double>(height_above +
10180 outer_radius)) <
10181 tol)
10182 face->set_boundary_id(4);
10183 // Front (z negative)
10184 else if (std::abs(center[2] - (-static_cast<double>(
10185 width_front + outer_radius))) <
10186 tol)
10187 face->set_boundary_id(5);
10188 // Back (z positive)
10189 else if (std::abs(center[2] - static_cast<double>(
10190 width_back + outer_radius)) < tol)
10191 face->set_boundary_id(6);
10192 }
10193 }
10194 }
10195
10196
10197
10198 template <>
10199 void
10201 const std::vector<unsigned int> &,
10202 const double,
10203 const double,
10204 const bool,
10205 const bool)
10206 {
10208 }
10209
10210
10211
10212 template <>
10213 void
10215 const std::vector<unsigned int> &,
10216 const double,
10217 const double,
10218 const bool,
10219 const bool)
10220 {
10222 }
10223} // namespace GridGenerator
10224
10225// explicit instantiations
10226# include "grid/grid_generator.inst"
10227
10228#endif // DOXYGEN
10229
*  iterator end()
*  const Number height
*  const Number radius
*  *  iterator begin()
*  *  const Number outer_radius
ArrayView< std::remove_reference_t< typename std::iterator_traits< Iterator >::reference >, MemorySpaceType > make_array_view(const Iterator begin, const Iterator end)
boost::container::small_vector< TriaIterator< TriaAccessor< dim - 1, dim, spacedim > >, ReferenceCells::max_n_faces< dim >() > face_iterators() const
unsigned int face_index(const unsigned int i) const
bool at_boundary(const unsigned int i) const
TriaIterator< TriaAccessor< dim - 1, dim, spacedim > > face(const unsigned int i) const
void set_material_id(const types::material_id new_material_id) const
unsigned int active_cell_index() const
types::material_id material_id() const
void make_mapping(const MeshType &source_grid, const MeshType &destination_grid)
void add_parameter(const std::string &entry, ParameterType &parameter, const std::string &documentation="", const Patterns::PatternBase &pattern= *Patterns::Tools::Convert< ParameterType >::to_pattern(), const bool has_to_be_set=false)
void enter_subsection(const std::string &subsection, const bool create_path_if_needed=true)
Definition point.h:111
static constexpr Point< dim, Number > unit_vector(const unsigned int i)
numbers::NumberTraits< Number >::real_type distance(const Point< dim, Number > &p) const
const Point< spacedim > & get_center() const
numbers::NumberTraits< Number >::real_type norm() const
constexpr numbers::NumberTraits< double >::real_type norm_square() const
void initialize(const Triangulation< dim, spacedim > &triangulation)
std_cxx20::ranges::iota_view< unsigned int, unsigned int > vertex_indices() const
unsigned int n_lines() const
unsigned int n_vertices() const
std_cxx20::ranges::iota_view< unsigned int, unsigned int > line_indices() const
void set_all_manifold_ids(const types::manifold_id manifold_ind) const
std_cxx20::ranges::iota_view< unsigned int, unsigned int > face_indices() const
types::manifold_id manifold_id() const
unsigned int vertex_index(const unsigned int i) const
Point< spacedim > center(const bool respect_manifold=false, const bool interpolate_from_surrounding=false) const
ReferenceCell< structdim > reference_cell() const
Point< spacedim > & vertex(const unsigned int i) const
typename::internal::TriangulationImplementation::Iterators< dim, spacedim >::line_iterator line(const unsigned int i) const
unsigned int n_faces() const
virtual void add_periodicity(const std::vector< GridTools::PeriodicFacePair< cell_iterator > > &)
virtual types::global_cell_index n_global_active_cells() const
unsigned int n_quads() const
virtual void clear()
virtual void copy_triangulation(const Triangulation< dim, spacedim > &other_tria)
unsigned int n_faces() const
bool all_reference_cells_are_hyper_cube() const
void save_user_flags_line(std::ostream &out) const
face_iterator end_face() const
cell_iterator begin(const unsigned int level=0) const
unsigned int n_lines() const
std::vector< Point< spacedim > > vertices
Definition tria.h:4608
virtual void create_triangulation(const std::vector< Point< spacedim > > &vertices, const std::vector< CellData< dim > > &cells, const SubCellData &subcelldata)
unsigned int n_active_cells() const
void refine_global(const unsigned int times=1)
const std::vector< Point< spacedim > > & get_vertices() const
unsigned int n_active_lines() const
unsigned int n_levels() const
cell_iterator end() const
virtual bool has_hanging_nodes() const
vertex_iterator begin_vertex() const
vertex_iterator end_vertex() const
virtual void execute_coarsening_and_refinement()
virtual unsigned int n_global_levels() const
cell_iterator last() const
face_iterator begin_face() const
unsigned int n_cells() const
void save_user_flags_quad(std::ostream &out) const
unsigned int n_vertices() const
bool all_reference_cells_are_simplex() const
active_cell_iterator begin_active(const unsigned int level=0) const
constexpr size_type size() const noexcept
#define DEAL_II_NAMESPACE_OPEN
Definition config.h:38
constexpr bool running_in_debug_mode()
Definition config.h:76
#define DEAL_II_CXX20_REQUIRES(condition)
Definition config.h:246
#define DEAL_II_NAMESPACE_CLOSE
Definition config.h:39
DerivativeForm< 1, spacedim, dim, Number > transpose(const DerivativeForm< 1, dim, spacedim, Number > &DF)
#define DEAL_II_ASSERT_UNREACHABLE()
#define DEAL_II_NOT_IMPLEMENTED()
IteratorRange< active_face_iterator > active_face_iterators() const
IteratorRange< active_cell_iterator > active_cell_iterators() const
IteratorRange< cell_iterator > cell_iterators() const
static ::ExceptionBase & ExcNotImplemented()
#define Assert(cond, exc)
#define AssertDimension(dim1, dim2)
static ::ExceptionBase & ExcLowerRange(int arg1, int arg2)
#define AssertIndexRange(index, range)
static ::ExceptionBase & ExcInternalError()
static ::ExceptionBase & ExcIndexRange(std::size_t arg1, std::size_t arg2, std::size_t arg3)
static ::ExceptionBase & ExcMessage(std::string arg1)
#define AssertThrow(cond, exc)
TriaActiveIterator< CellAccessor< dim, spacedim > > active_cell_iterator
Definition tria.h:1648
TriaIterator< TriaAccessor< dim - 1, dim, spacedim > > face_iterator
Definition tria.h:1663
TriaIterator< CellAccessor< dim, spacedim > > cell_iterator
Definition tria.h:1624
void set_all_manifold_ids_on_boundary(const types::manifold_id number)
const Manifold< dim, spacedim > & get_manifold(const types::manifold_id number) const
void copy_boundary_to_manifold_id(Triangulation< dim, spacedim > &tria, const bool reset_boundary_ids=false)
virtual std::vector< types::manifold_id > get_manifold_ids() const
void set_manifold(const types::manifold_id number, const Manifold< dim, spacedim > &manifold_object)
void reset_all_manifolds()
void set_all_manifold_ids(const types::manifold_id number)
void set_manifold_id(const types::manifold_id) const
void consistently_order_cells(std::vector< CellData< dim > > &cells)
const Mapping< dim, spacedim > & get_default_linear_mapping(const Triangulation< dim, spacedim > &triangulation)
Definition mapping.cc:314
CGAL::Exact_predicates_exact_constructions_kernel_with_sqrt K
void interpolate(const DoFHandler< dim, spacedim > &dof1, const InVector &u1, const DoFHandler< dim, spacedim > &dof2, OutVector &u2)
void create_triangulation(Triangulation< dim, dim > &tria, const AdditionalData &additional_data=AdditionalData())
void parallelepiped(Triangulation< dim > &tria, const Point< dim >(&corners)[dim], const bool colorize=false)
void hyper_cross(Triangulation< dim, spacedim > &tria, const std::vector< unsigned int > &sizes, const bool colorize_cells=false)
A center cell with stacks of cell protruding from each surface.
void hyper_cube_with_cylindrical_hole(Triangulation< dim, spacedim > &triangulation, const double inner_radius=.25, const double outer_radius=.5, const double L=.5, const unsigned int repetitions=1, const bool colorize=false)
void hyper_ball_balanced(Triangulation< dim > &tria, const Point< dim > &center=Point< dim >(), const double radius=1.)
void plate_with_a_hole(Triangulation< dim > &tria, const double inner_radius=0.4, const double outer_radius=1., const double pad_bottom=2., const double pad_top=2., const double pad_left=1., const double pad_right=1., const Point< dim > &center=Point< dim >(), const types::manifold_id polar_manifold_id=0, const types::manifold_id tfi_manifold_id=1, const double L=1., const unsigned int n_slices=2, const bool colorize=false)
Rectangular plate with an (offset) cylindrical hole.
void enclosed_hyper_cube(Triangulation< dim > &tria, const double left=0., const double right=1., const double thickness=1., const bool colorize=false)
void uniform_channel_with_sphere(Triangulation< dim > &tria, const std::vector< unsigned int > &lengths_heights_widths, const double inner_radius, const double outer_radius, const bool use_transfinite_region=false, const bool colorize=false)
void replicate_triangulation(const Triangulation< dim, spacedim > &input, const std::vector< unsigned int > &extents, Triangulation< dim, spacedim > &result)
Replicate a given triangulation in multiple coordinate axes.
void parallelogram(Triangulation< dim > &tria, const Point< dim >(&corners)[dim], const bool colorize=false)
void general_cell(Triangulation< dim, spacedim > &tria, const std::vector< Point< spacedim > > &vertices, const bool colorize=false)
void subdivided_hyper_cube(Triangulation< dim, spacedim > &tria, const unsigned int repetitions, const double left=0., const double right=1., const bool colorize=false)
void hyper_shell(Triangulation< dim, spacedim > &tria, const Point< spacedim > &center, const double inner_radius, const double outer_radius, const unsigned int n_cells=0, bool colorize=false)
void hyper_L(Triangulation< dim > &tria, const double left=-1., const double right=1., const bool colorize=false)
void subdivided_hyper_cube_with_simplices(Triangulation< dim, spacedim > &tria, const unsigned int repetitions, const double left=0.0, const double right=1.0, const bool colorize=false)
void hyper_cube_slit(Triangulation< dim > &tria, const double left=0., const double right=1., const bool colorize=false)
void hyper_cube_with_spherical_hole(Triangulation< dim > &tria, const double inner_radius=0.25, const double outer_radius=0.5, const bool colorize=false)
void eccentric_hyper_shell(Triangulation< dim > &triangulation, const Point< dim > &inner_center, const Point< dim > &outer_center, const double inner_radius, const double outer_radius, const unsigned int n_cells)
void hyper_rectangle(Triangulation< dim, spacedim > &tria, const Point< dim > &p1, const Point< dim > &p2, const bool colorize=false)
void uniform_channel_with_cylinder(Triangulation< dim > &tria, const std::vector< unsigned int > &lengths_and_heights, const double depth=1, const unsigned int depth_division=1, const double shell_region_radius=0.75, const unsigned int n_shells=2, const double skewness=2.0, const bool use_transfinite_region=false, const bool colorize=false)
void cylinder(Triangulation< dim > &tria, const double radius=1., const double half_length=1.)
void moebius(Triangulation< 3, 3 > &tria, const unsigned int n_cells, const unsigned int n_rotations, const double R, const double r)
void extrude_triangulation(const Triangulation< 2, 2 > &input, const unsigned int n_slices, const double height, Triangulation< 3, 3 > &result, const bool copy_manifold_ids=false, const std::vector< types::manifold_id > &manifold_priorities={})
void half_hyper_shell(Triangulation< dim > &tria, const Point< dim > &center, const double inner_radius, const double outer_radius, const unsigned int n_cells=0, const bool colorize=false)
void quarter_hyper_ball(Triangulation< dim > &tria, const Point< dim > &center=Point< dim >(), const double radius=1.)
void cheese(Triangulation< dim, spacedim > &tria, const std::vector< unsigned int > &holes)
Rectangular domain with rectangular pattern of holes.
void create_union_triangulation(const Triangulation< dim, spacedim > &triangulation_1, const Triangulation< dim, spacedim > &triangulation_2, Triangulation< dim, spacedim > &result)
void hyper_ball(Triangulation< dim, spacedim > &tria, const Point< spacedim > &center={}, const double radius=1., const bool attach_spherical_manifold_on_boundary_cells=false)
void subdivided_hyper_rectangle_with_simplices(Triangulation< dim, spacedim > &tria, const std::vector< unsigned int > &repetitions, const Point< dim > &p1, const Point< dim > &p2, const bool colorize=false)
void non_standard_orientation_mesh(Triangulation< 2 > &tria, const unsigned int n_rotate_middle_square)
return_type extract_boundary_mesh(const MeshType< dim, spacedim > &volume_mesh, MeshType< dim - 1, spacedim > &surface_mesh, const std::set< types::boundary_id > &boundary_ids=std::set< types::boundary_id >())
void alfeld_split_of_simplex_mesh(const Triangulation< dim, spacedim > &in_tria, Triangulation< dim, spacedim > &out_tria)
void subdivided_parallelepiped(Triangulation< dim > &tria, const unsigned int n_subdivisions, const Point< dim >(&corners)[dim], const bool colorize=false)
void subdivided_cylinder(Triangulation< dim > &tria, const unsigned int x_subdivisions, const double radius=1., const double half_length=1.)
void convert_hypercube_to_simplex_mesh(const Triangulation< dim, spacedim > &in_tria, Triangulation< dim, spacedim > &out_tria, const unsigned int n_divisions=(dim==2 ? 8u :24u))
void channel_with_cylinder(Triangulation< dim > &tria, const double shell_region_width=0.03, const unsigned int n_shells=2, const double skewness=2.0, const bool colorize=false)
void subdivided_hyper_L(Triangulation< dim, spacedim > &tria, const std::vector< unsigned int > &repetitions, const Point< dim > &bottom_left, const Point< dim > &top_right, const std::vector< int > &n_cells_to_remove)
void convert_simplex_to_hypercube_mesh(const Triangulation< dim, spacedim > &in_tria, Triangulation< dim, spacedim > &out_tria)
void hyper_sphere(Triangulation< spacedim - 1, spacedim > &tria, const Point< spacedim > &center=Point< spacedim >(), const double radius=1.)
void concentric_hyper_shells(Triangulation< dim > &triangulation, const Point< dim > &center, const double inner_radius=0.125, const double outer_radius=0.25, const unsigned int n_shells=1, const double skewness=0.1, const unsigned int n_cells_per_shell=0, const bool colorize=false)
void reference_cell(Triangulation< dim, spacedim > &tria, const ReferenceCell< dim > &reference_cell)
void subdivided_hyper_rectangle(Triangulation< dim, spacedim > &tria, const std::vector< unsigned int > &repetitions, const Point< dim > &p1, const Point< dim > &p2, const bool colorize=false)
void quarter_hyper_shell(Triangulation< dim > &tria, const Point< dim > &center, const double inner_radius, const double outer_radius, const unsigned int n_cells=0, const bool colorize=false)
void hyper_cube(Triangulation< dim, spacedim > &tria, const double left=0., const double right=1., const bool colorize=false)
void create_triangulation_with_removed_cells(const Triangulation< dim, spacedim > &input_triangulation, const std::set< typename Triangulation< dim, spacedim >::active_cell_iterator > &cells_to_remove, Triangulation< dim, spacedim > &result)
void simplex(Triangulation< dim, dim > &tria, const std::vector< Point< dim > > &vertices)
void truncated_cone(Triangulation< dim > &tria, const double radius_0=1.0, const double radius_1=0.5, const double half_length=1.0)
void merge_triangulations(const Triangulation< dim, spacedim > &triangulation_1, const Triangulation< dim, spacedim > &triangulation_2, Triangulation< dim, spacedim > &result, const double duplicated_vertex_tolerance=1.0e-12, const bool copy_manifold_ids=false, const bool copy_boundary_ids=false)
void half_hyper_ball(Triangulation< dim > &tria, const Point< dim > &center=Point< dim >(), const double radius=1.)
void cylinder_shell(Triangulation< dim > &tria, const double length, const double inner_radius, const double outer_radius, const unsigned int n_radial_cells=0, const unsigned int n_axial_cells=0, const bool colorize=false)
void flatten_triangulation(const Triangulation< dim, spacedim1 > &in_tria, Triangulation< dim, spacedim2 > &out_tria)
void delete_unused_vertices(std::vector< Point< spacedim > > &vertices, std::vector< CellData< dim > > &cells, SubCellData &subcelldata)
void scale(const double scaling_factor, Triangulation< dim, spacedim > &triangulation)
void rotate(const double angle, Triangulation< dim, spacedim > &triangulation)
void collect_periodic_faces(const MeshType &mesh, const types::boundary_id b_id1, const types::boundary_id b_id2, const unsigned int direction, std::vector< PeriodicFacePair< typename MeshType::cell_iterator > > &matched_pairs, const Tensor< 1, MeshType::space_dimension > &offset=::Tensor< 1, MeshType::space_dimension >(), const FullMatrix< double > &matrix=FullMatrix< double >(), const double abs_tol=1e-10)
void transform(const Transformation &transformation, Triangulation< dim, spacedim > &triangulation)
void shift(const Tensor< 1, spacedim > &shift_vector, Triangulation< dim, spacedim > &triangulation)
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)
bool have_same_coarse_mesh(const Triangulation< dim, spacedim > &mesh_1, const Triangulation< dim, spacedim > &mesh_2)
double volume(const Triangulation< dim, spacedim > &tria)
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)
constexpr char L
constexpr char N
constexpr char U
constexpr char A
double norm(const FEValuesBase< dim > &fe, const ArrayView< const std::vector< Tensor< 1, dim > > > &Du)
Definition divergence.h:469
Point< spacedim > point(const gp_Pnt &p, const double tolerance=1e-10)
Definition utilities.cc:210
SymmetricTensor< 2, dim, Number > C(const Tensor< 2, dim, Number > &F)
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)
SymmetricTensor< 2, dim, Number > b(const Tensor< 2, dim, Number > &F)
SymmetricTensor< 2, dim, Number > d(const Tensor< 2, dim, Number > &F, const Tensor< 2, dim, Number > &dF_dt)
SymmetricTensor< 2, dim, Number > epsilon(const Tensor< 2, dim, Number > &Grad_u)
Tensor< 2, dim, Number > F(const Tensor< 2, dim, Number > &Grad_u)
Tensor< 2, 2, Number > rotation_matrix_2d(const Number &angle)
constexpr const ReferenceCell< dim > & get_simplex()
constexpr const ReferenceCell< dim > & get_hypercube()
constexpr T fixed_power(const T t)
Definition utilities.h:942
std::string int_to_string(const unsigned int value, const unsigned int digits=numbers::invalid_unsigned_int)
Definition utilities.cc:464
long double gamma(const unsigned int n)
unsigned int n_cells(const internal::TriangulationImplementation::NumberCache< 1 > &c)
Definition tria.cc:15817
void copy(const T *begin, const T *end, U *dest)
constexpr double PI_2
Definition numbers.h:245
constexpr double E
Definition numbers.h:215
constexpr double PI
Definition numbers.h:240
constexpr unsigned int invalid_unsigned_int
Definition types.h:236
constexpr types::boundary_id internal_face_boundary_id
Definition types.h:327
constexpr types::boundary_id invalid_boundary_id
Definition types.h:307
constexpr types::manifold_id flat_manifold_id
Definition types.h:340
constexpr types::material_id invalid_material_id
Definition types.h:292
::VectorizedArray< Number, width > tan(const ::VectorizedArray< Number, width > &)
inline ::VectorizedArray< Number, width > tanh(const ::VectorizedArray< Number, width > &x)
::VectorizedArray< Number, width > min(const ::VectorizedArray< Number, width > &, const ::VectorizedArray< Number, width > &)
::VectorizedArray< Number, width > max(const ::VectorizedArray< Number, width > &, const ::VectorizedArray< Number, width > &)
::VectorizedArray< Number, width > cos(const ::VectorizedArray< Number, width > &)
::VectorizedArray< Number, width > sin(const ::VectorizedArray< Number, width > &)
::VectorizedArray< Number, width > sqrt(const ::VectorizedArray< Number, width > &)
::VectorizedArray< Number, width > pow(const ::VectorizedArray< Number, width > &, const Number p)
inline ::VectorizedArray< Number, width > atan(const ::VectorizedArray< Number, width > &x)
::VectorizedArray< Number, width > abs(const ::VectorizedArray< Number, width > &)
unsigned int material_id
Definition types.h:182
unsigned int boundary_id
Definition types.h:159
unsigned int manifold_id
Definition types.h:171
typename internal::ndarray::HelperArray< T, Ns... >::type ndarray
Definition ndarray.h:105
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 constexpr unsigned int vertices_per_cell
static constexpr std::array< int, faces_per_cell > unit_normal_orientation
static constexpr ndarray< unsigned int, vertices_per_cell, dim > vertex_to_face
static constexpr unsigned int faces_per_cell
static std_cxx20::ranges::iota_view< unsigned int, unsigned int > face_indices()
static constexpr std::array< unsigned int, faces_per_cell > unit_normal_direction
static constexpr std::array< unsigned int, faces_per_cell > opposite_face
static std_cxx20::ranges::iota_view< unsigned int, unsigned int > vertex_indices()
std::vector< CellData< 2 > > boundary_quads
Definition cell_data.h:247
std::vector< CellData< 1 > > boundary_lines
Definition cell_data.h:231
constexpr Number determinant(const SymmetricTensor< 2, dim, Number > &)
constexpr Tensor< 1, dim, typename ProductType< Number1, Number2 >::type > cross_product_3d(const Tensor< 1, dim, Number1 > &src1, const Tensor< 1, dim, Number2 > &src2)
Definition tensor.h:2664