deal.II version GIT relicensing-6917-gb466a1809f 2026-10-16 06:30:01+00:00
\(\newcommand{\dealvcentcolon}{\mathrel{\mathop{:}}}\) \(\newcommand{\dealcoloneq}{\dealvcentcolon\mathrel{\mkern-1.2mu}=}\) \(\newcommand{\jump}[1]{\left[\!\left[ #1 \right]\!\right]}\) \(\newcommand{\average}[1]{\left\{\!\left\{ #1 \right\}\!\right\}}\)
Loading...
Searching...
No Matches
grid_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)
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
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) -
671 0.1036 * Utilities::fixed_power<4>(
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) ?
688 m / Utilities::fixed_power<2>(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) -
702 0.1036 * Utilities::fixed_power<4>(
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 :
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
3587 cell->set_all_manifold_ids(numbers::flat_manifold_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)
3859 cell->set_all_manifold_ids(numbers::flat_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)
4115 cell->set_all_manifold_ids(numbers::flat_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 {
4124 inner_manifold.initialize(tria);
4125 tria.set_manifold(tfi_manifold_id, inner_manifold);
4126 }
4127 else
4128 {
4129 tria.set_manifold(tfi_manifold_id, FlatManifold<2>());
4130 }
4131
4132 if (colorize)
4133 for (const auto &face : tria.active_face_iterators())
4134 if (face->at_boundary())
4135 {
4136 const Point<2> center = face->center();
4137 // left side
4138 if (std::abs(center[0] - (-static_cast<double>(length_pre))) <
4139 1e-10)
4140 face->set_boundary_id(0);
4141 // right side
4142 else if (std::abs(center[0] - static_cast<double>(length_post)) <
4143 1e-10)
4144 face->set_boundary_id(1);
4145 // cylinder boundary
4146 else if (face->manifold_id() == polar_manifold_id)
4147 face->set_boundary_id(2);
4148 // bottom side
4149 else if (std::abs(center[1] -
4150 (-static_cast<double>(height_below))) < 1e-10)
4151 face->set_boundary_id(3);
4152 // top side
4153 else
4154 face->set_boundary_id(4);
4155 }
4156 }
4157
4158 template <>
4159 void
4161 Triangulation<3> &tria,
4162 const std::vector<unsigned int> &lengths_and_heights,
4163 const double depth,
4164 unsigned int depth_division,
4165 const double shell_region_radius,
4166 const unsigned int n_shells,
4167 const double skewness,
4168 const bool use_transfinite_region,
4169 const bool colorize)
4170 {
4171 Triangulation<2> tria_2;
4173 lengths_and_heights,
4174 depth,
4175 depth_division,
4176 shell_region_radius,
4177 n_shells,
4178 skewness,
4179 use_transfinite_region,
4180 colorize);
4181
4182 // extrude to 3d
4183 extrude_triangulation(tria_2, depth_division, depth, tria, true);
4184
4185 // set up the new 3d manifolds
4186 const types::manifold_id cylindrical_manifold_id = 0;
4187 const types::manifold_id tfi_manifold_id = 1;
4188 const PolarManifold<2> *const m_ptr =
4189 dynamic_cast<const PolarManifold<2> *>(
4190 &tria_2.get_manifold(cylindrical_manifold_id));
4191 Assert(m_ptr != nullptr, ExcInternalError());
4192 const Point<3> axial_point(m_ptr->get_center()[0],
4193 m_ptr->get_center()[1],
4194 0.0);
4195 const Tensor<1, 3> direction{{0.0, 0.0, 1.0}};
4196
4197 const CylindricalManifold<3> cylindrical_manifold(direction, axial_point);
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 else
4207 {
4208 tria.set_manifold(tfi_manifold_id, FlatManifold<3>());
4209 }
4210
4211 // From extrude_triangulation: since the maximum boundary id of tria_2 was
4212 // 4, the front boundary id is 4 and the back is 5. They remain unchanged.
4213 }
4214
4215 template <int dim, int spacedim>
4216 void
4218 const std::vector<unsigned int> &sizes,
4219 const bool colorize)
4220 {
4222 Assert(dim > 1, ExcNotImplemented());
4223 Assert(dim < 4, ExcNotImplemented());
4224
4225 // If there is a desire at some point to change the geometry of
4226 // the cells, this tensor can be made an argument to the function.
4227 Tensor<1, dim> dimensions;
4228 for (unsigned int d = 0; d < dim; ++d)
4229 dimensions[d] = 1.;
4230
4231 std::vector<Point<spacedim>> points;
4232 unsigned int n_cells = 1;
4233 for (const unsigned int i : GeometryInfo<dim>::face_indices())
4234 n_cells += sizes[i];
4235
4236 std::vector<CellData<dim>> cells(n_cells);
4237 // Vertices of the center cell
4238 for (const unsigned int i : GeometryInfo<dim>::vertex_indices())
4239 {
4241 for (unsigned int d = 0; d < dim; ++d)
4242 p[d] = 0.5 * dimensions[d] *
4245 points.push_back(p);
4246 cells[0].vertices[i] = i;
4247 }
4248 cells[0].material_id = 0;
4249
4250 // The index of the first cell of the leg.
4251 unsigned int cell_index = 1;
4252 // The legs of the cross
4253 for (const unsigned int face : GeometryInfo<dim>::face_indices())
4254 {
4255 const unsigned int oface = GeometryInfo<dim>::opposite_face[face];
4256 const unsigned int dir = GeometryInfo<dim>::unit_normal_direction[face];
4257
4258 // We are moving in the direction of face
4259 for (unsigned int j = 0; j < sizes[face]; ++j, ++cell_index)
4260 {
4261 const unsigned int last_cell = (j == 0) ? 0U : (cell_index - 1);
4262
4263 for (unsigned int v = 0; v < GeometryInfo<dim>::vertices_per_face;
4264 ++v)
4265 {
4266 const unsigned int cellv =
4268 const unsigned int ocellv =
4270 // First the vertices which already exist
4271 cells[cell_index].vertices[ocellv] =
4272 cells[last_cell].vertices[cellv];
4273
4274 // Now the new vertices
4275 cells[cell_index].vertices[cellv] = points.size();
4276
4277 Point<spacedim> p = points[cells[cell_index].vertices[ocellv]];
4279 dimensions[dir];
4280 points.push_back(p);
4281 }
4282 cells[cell_index].material_id = (colorize) ? (face + 1U) : 0U;
4283 }
4284 }
4285 tria.create_triangulation(points, cells, SubCellData());
4286 }
4287
4288
4289 template <>
4290 void
4291 hyper_cube_slit(Triangulation<1> &, const double, const double, const bool)
4292 {
4294 }
4295
4296
4297
4298 template <>
4299 void
4301 const double,
4302 const double,
4303 const double,
4304 const bool)
4305 {
4307 }
4308
4309
4310
4311 template <>
4312 void
4313 hyper_L(Triangulation<1> &, const double, const double, const bool)
4314 {
4316 }
4317
4318
4319
4320 template <>
4321 void
4322 hyper_ball_balanced(Triangulation<1> &, const Point<1> &, const double)
4323 {
4325 }
4326
4327
4328
4329 template <>
4330 void
4331 cylinder(Triangulation<1> &, const double, const double)
4332 {
4334 }
4335
4336
4337 template <>
4338 void
4340 const unsigned int,
4341 const double,
4342 const double)
4343 {
4345 }
4346
4347
4348
4349 template <>
4350 void
4351 truncated_cone(Triangulation<1> &, const double, const double, const double)
4352 {
4354 }
4355
4356
4357
4358 template <>
4359 void
4361 const Point<1> &,
4362 const double,
4363 const double,
4364 const unsigned int,
4365 const bool)
4366 {
4368 }
4369
4370 template <>
4371 void
4373 const double,
4374 const double,
4375 const double,
4376 const unsigned int,
4377 const unsigned int,
4378 const bool)
4379 {
4381 }
4382
4383
4384 template <>
4385 void
4386 quarter_hyper_ball(Triangulation<1> &, const Point<1> &, const double)
4387 {
4389 }
4390
4391
4392 template <>
4393 void
4394 half_hyper_ball(Triangulation<1> &, const Point<1> &, const double)
4395 {
4397 }
4398
4399
4400 template <>
4401 void
4403 const Point<1> &,
4404 const double,
4405 const double,
4406 const unsigned int,
4407 const bool)
4408 {
4410 }
4411
4412 template <>
4413 void
4415 const Point<1> &,
4416 const double,
4417 const double,
4418 const unsigned int,
4419 const bool)
4420 {
4422 }
4423
4424 template <>
4425 void
4427 const double left,
4428 const double right,
4429 const double thickness,
4430 const bool colorize)
4431 {
4432 Assert(left < right,
4433 ExcMessage("Invalid left-to-right bounds of enclosed hypercube"));
4434
4435 std::vector<Point<2>> vertices(16);
4436 double coords[4];
4437 coords[0] = left - thickness;
4438 coords[1] = left;
4439 coords[2] = right;
4440 coords[3] = right + thickness;
4441
4442 unsigned int k = 0;
4443 for (const double y : coords)
4444 for (const double x : coords)
4445 vertices[k++] = Point<2>(x, y);
4446
4447 const types::material_id materials[9] = {5, 4, 6, 1, 0, 2, 9, 8, 10};
4448
4449 std::vector<CellData<2>> cells(9);
4450 k = 0;
4451 for (unsigned int i0 = 0; i0 < 3; ++i0)
4452 for (unsigned int i1 = 0; i1 < 3; ++i1)
4453 {
4454 cells[k].vertices[0] = i1 + 4 * i0;
4455 cells[k].vertices[1] = i1 + 4 * i0 + 1;
4456 cells[k].vertices[2] = i1 + 4 * i0 + 4;
4457 cells[k].vertices[3] = i1 + 4 * i0 + 5;
4458 if (colorize)
4459 cells[k].material_id = materials[k];
4460 ++k;
4461 }
4462 tria.create_triangulation(vertices,
4463 cells,
4464 SubCellData()); // no boundary information
4465 }
4466
4467
4468
4469 // Implementation for 2d only
4470 template <>
4471 void
4473 const double left,
4474 const double right,
4475 const bool colorize)
4476 {
4477 const double rl2 = (right + left) / 2;
4478 const Point<2> vertices[10] = {Point<2>(left, left),
4479 Point<2>(rl2, left),
4480 Point<2>(rl2, rl2),
4481 Point<2>(left, rl2),
4482 Point<2>(right, left),
4483 Point<2>(right, rl2),
4484 Point<2>(rl2, right),
4485 Point<2>(left, right),
4486 Point<2>(right, right),
4487 Point<2>(rl2, left)};
4488 const int cell_vertices[4][4] = {{0, 1, 3, 2},
4489 {9, 4, 2, 5},
4490 {3, 2, 7, 6},
4491 {2, 5, 6, 8}};
4492 std::vector<CellData<2>> cells(4, CellData<2>());
4493 for (unsigned int i = 0; i < 4; ++i)
4494 {
4495 for (unsigned int j = 0; j < 4; ++j)
4496 cells[i].vertices[j] = cell_vertices[i][j];
4497 cells[i].material_id = 0;
4498 }
4499 tria.create_triangulation(std::vector<Point<2>>(std::begin(vertices),
4500 std::end(vertices)),
4501 cells,
4502 SubCellData()); // no boundary information
4503
4504 if (colorize)
4505 {
4507 cell->face(1)->set_boundary_id(1);
4508 ++cell;
4509 cell->face(0)->set_boundary_id(2);
4510 }
4511 }
4512
4513
4514
4515 template <>
4516 void
4517 truncated_cone(Triangulation<2> &triangulation,
4518 const double radius_0,
4519 const double radius_1,
4520 const double half_length)
4521 {
4522 Point<2> vertices_tmp[4];
4523
4524 vertices_tmp[0] = Point<2>(-half_length, -radius_0);
4525 vertices_tmp[1] = Point<2>(half_length, -radius_1);
4526 vertices_tmp[2] = Point<2>(-half_length, radius_0);
4527 vertices_tmp[3] = Point<2>(half_length, radius_1);
4528
4529 const std::vector<Point<2>> vertices(std::begin(vertices_tmp),
4530 std::end(vertices_tmp));
4531 unsigned int cell_vertices[1][GeometryInfo<2>::vertices_per_cell];
4532
4533 for (const unsigned int i : GeometryInfo<2>::vertex_indices())
4534 cell_vertices[0][i] = i;
4535
4536 std::vector<CellData<2>> cells(1, CellData<2>());
4537
4538 for (const unsigned int i : GeometryInfo<2>::vertex_indices())
4539 cells[0].vertices[i] = cell_vertices[0][i];
4540
4541 cells[0].material_id = 0;
4542 triangulation.create_triangulation(vertices, cells, SubCellData());
4543
4544 Triangulation<2>::cell_iterator cell = triangulation.begin();
4545
4546 cell->face(0)->set_boundary_id(1);
4547 cell->face(1)->set_boundary_id(2);
4548
4549 for (unsigned int i = 2; i < 4; ++i)
4550 cell->face(i)->set_boundary_id(0);
4551 }
4552
4553
4554
4555 // Implementation for 2d only
4556 template <>
4557 void
4559 const double a,
4560 const double b,
4561 const bool colorize)
4562 {
4563 const Point<2> vertices[8] = {Point<2>(a, a),
4564 Point<2>((a + b) / 2, a),
4565 Point<2>(b, a),
4566 Point<2>(a, (a + b) / 2),
4567 Point<2>((a + b) / 2, (a + b) / 2),
4568 Point<2>(b, (a + b) / 2),
4569 Point<2>(a, b),
4570 Point<2>((a + b) / 2, b)};
4571 const int cell_vertices[3][4] = {{0, 1, 3, 4}, {1, 2, 4, 5}, {3, 4, 6, 7}};
4572
4573 std::vector<CellData<2>> cells(3, CellData<2>());
4574
4575 for (unsigned int i = 0; i < 3; ++i)
4576 {
4577 for (unsigned int j = 0; j < 4; ++j)
4578 cells[i].vertices[j] = cell_vertices[i][j];
4579 cells[i].material_id = 0;
4580 }
4581
4582 tria.create_triangulation(std::vector<Point<2>>(std::begin(vertices),
4583 std::end(vertices)),
4584 cells,
4585 SubCellData());
4586
4587 if (colorize)
4588 {
4590
4591 cell->face(0)->set_boundary_id(0);
4592 cell->face(2)->set_boundary_id(1);
4593 ++cell;
4594
4595 cell->face(1)->set_boundary_id(2);
4596 cell->face(2)->set_boundary_id(1);
4597 cell->face(3)->set_boundary_id(3);
4598 ++cell;
4599
4600 cell->face(0)->set_boundary_id(0);
4601 cell->face(1)->set_boundary_id(4);
4602 cell->face(3)->set_boundary_id(5);
4603 }
4604 }
4605
4606
4607
4608 template <int dim, int spacedim>
4609 void
4611 const std::vector<unsigned int> &repetitions,
4612 const Point<dim> &bottom_left,
4613 const Point<dim> &top_right,
4614 const std::vector<int> &n_cells_to_remove)
4615 {
4616 Assert(dim > 1, ExcNotImplemented());
4617 // Check the consistency of the dimensions provided.
4618 AssertDimension(repetitions.size(), dim);
4619 AssertDimension(n_cells_to_remove.size(), dim);
4620 for (unsigned int d = 0; d < dim; ++d)
4621 {
4622 Assert(std::fabs(n_cells_to_remove[d]) <= repetitions[d],
4623 ExcMessage("Attempting to cut away too many cells."));
4624 }
4625 // Create the domain to be cut
4628 repetitions,
4629 bottom_left,
4630 top_right);
4631 // compute the vertex of the cut step, we will cut according to the
4632 // location of the cartesian coordinates of the cell centers
4633 std::array<double, dim> h;
4634 Point<dim> cut_step;
4635 for (unsigned int d = 0; d < dim; ++d)
4636 {
4637 // mesh spacing in each direction in cartesian coordinates
4638 h[d] = (top_right[d] - bottom_left[d]) / repetitions[d];
4639 // left to right, bottom to top, front to back
4640 if (n_cells_to_remove[d] >= 0)
4641 {
4642 // cartesian coordinates of vertex location
4643 cut_step[d] =
4644 h[d] * std::fabs(n_cells_to_remove[d]) + bottom_left[d];
4645 }
4646 // right to left, top to bottom, back to front
4647 else
4648 {
4649 cut_step[d] = top_right[d] - h[d] * std::fabs(n_cells_to_remove[d]);
4650 }
4651 }
4652
4653
4654 // compute cells to remove
4655 std::set<typename Triangulation<dim, spacedim>::active_cell_iterator>
4656 cells_to_remove;
4657 for (const auto &cell : rectangle.active_cell_iterators())
4658 {
4659 bool remove_cell = true;
4660 for (unsigned int d = 0; d < dim && remove_cell; ++d)
4661 if ((n_cells_to_remove[d] > 0 && cell->center()[d] >= cut_step[d]) ||
4662 (n_cells_to_remove[d] < 0 && cell->center()[d] <= cut_step[d]))
4663 remove_cell = false;
4664 if (remove_cell)
4665 cells_to_remove.insert(cell);
4666 }
4667
4669 cells_to_remove,
4670 tria);
4671 }
4672
4673
4674
4675 template <int dim, int spacedim>
4676 void
4678 const Point<spacedim> &p,
4679 const double radius,
4680 const bool internal_manifolds)
4681 {
4682 if constexpr (dim == 2)
4683 {
4684 const auto embed_point = [](const double x,
4685 const double y) -> Point<spacedim> {
4686 if constexpr (spacedim == 2)
4687 return Point<spacedim>(x, y);
4688 else if constexpr (spacedim == 3)
4689 return Point<spacedim>(x, y, 0);
4690 else
4692 };
4693
4694
4695 // Equilibrate cell sizes at transition from the inner part
4696 // to the radial cells
4697 const double a = 1. / (1 + std::sqrt(2.0));
4698 const Point<spacedim> vertices[8] = {
4699 p + embed_point(-1., -1.) * (radius / std::sqrt(2.0)),
4700 p + embed_point(+1., -1.) * (radius / std::sqrt(2.0)),
4701 p + embed_point(-1., -1.) * (radius / std::sqrt(2.0) * a),
4702 p + embed_point(+1., -1.) * (radius / std::sqrt(2.0) * a),
4703 p + embed_point(-1., +1.) * (radius / std::sqrt(2.0) * a),
4704 p + embed_point(+1., +1.) * (radius / std::sqrt(2.0) * a),
4705 p + embed_point(-1., +1.) * (radius / std::sqrt(2.0)),
4706 p + embed_point(+1., +1.) * (radius / std::sqrt(2.0))};
4707
4708 std::vector<CellData<2>> cells(5, CellData<2>());
4709
4710 for (unsigned int i = 0; i < 5; ++i)
4711 {
4712 for (unsigned int j = 0; j < 4; ++j)
4713 cells[i].vertices[j] = circle_cell_vertices[i][j];
4714 cells[i].material_id = 0;
4715 cells[i].manifold_id = (i == 2 ? numbers::flat_manifold_id : 1);
4716 }
4717
4718 tria.create_triangulation(std::vector<Point<spacedim>>(
4719 std::begin(vertices), std::end(vertices)),
4720 cells,
4721 SubCellData()); // no boundary information
4724 if (internal_manifolds)
4726 else
4728 }
4729 else if constexpr (dim == 3)
4730 {
4731 const double a =
4732 1. / (1 + std::sqrt(3.0)); // equilibrate cell sizes at transition
4733 // from the inner part to the radial
4734 // cells
4735 const unsigned int n_vertices = 16;
4736 const Point<3> vertices[n_vertices] = {
4737 // first the vertices of the inner
4738 // cell
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 p + Point<3>(-1, +1, -1) * (radius / std::sqrt(3.0) * a),
4744 p + Point<3>(+1, +1, -1) * (radius / std::sqrt(3.0) * a),
4745 p + Point<3>(+1, +1, +1) * (radius / std::sqrt(3.0) * a),
4746 p + Point<3>(-1, +1, +1) * (radius / std::sqrt(3.0) * a),
4747 // now the eight vertices at
4748 // the outer sphere
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 p + Point<3>(-1, +1, -1) * (radius / std::sqrt(3.0)),
4754 p + Point<3>(+1, +1, -1) * (radius / std::sqrt(3.0)),
4755 p + Point<3>(+1, +1, +1) * (radius / std::sqrt(3.0)),
4756 p + Point<3>(-1, +1, +1) * (radius / std::sqrt(3.0)),
4757 };
4758
4759 // one needs to draw the seven cubes to
4760 // understand what's going on here
4761 const unsigned int n_cells = 7;
4762 const int cell_vertices[n_cells][8] = {
4763 {0, 1, 4, 5, 3, 2, 7, 6}, // center
4764 {8, 9, 12, 13, 0, 1, 4, 5}, // bottom
4765 {9, 13, 1, 5, 10, 14, 2, 6}, // right
4766 {11, 10, 3, 2, 15, 14, 7, 6}, // top
4767 {8, 0, 12, 4, 11, 3, 15, 7}, // left
4768 {8, 9, 0, 1, 11, 10, 3, 2}, // front
4769 {12, 4, 13, 5, 15, 7, 14, 6}}; // back
4770
4771 std::vector<CellData<3>> cells(n_cells, CellData<3>());
4772
4773 for (unsigned int i = 0; i < n_cells; ++i)
4774 {
4775 for (const unsigned int j : GeometryInfo<3>::vertex_indices())
4776 cells[i].vertices[j] = cell_vertices[i][j];
4777 cells[i].material_id = 0;
4778 cells[i].manifold_id = i == 0 ? numbers::flat_manifold_id : 1;
4779 }
4780
4781 tria.create_triangulation(std::vector<Point<3>>(std::begin(vertices),
4782 std::end(vertices)),
4783 cells,
4784 SubCellData()); // no boundary information
4787 if (internal_manifolds)
4789 else
4790 tria.set_manifold(1, FlatManifold<3>());
4791 }
4792 else
4794 }
4795
4796
4797
4798 template <int spacedim>
4799 void
4800 hyper_shell_2D(Triangulation<2, spacedim> &tria,
4801 const Point<spacedim> &center,
4802 const double inner_radius,
4803 const double outer_radius,
4804 const unsigned int n_cells,
4805 const bool colorize)
4806 {
4807 Assert((inner_radius > 0) && (inner_radius < outer_radius),
4808 ExcInvalidRadii());
4809
4810 const double pi = numbers::PI;
4811
4812 // determine the number of cells
4813 // for the grid. if not provided by
4814 // the user determine it such that
4815 // the length of each cell on the
4816 // median (in the middle between
4817 // the two circles) is equal to its
4818 // radial extent (which is the
4819 // difference between the two
4820 // radii)
4821 const unsigned int N =
4822 (n_cells == 0 ? static_cast<unsigned int>(
4823 std::ceil((2 * pi * (outer_radius + inner_radius) / 2) /
4824 (outer_radius - inner_radius))) :
4825 n_cells);
4826
4827 // set up N vertices on the
4828 // outer and N vertices on
4829 // the inner circle. the
4830 // first N ones are on the
4831 // outer one, and all are
4832 // numbered counter-clockwise
4833 std::vector<Point<spacedim>> vertices(2 * N);
4834 for (unsigned int i = 0; i < N; ++i)
4835 {
4837 point[0] = std::cos(2 * pi * i / N);
4838 point[1] = std::sin(2 * pi * i / N);
4839
4840 vertices[i] = point * outer_radius;
4841 vertices[i + N] = vertices[i] * (inner_radius / outer_radius);
4842
4843 vertices[i] += center;
4844 vertices[i + N] += center;
4845 }
4846
4847 std::vector<CellData<2>> cells(N, CellData<2>());
4848
4849 for (unsigned int i = 0; i < N; ++i)
4850 {
4851 cells[i].vertices[0] = i;
4852 cells[i].vertices[1] = (i + 1) % N;
4853 cells[i].vertices[2] = N + i;
4854 cells[i].vertices[3] = N + ((i + 1) % N);
4855
4856 cells[i].material_id = 0;
4857 }
4858
4859 tria.create_triangulation(vertices, cells, SubCellData());
4860
4861 if (colorize)
4862 colorize_hyper_shell(tria, center, inner_radius, outer_radius);
4863
4864 tria.set_all_manifold_ids(0);
4866 }
4867
4868
4869
4870 template <>
4871 void
4873 const Point<2> &center,
4874 const double inner_radius,
4875 const double outer_radius,
4876 const unsigned int n_cells,
4877 const bool colorize)
4878 {
4879 hyper_shell_2D(tria, center, inner_radius, outer_radius, n_cells, colorize);
4880 }
4881
4882
4883
4884 template <>
4885 void
4887 const Point<3> &center,
4888 const double inner_radius,
4889 const double outer_radius,
4890 const unsigned int n_cells,
4891 const bool colorize)
4892 {
4893 hyper_shell_2D(tria, center, inner_radius, outer_radius, n_cells, colorize);
4894 }
4895
4896
4897
4898 template <int dim>
4899 void
4901 const Point<dim> &inner_center,
4902 const Point<dim> &outer_center,
4903 const double inner_radius,
4904 const double outer_radius,
4905 const unsigned int n_cells)
4906 {
4908 tria, outer_center, inner_radius, outer_radius, n_cells, true);
4909
4910 // check the consistency of the dimensions provided
4911 Assert(
4912 outer_radius - inner_radius > outer_center.distance(inner_center),
4914 "The inner radius is greater than or equal to the outer radius plus eccentricity."));
4915
4916 // shift nodes along the inner boundary according to the position of
4917 // inner_circle
4918 std::set<Point<dim> *> vertices_to_move;
4919
4920 for (const auto &face : tria.active_face_iterators())
4921 if (face->boundary_id() == 0)
4922 for (unsigned int v = 0; v < GeometryInfo<dim>::vertices_per_face; ++v)
4923 vertices_to_move.insert(&face->vertex(v));
4924
4925 const auto shift = inner_center - outer_center;
4926 for (const auto &p : vertices_to_move)
4927 (*p) += shift;
4928
4929 // the original hyper_shell function assigns the same manifold id
4930 // to all cells and faces. Set all manifolds ids to a different
4931 // value (2), then use boundary ids to assign different manifolds to
4932 // the inner (0) and outer manifolds (1). Use a transfinite manifold
4933 // for all faces and cells aside from the boundaries.
4934 tria.set_all_manifold_ids(2);
4936
4937 SphericalManifold<dim> inner_manifold(inner_center);
4938 SphericalManifold<dim> outer_manifold(outer_center);
4939
4944 transfinite.initialize(tria);
4945
4946 tria.set_manifold(0, inner_manifold);
4947 tria.set_manifold(1, outer_manifold);
4948 tria.set_manifold(2, transfinite);
4949 }
4950
4951
4952
4953 // Implementation for 2d only
4954 template <>
4955 void
4957 const double radius,
4958 const double half_length)
4959 {
4960 Point<2> p1(-half_length, -radius);
4961 Point<2> p2(half_length, radius);
4962
4963 hyper_rectangle(tria, p1, p2, true);
4964
4967 while (f != end)
4968 {
4969 switch (f->boundary_id())
4970 {
4971 case 0:
4972 f->set_boundary_id(1);
4973 break;
4974 case 1:
4975 f->set_boundary_id(2);
4976 break;
4977 default:
4978 f->set_boundary_id(0);
4979 break;
4980 }
4981 ++f;
4982 }
4983 }
4984
4985 template <>
4986 void
4988 const unsigned int,
4989 const double,
4990 const double)
4991 {
4993 }
4994
4995
4996
4997 // Implementation for 2d only
4998 template <>
4999 void
5001 const double,
5002 const double,
5003 const double,
5004 const unsigned int,
5005 const unsigned int,
5006 const bool)
5007 {
5009 }
5010
5011
5012 template <>
5013 void
5015 const Point<2> &p,
5016 const double radius)
5017 {
5018 const unsigned int dim = 2;
5019
5020 // the numbers 0.55647 and 0.42883 have been found by a search for the
5021 // best aspect ratio (defined as the maximal between the minimal singular
5022 // value of the Jacobian)
5023 const Point<dim> vertices[7] = {p + Point<dim>(0, 0) * radius,
5024 p + Point<dim>(+1, 0) * radius,
5025 p + Point<dim>(+1, 0) * (radius * 0.55647),
5026 p + Point<dim>(0, +1) * (radius * 0.55647),
5027 p + Point<dim>(+1, +1) * (radius * 0.42883),
5028 p + Point<dim>(0, +1) * radius,
5029 p + Point<dim>(+1, +1) *
5030 (radius / std::sqrt(2.0))};
5031
5032 const int cell_vertices[3][4] = {{0, 2, 3, 4}, {1, 6, 2, 4}, {5, 3, 6, 4}};
5033
5034 std::vector<CellData<dim>> cells(3, CellData<dim>());
5035
5036 for (unsigned int i = 0; i < 3; ++i)
5037 {
5038 for (unsigned int j = 0; j < 4; ++j)
5039 cells[i].vertices[j] = cell_vertices[i][j];
5040 cells[i].material_id = 0;
5041 }
5042
5043 tria.create_triangulation(std::vector<Point<dim>>(std::begin(vertices),
5044 std::end(vertices)),
5045 cells,
5046 SubCellData()); // no boundary information
5047
5050
5052
5053 while (cell != end)
5054 {
5055 for (const unsigned int i : GeometryInfo<dim>::face_indices())
5056 {
5057 if (cell->face(i)->boundary_id() ==
5059 continue;
5060
5061 // If one the components is the same as the respective
5062 // component of the center, then this is part of the plane
5063 if (cell->face(i)->center()[0] < p[0] + 1.e-5 * radius ||
5064 cell->face(i)->center()[1] < p[1] + 1.e-5 * radius)
5065 {
5066 cell->face(i)->set_boundary_id(1);
5067 cell->face(i)->set_manifold_id(numbers::flat_manifold_id);
5068 }
5069 }
5070 ++cell;
5071 }
5073 }
5074
5075
5076 template <>
5077 void
5079 const Point<2> &p,
5080 const double radius)
5081 {
5082 // equilibrate cell sizes at
5083 // transition from the inner part
5084 // to the radial cells
5085 const double a = 1. / (1 + std::sqrt(2.0));
5086 const Point<2> vertices[8] = {
5087 p + Point<2>(0, -1) * radius,
5088 p + Point<2>(+1, -1) * (radius / std::sqrt(2.0)),
5089 p + Point<2>(0, -1) * (radius / std::sqrt(2.0) * a),
5090 p + Point<2>(+1, -1) * (radius / std::sqrt(2.0) * a),
5091 p + Point<2>(0, +1) * (radius / std::sqrt(2.0) * a),
5092 p + Point<2>(+1, +1) * (radius / std::sqrt(2.0) * a),
5093 p + Point<2>(0, +1) * radius,
5094 p + Point<2>(+1, +1) * (radius / std::sqrt(2.0))};
5095
5096 const int cell_vertices[5][4] = {{0, 1, 2, 3},
5097 {2, 3, 4, 5},
5098 {1, 7, 3, 5},
5099 {6, 4, 7, 5}};
5100
5101 std::vector<CellData<2>> cells(4, CellData<2>());
5102
5103 for (unsigned int i = 0; i < 4; ++i)
5104 {
5105 for (unsigned int j = 0; j < 4; ++j)
5106 cells[i].vertices[j] = cell_vertices[i][j];
5107 cells[i].material_id = 0;
5108 }
5109
5110 tria.create_triangulation(std::vector<Point<2>>(std::begin(vertices),
5111 std::end(vertices)),
5112 cells,
5113 SubCellData()); // no boundary information
5114
5117
5119
5120 while (cell != end)
5121 {
5122 for (const unsigned int i : GeometryInfo<2>::face_indices())
5123 {
5124 if (cell->face(i)->boundary_id() ==
5126 continue;
5127
5128 // If x is zero, then this is part of the plane
5129 if (cell->face(i)->center()[0] < p[0] + 1.e-5 * radius)
5130 {
5131 cell->face(i)->set_boundary_id(1);
5132 cell->face(i)->set_manifold_id(numbers::flat_manifold_id);
5133 }
5134 }
5135 ++cell;
5136 }
5138 }
5139
5140
5141
5142 // Implementation for 2d only
5143 template <>
5144 void
5146 const Point<2> &center,
5147 const double inner_radius,
5148 const double outer_radius,
5149 const unsigned int n_cells,
5150 const bool colorize)
5151 {
5152 Assert((inner_radius > 0) && (inner_radius < outer_radius),
5153 ExcInvalidRadii());
5154
5155 const double pi = numbers::PI;
5156 // determine the number of cells
5157 // for the grid. if not provided by
5158 // the user determine it such that
5159 // the length of each cell on the
5160 // median (in the middle between
5161 // the two circles) is equal to its
5162 // radial extent (which is the
5163 // difference between the two
5164 // radii)
5165 const unsigned int N =
5166 (n_cells == 0 ? static_cast<unsigned int>(
5167 std::ceil((pi * (outer_radius + inner_radius) / 2) /
5168 (outer_radius - inner_radius))) :
5169 n_cells);
5170
5171 // set up N+1 vertices on the
5172 // outer and N+1 vertices on
5173 // the inner circle. the
5174 // first N+1 ones are on the
5175 // outer one, and all are
5176 // numbered counter-clockwise
5177 std::vector<Point<2>> vertices(2 * (N + 1));
5178 for (unsigned int i = 0; i <= N; ++i)
5179 {
5180 // enforce that the x-coordinates
5181 // of the first and last point of
5182 // each half-circle are exactly
5183 // zero (contrary to what we may
5184 // compute using the imprecise
5185 // value of pi)
5186 vertices[i] =
5187 Point<2>(((i == 0) || (i == N) ? 0 : std::cos(pi * i / N - pi / 2)),
5188 std::sin(pi * i / N - pi / 2)) *
5190 vertices[i + N + 1] = vertices[i] * (inner_radius / outer_radius);
5191
5192 vertices[i] += center;
5193 vertices[i + N + 1] += center;
5194 }
5195
5196
5197 std::vector<CellData<2>> cells(N, CellData<2>());
5198
5199 for (unsigned int i = 0; i < N; ++i)
5200 {
5201 cells[i].vertices[0] = i;
5202 cells[i].vertices[1] = (i + 1) % (N + 1);
5203 cells[i].vertices[2] = N + 1 + i;
5204 cells[i].vertices[3] = N + 1 + ((i + 1) % (N + 1));
5205
5206 cells[i].material_id = 0;
5207 }
5208
5209 tria.create_triangulation(vertices, cells, SubCellData());
5210
5211 if (colorize)
5212 {
5214 for (; cell != tria.end(); ++cell)
5215 {
5216 cell->face(2)->set_boundary_id(1);
5217 }
5218 tria.begin()->face(0)->set_boundary_id(3);
5219
5220 tria.last()->face(1)->set_boundary_id(2);
5221 }
5222 tria.set_all_manifold_ids(0);
5223 tria.set_manifold(0, SphericalManifold<2>(center));
5224 }
5225
5226
5227 template <>
5228 void
5230 const Point<2> &center,
5231 const double inner_radius,
5232 const double outer_radius,
5233 const unsigned int n_cells,
5234 const bool colorize)
5235 {
5236 Assert((inner_radius > 0) && (inner_radius < outer_radius),
5237 ExcInvalidRadii());
5238
5239 const double pi = numbers::PI;
5240 // determine the number of cells
5241 // for the grid. if not provided by
5242 // the user determine it such that
5243 // the length of each cell on the
5244 // median (in the middle between
5245 // the two circles) is equal to its
5246 // radial extent (which is the
5247 // difference between the two
5248 // radii)
5249 const unsigned int N =
5250 (n_cells == 0 ? static_cast<unsigned int>(
5251 std::ceil((pi * (outer_radius + inner_radius) / 4) /
5252 (outer_radius - inner_radius))) :
5253 n_cells);
5254
5255 // set up N+1 vertices on the
5256 // outer and N+1 vertices on
5257 // the inner circle. the
5258 // first N+1 ones are on the
5259 // outer one, and all are
5260 // numbered counter-clockwise
5261 std::vector<Point<2>> vertices(2 * (N + 1));
5262 for (unsigned int i = 0; i <= N; ++i)
5263 {
5264 // enforce that the x-coordinates
5265 // of the last point is exactly
5266 // zero (contrary to what we may
5267 // compute using the imprecise
5268 // value of pi)
5269 vertices[i] = Point<2>(((i == N) ? 0 : std::cos(pi * i / N / 2)),
5270 std::sin(pi * i / N / 2)) *
5272 vertices[i + N + 1] = vertices[i] * (inner_radius / outer_radius);
5273
5274 vertices[i] += center;
5275 vertices[i + N + 1] += center;
5276 }
5277
5278
5279 std::vector<CellData<2>> cells(N, CellData<2>());
5280
5281 for (unsigned int i = 0; i < N; ++i)
5282 {
5283 cells[i].vertices[0] = i;
5284 cells[i].vertices[1] = (i + 1) % (N + 1);
5285 cells[i].vertices[2] = N + 1 + i;
5286 cells[i].vertices[3] = N + 1 + ((i + 1) % (N + 1));
5287
5288 cells[i].material_id = 0;
5289 }
5290
5291 tria.create_triangulation(vertices, cells, SubCellData());
5292
5293 if (colorize)
5294 {
5296 for (; cell != tria.end(); ++cell)
5297 {
5298 cell->face(2)->set_boundary_id(1);
5299 }
5300 tria.begin()->face(0)->set_boundary_id(3);
5301
5302 tria.last()->face(1)->set_boundary_id(2);
5303 }
5304
5305 tria.set_all_manifold_ids(0);
5306 tria.set_manifold(0, SphericalManifold<2>(center));
5307 }
5308
5309
5310
5311 // Implementation for 3d only
5312 template <>
5313 void
5315 const double left,
5316 const double right,
5317 const bool colorize)
5318 {
5319 const double rl2 = (right + left) / 2;
5320 const double len = (right - left) / 2.;
5321
5322 const Point<3> vertices[20] = {
5323 Point<3>(left, left, -len / 2.), Point<3>(rl2, left, -len / 2.),
5324 Point<3>(rl2, rl2, -len / 2.), Point<3>(left, rl2, -len / 2.),
5325 Point<3>(right, left, -len / 2.), Point<3>(right, rl2, -len / 2.),
5326 Point<3>(rl2, right, -len / 2.), Point<3>(left, right, -len / 2.),
5327 Point<3>(right, right, -len / 2.), Point<3>(rl2, left, -len / 2.),
5328 Point<3>(left, left, len / 2.), Point<3>(rl2, left, len / 2.),
5329 Point<3>(rl2, rl2, len / 2.), Point<3>(left, rl2, len / 2.),
5330 Point<3>(right, left, len / 2.), Point<3>(right, rl2, len / 2.),
5331 Point<3>(rl2, right, len / 2.), Point<3>(left, right, len / 2.),
5332 Point<3>(right, right, len / 2.), Point<3>(rl2, left, len / 2.)};
5333 const int cell_vertices[4][8] = {{0, 1, 3, 2, 10, 11, 13, 12},
5334 {9, 4, 2, 5, 19, 14, 12, 15},
5335 {3, 2, 7, 6, 13, 12, 17, 16},
5336 {2, 5, 6, 8, 12, 15, 16, 18}};
5337 std::vector<CellData<3>> cells(4, CellData<3>());
5338 for (unsigned int i = 0; i < 4; ++i)
5339 {
5340 for (unsigned int j = 0; j < 8; ++j)
5341 cells[i].vertices[j] = cell_vertices[i][j];
5342 cells[i].material_id = 0;
5343 }
5344 tria.create_triangulation(std::vector<Point<3>>(std::begin(vertices),
5345 std::end(vertices)),
5346 cells,
5347 SubCellData()); // no boundary information
5348
5349 if (colorize)
5350 {
5352 cell->face(1)->set_boundary_id(1);
5353 ++cell;
5354 cell->face(0)->set_boundary_id(2);
5355 }
5356 }
5357
5358
5359
5360 // Implementation for 3d only
5361 template <>
5362 void
5364 const double left,
5365 const double right,
5366 const double thickness,
5367 const bool colorize)
5368 {
5369 Assert(left < right,
5370 ExcMessage("Invalid left-to-right bounds of enclosed hypercube"));
5371
5372 std::vector<Point<3>> vertices(64);
5373 double coords[4];
5374 coords[0] = left - thickness;
5375 coords[1] = left;
5376 coords[2] = right;
5377 coords[3] = right + thickness;
5378
5379 unsigned int k = 0;
5380 for (const double z : coords)
5381 for (const double y : coords)
5382 for (const double x : coords)
5383 vertices[k++] = Point<3>(x, y, z);
5384
5385 const types::material_id materials[27] = {21, 20, 22, 17, 16, 18, 25,
5386 24, 26, 5, 4, 6, 1, 0,
5387 2, 9, 8, 10, 37, 36, 38,
5388 33, 32, 34, 41, 40, 42};
5389
5390 std::vector<CellData<3>> cells(27);
5391 k = 0;
5392 for (unsigned int z = 0; z < 3; ++z)
5393 for (unsigned int y = 0; y < 3; ++y)
5394 for (unsigned int x = 0; x < 3; ++x)
5395 {
5396 cells[k].vertices[0] = x + 4 * y + 16 * z;
5397 cells[k].vertices[1] = x + 4 * y + 16 * z + 1;
5398 cells[k].vertices[2] = x + 4 * y + 16 * z + 4;
5399 cells[k].vertices[3] = x + 4 * y + 16 * z + 5;
5400 cells[k].vertices[4] = x + 4 * y + 16 * z + 16;
5401 cells[k].vertices[5] = x + 4 * y + 16 * z + 17;
5402 cells[k].vertices[6] = x + 4 * y + 16 * z + 20;
5403 cells[k].vertices[7] = x + 4 * y + 16 * z + 21;
5404 if (colorize)
5405 cells[k].material_id = materials[k];
5406 ++k;
5407 }
5408 tria.create_triangulation(vertices,
5409 cells,
5410 SubCellData()); // no boundary information
5411 }
5412
5413
5414
5415 template <>
5416 void
5417 truncated_cone(Triangulation<3> &triangulation,
5418 const double radius_0,
5419 const double radius_1,
5420 const double half_length)
5421 {
5422 Assert(triangulation.n_cells() == 0,
5423 ExcMessage("The output triangulation object needs to be empty."));
5424 Assert(0 < radius_0, ExcMessage("The radii must be positive."));
5425 Assert(0 < radius_1, ExcMessage("The radii must be positive."));
5426 Assert(0 < half_length, ExcMessage("The half length must be positive."));
5427
5428 const auto n_slices = 1 + static_cast<unsigned int>(std::ceil(
5429 half_length / std::max(radius_0, radius_1)));
5430
5431 Triangulation<2> triangulation_2;
5432 GridGenerator::hyper_ball(triangulation_2, Point<2>(), radius_0);
5434 n_slices,
5435 2 * half_length,
5436 triangulation);
5437 GridTools::rotate(Tensor<1, 3>({0., 1., 0.}), numbers::PI_2, triangulation);
5438 GridTools::shift(Tensor<1, 3>({-half_length, 0.0, 0.0}), triangulation);
5439 // At this point we have a cylinder. Multiply the y and z coordinates by a
5440 // factor that scales (with x) linearly between radius_0 and radius_1 to fix
5441 // the circle radii and interior points:
5442 auto shift_radii = [=](const Point<3> &p) {
5443 const double slope = (radius_1 / radius_0 - 1.0) / (2.0 * half_length);
5444 const double factor = slope * (p[0] - -half_length) + 1.0;
5445 return Point<3>(p[0], factor * p[1], factor * p[2]);
5446 };
5447 GridTools::transform(shift_radii, triangulation);
5448
5449 // Set boundary ids at -half_length to 1 and at half_length to 2. Set the
5450 // manifold id on hull faces (i.e., faces not on either end) to 0.
5451 for (const auto &face : triangulation.active_face_iterators())
5452 if (face->at_boundary())
5453 {
5454 if (std::abs(face->center()[0] - -half_length) < 1e-8 * half_length)
5455 face->set_boundary_id(1);
5456 else if (std::abs(face->center()[0] - half_length) <
5457 1e-8 * half_length)
5458 face->set_boundary_id(2);
5459 else
5460 face->set_all_manifold_ids(0);
5461 }
5462
5463 triangulation.set_manifold(0, CylindricalManifold<3>());
5464 }
5465
5466
5467 // Implementation for 3d only
5468 template <>
5469 void
5471 const double a,
5472 const double b,
5473 const bool colorize)
5474 {
5475 // we slice out the top back right
5476 // part of the cube
5477 const Point<3> vertices[26] = {
5478 // front face of the big cube
5479 Point<3>(a, a, a),
5480 Point<3>((a + b) / 2, a, a),
5481 Point<3>(b, a, a),
5482 Point<3>(a, a, (a + b) / 2),
5483 Point<3>((a + b) / 2, a, (a + b) / 2),
5484 Point<3>(b, a, (a + b) / 2),
5485 Point<3>(a, a, b),
5486 Point<3>((a + b) / 2, a, b),
5487 Point<3>(b, a, b),
5488 // middle face of the big cube
5489 Point<3>(a, (a + b) / 2, a),
5490 Point<3>((a + b) / 2, (a + b) / 2, a),
5491 Point<3>(b, (a + b) / 2, a),
5492 Point<3>(a, (a + b) / 2, (a + b) / 2),
5493 Point<3>((a + b) / 2, (a + b) / 2, (a + b) / 2),
5494 Point<3>(b, (a + b) / 2, (a + b) / 2),
5495 Point<3>(a, (a + b) / 2, b),
5496 Point<3>((a + b) / 2, (a + b) / 2, b),
5497 Point<3>(b, (a + b) / 2, b),
5498 // back face of the big cube
5499 // last (top right) point is missing
5500 Point<3>(a, b, a),
5501 Point<3>((a + b) / 2, b, a),
5502 Point<3>(b, b, a),
5503 Point<3>(a, b, (a + b) / 2),
5504 Point<3>((a + b) / 2, b, (a + b) / 2),
5505 Point<3>(b, b, (a + b) / 2),
5506 Point<3>(a, b, b),
5507 Point<3>((a + b) / 2, b, b)};
5508 const int cell_vertices[7][8] = {{0, 1, 9, 10, 3, 4, 12, 13},
5509 {1, 2, 10, 11, 4, 5, 13, 14},
5510 {3, 4, 12, 13, 6, 7, 15, 16},
5511 {4, 5, 13, 14, 7, 8, 16, 17},
5512 {9, 10, 18, 19, 12, 13, 21, 22},
5513 {10, 11, 19, 20, 13, 14, 22, 23},
5514 {12, 13, 21, 22, 15, 16, 24, 25}};
5515
5516 std::vector<CellData<3>> cells(7, CellData<3>());
5517
5518 for (unsigned int i = 0; i < 7; ++i)
5519 {
5520 for (unsigned int j = 0; j < 8; ++j)
5521 cells[i].vertices[j] = cell_vertices[i][j];
5522 cells[i].material_id = 0;
5523 }
5524
5525 tria.create_triangulation(std::vector<Point<3>>(std::begin(vertices),
5526 std::end(vertices)),
5527 cells,
5528 SubCellData()); // no boundary information
5529
5530 if (colorize)
5531 {
5533 }
5534 }
5535
5536
5537
5538 void
5540 const unsigned int n_rotate_middle_square)
5541 {
5542 AssertThrow(n_rotate_middle_square < 4,
5543 ExcMessage("The number of rotation by pi/2 of the right square "
5544 "must be in the half-open range [0,4)."));
5545
5546 constexpr unsigned int dim = 2;
5547
5548 const unsigned int n_cells = 5;
5549 std::vector<CellData<dim>> cells(n_cells);
5550
5551 // Corner points of the cube [0,1]^2
5552 const std::vector<Point<dim>> vertices = {Point<dim>(0, 0), // 0
5553 Point<dim>(1, 0), // 1
5554 Point<dim>(0, 1), // 2
5555 Point<dim>(1, 1), // 3
5556 Point<dim>(2, 0), // 4
5557 Point<dim>(2, 1), // 5
5558 Point<dim>(3, 0), // 6
5559 Point<dim>(3, 1), // 7
5560 Point<dim>(1, -1), // 8
5561 Point<dim>(2, -1), // 9
5562 Point<dim>(1, 2), // 10
5563 Point<dim>(2, 2)}; // 11
5564
5565
5566 // consistent orientation
5567 unsigned int cell_vertices[n_cells][4] = {{0, 1, 2, 3},
5568 {1, 4, 3, 5}, // rotating cube
5569 {8, 9, 1, 4},
5570 {4, 6, 5, 7},
5571 {3, 5, 10, 11}};
5572
5573 switch (n_rotate_middle_square)
5574 {
5575 case /* rotate right square */ 1:
5576 {
5577 cell_vertices[1][0] = 4;
5578 cell_vertices[1][1] = 5;
5579 cell_vertices[1][2] = 1;
5580 cell_vertices[1][3] = 3;
5581 break;
5582 }
5583
5584 case /* rotate right square */ 2:
5585 {
5586 cell_vertices[1][0] = 5;
5587 cell_vertices[1][1] = 3;
5588 cell_vertices[1][2] = 4;
5589 cell_vertices[1][3] = 1;
5590 break;
5591 }
5592
5593 case /* rotate right square */ 3:
5594 {
5595 cell_vertices[1][0] = 3;
5596 cell_vertices[1][1] = 1;
5597 cell_vertices[1][2] = 5;
5598 cell_vertices[1][3] = 4;
5599 break;
5600 }
5601
5602 default /* 0 */:
5603 break;
5604 } // switch
5605
5606 cells.resize(n_cells, CellData<dim>());
5607
5608 for (unsigned int cell_index = 0; cell_index < n_cells; ++cell_index)
5609 {
5610 for (const unsigned int vertex_index :
5612 {
5613 cells[cell_index].vertices[vertex_index] =
5614 cell_vertices[cell_index][vertex_index];
5615 cells[cell_index].material_id = 0;
5616 }
5617 }
5618
5619 tria.create_triangulation(vertices, cells, SubCellData());
5620 }
5621
5622
5623 void
5625 const bool face_orientation,
5626 const bool face_flip,
5627 const bool face_rotation,
5628 const bool manipulate_left_cube)
5629 {
5630 constexpr unsigned int dim = 3;
5631
5632 const unsigned int n_cells = 2;
5633 std::vector<CellData<dim>> cells(n_cells);
5634
5635 // Corner points of the cube [0,1]^3
5636 const std::vector<Point<dim>> vertices = {Point<dim>(0, 0, 0), // 0
5637 Point<dim>(1, 0, 0), // 1
5638 Point<dim>(0, 1, 0), // 2
5639 Point<dim>(1, 1, 0), // 3
5640 Point<dim>(0, 0, 1), // 4
5641 Point<dim>(1, 0, 1), // 5
5642 Point<dim>(0, 1, 1), // 6
5643 Point<dim>(1, 1, 1), // 7
5644 Point<dim>(2, 0, 0), // 8
5645 Point<dim>(2, 1, 0), // 9
5646 Point<dim>(2, 0, 1), // 10
5647 Point<dim>(2, 1, 1)}; // 11
5648
5649 unsigned int cell_vertices[n_cells][8] = {
5650 {0, 1, 2, 3, 4, 5, 6, 7}, // unit cube
5651 {1, 8, 3, 9, 5, 10, 7, 11}}; // shifted cube
5652
5653 // binary to case number
5654 const unsigned int this_case = 4 * static_cast<int>(face_orientation) +
5655 2 * static_cast<int>(face_flip) +
5656 static_cast<int>(face_rotation);
5657
5658 if (manipulate_left_cube)
5659 {
5660 switch (this_case)
5661 {
5662 case 0:
5663 {
5664 cell_vertices[0][0] = 1;
5665 cell_vertices[0][1] = 0;
5666 cell_vertices[0][2] = 5;
5667 cell_vertices[0][3] = 4;
5668 cell_vertices[0][4] = 3;
5669 cell_vertices[0][5] = 2;
5670 cell_vertices[0][6] = 7;
5671 cell_vertices[0][7] = 6;
5672 break;
5673 }
5674
5675 case 1:
5676 {
5677 cell_vertices[0][0] = 5;
5678 cell_vertices[0][1] = 4;
5679 cell_vertices[0][2] = 7;
5680 cell_vertices[0][3] = 6;
5681 cell_vertices[0][4] = 1;
5682 cell_vertices[0][5] = 0;
5683 cell_vertices[0][6] = 3;
5684 cell_vertices[0][7] = 2;
5685 break;
5686 }
5687
5688 case 2:
5689 {
5690 cell_vertices[0][0] = 7;
5691 cell_vertices[0][1] = 6;
5692 cell_vertices[0][2] = 3;
5693 cell_vertices[0][3] = 2;
5694 cell_vertices[0][4] = 5;
5695 cell_vertices[0][5] = 4;
5696 cell_vertices[0][6] = 1;
5697 cell_vertices[0][7] = 0;
5698 break;
5699 }
5700 case 3:
5701 {
5702 cell_vertices[0][0] = 3;
5703 cell_vertices[0][1] = 2;
5704 cell_vertices[0][2] = 1;
5705 cell_vertices[0][3] = 0;
5706 cell_vertices[0][4] = 7;
5707 cell_vertices[0][5] = 6;
5708 cell_vertices[0][6] = 5;
5709 cell_vertices[0][7] = 4;
5710 break;
5711 }
5712
5713 case 4:
5714 {
5715 cell_vertices[0][0] = 0;
5716 cell_vertices[0][1] = 1;
5717 cell_vertices[0][2] = 2;
5718 cell_vertices[0][3] = 3;
5719 cell_vertices[0][4] = 4;
5720 cell_vertices[0][5] = 5;
5721 cell_vertices[0][6] = 6;
5722 cell_vertices[0][7] = 7;
5723 break;
5724 }
5725
5726 case 5:
5727 {
5728 cell_vertices[0][0] = 2;
5729 cell_vertices[0][1] = 3;
5730 cell_vertices[0][2] = 6;
5731 cell_vertices[0][3] = 7;
5732 cell_vertices[0][4] = 0;
5733 cell_vertices[0][5] = 1;
5734 cell_vertices[0][6] = 4;
5735 cell_vertices[0][7] = 5;
5736 break;
5737 }
5738
5739 case 6:
5740 {
5741 cell_vertices[0][0] = 6;
5742 cell_vertices[0][1] = 7;
5743 cell_vertices[0][2] = 4;
5744 cell_vertices[0][3] = 5;
5745 cell_vertices[0][4] = 2;
5746 cell_vertices[0][5] = 3;
5747 cell_vertices[0][6] = 0;
5748 cell_vertices[0][7] = 1;
5749 break;
5750 }
5751
5752 case 7:
5753 {
5754 cell_vertices[0][0] = 4;
5755 cell_vertices[0][1] = 5;
5756 cell_vertices[0][2] = 0;
5757 cell_vertices[0][3] = 1;
5758 cell_vertices[0][4] = 6;
5759 cell_vertices[0][5] = 7;
5760 cell_vertices[0][6] = 2;
5761 cell_vertices[0][7] = 3;
5762 break;
5763 }
5764 } // switch
5765 }
5766 else
5767 {
5768 switch (this_case)
5769 {
5770 case 0:
5771 {
5772 cell_vertices[1][0] = 8;
5773 cell_vertices[1][1] = 1;
5774 cell_vertices[1][2] = 10;
5775 cell_vertices[1][3] = 5;
5776 cell_vertices[1][4] = 9;
5777 cell_vertices[1][5] = 3;
5778 cell_vertices[1][6] = 11;
5779 cell_vertices[1][7] = 7;
5780 break;
5781 }
5782
5783 case 1:
5784 {
5785 cell_vertices[1][0] = 10;
5786 cell_vertices[1][1] = 5;
5787 cell_vertices[1][2] = 11;
5788 cell_vertices[1][3] = 7;
5789 cell_vertices[1][4] = 8;
5790 cell_vertices[1][5] = 1;
5791 cell_vertices[1][6] = 9;
5792 cell_vertices[1][7] = 3;
5793 break;
5794 }
5795
5796 case 2:
5797 {
5798 cell_vertices[1][0] = 11;
5799 cell_vertices[1][1] = 7;
5800 cell_vertices[1][2] = 9;
5801 cell_vertices[1][3] = 3;
5802 cell_vertices[1][4] = 10;
5803 cell_vertices[1][5] = 5;
5804 cell_vertices[1][6] = 8;
5805 cell_vertices[1][7] = 1;
5806 break;
5807 }
5808
5809 case 3:
5810 {
5811 cell_vertices[1][0] = 9;
5812 cell_vertices[1][1] = 3;
5813 cell_vertices[1][2] = 8;
5814 cell_vertices[1][3] = 1;
5815 cell_vertices[1][4] = 11;
5816 cell_vertices[1][5] = 7;
5817 cell_vertices[1][6] = 10;
5818 cell_vertices[1][7] = 5;
5819 break;
5820 }
5821
5822 case 4:
5823 {
5824 cell_vertices[1][0] = 1;
5825 cell_vertices[1][1] = 8;
5826 cell_vertices[1][2] = 3;
5827 cell_vertices[1][3] = 9;
5828 cell_vertices[1][4] = 5;
5829 cell_vertices[1][5] = 10;
5830 cell_vertices[1][6] = 7;
5831 cell_vertices[1][7] = 11;
5832 break;
5833 }
5834
5835 case 5:
5836 {
5837 cell_vertices[1][0] = 5;
5838 cell_vertices[1][1] = 10;
5839 cell_vertices[1][2] = 1;
5840 cell_vertices[1][3] = 8;
5841 cell_vertices[1][4] = 7;
5842 cell_vertices[1][5] = 11;
5843 cell_vertices[1][6] = 3;
5844 cell_vertices[1][7] = 9;
5845 break;
5846 }
5847
5848 case 6:
5849 {
5850 cell_vertices[1][0] = 7;
5851 cell_vertices[1][1] = 11;
5852 cell_vertices[1][2] = 5;
5853 cell_vertices[1][3] = 10;
5854 cell_vertices[1][4] = 3;
5855 cell_vertices[1][5] = 9;
5856 cell_vertices[1][6] = 1;
5857 cell_vertices[1][7] = 8;
5858 break;
5859 }
5860
5861 case 7:
5862 {
5863 cell_vertices[1][0] = 3;
5864 cell_vertices[1][1] = 9;
5865 cell_vertices[1][2] = 7;
5866 cell_vertices[1][3] = 11;
5867 cell_vertices[1][4] = 1;
5868 cell_vertices[1][5] = 8;
5869 cell_vertices[1][6] = 5;
5870 cell_vertices[1][7] = 10;
5871 break;
5872 }
5873 } // switch
5874 }
5875
5876 cells.resize(n_cells, CellData<dim>());
5877
5878 for (unsigned int cell_index = 0; cell_index < n_cells; ++cell_index)
5879 {
5880 for (const unsigned int vertex_index :
5882 {
5883 cells[cell_index].vertices[vertex_index] =
5884 cell_vertices[cell_index][vertex_index];
5885 cells[cell_index].material_id = 0;
5886 }
5887 }
5888
5889 tria.create_triangulation(vertices, cells, SubCellData());
5890 }
5891
5892
5893
5894 template <int spacedim>
5895 void
5897 const Point<spacedim> &p,
5898 const double radius)
5899 {
5900 Triangulation<spacedim> volume_mesh;
5901 GridGenerator::hyper_ball(volume_mesh, p, radius);
5902 const std::set<types::boundary_id> boundary_ids = {0};
5903 GridGenerator::extract_boundary_mesh(volume_mesh, tria, boundary_ids);
5904 tria.set_all_manifold_ids(0);
5906 }
5907
5908
5909
5910 // Implementation for 3d only
5911 template <>
5912 void
5914 const unsigned int x_subdivisions,
5915 const double radius,
5916 const double half_length)
5917 {
5918 // Copy the base from hyper_ball<3>
5919 // and transform it to yz
5920 const double d = radius / std::sqrt(2.0);
5921 const double a = d / (1 + std::sqrt(2.0));
5922
5923 std::vector<Point<3>> vertices;
5924 const double initial_height = -half_length;
5925 const double height_increment = 2. * half_length / x_subdivisions;
5926
5927 for (unsigned int rep = 0; rep < (x_subdivisions + 1); ++rep)
5928 {
5929 const double height = initial_height + height_increment * rep;
5930
5931 vertices.emplace_back(-d, height, -d);
5932 vertices.emplace_back(d, height, -d);
5933 vertices.emplace_back(-a, height, -a);
5934 vertices.emplace_back(a, height, -a);
5935 vertices.emplace_back(-a, height, a);
5936 vertices.emplace_back(a, height, a);
5937 vertices.emplace_back(-d, height, d);
5938 vertices.emplace_back(d, height, d);
5939 }
5940
5941 // Turn cylinder such that y->x
5942 for (auto &vertex : vertices)
5943 {
5944 const double h = vertex[1];
5945 vertex[1] = -vertex[0];
5946 vertex[0] = h;
5947 }
5948
5949 std::vector<std::vector<int>> cell_vertices;
5950 cell_vertices.push_back({0, 1, 8, 9, 2, 3, 10, 11});
5951 cell_vertices.push_back({0, 2, 8, 10, 6, 4, 14, 12});
5952 cell_vertices.push_back({2, 3, 10, 11, 4, 5, 12, 13});
5953 cell_vertices.push_back({1, 7, 9, 15, 3, 5, 11, 13});
5954 cell_vertices.push_back({6, 4, 14, 12, 7, 5, 15, 13});
5955
5956 for (unsigned int rep = 1; rep < x_subdivisions; ++rep)
5957 {
5958 for (unsigned int i = 0; i < 5; ++i)
5959 {
5960 std::vector<int> new_cell_vertices(8);
5961 for (unsigned int j = 0; j < 8; ++j)
5962 new_cell_vertices[j] = cell_vertices[i][j] + 8 * rep;
5963 cell_vertices.push_back(new_cell_vertices);
5964 }
5965 }
5966
5967 unsigned int n_cells = x_subdivisions * 5;
5968
5969 std::vector<CellData<3>> cells(n_cells, CellData<3>());
5970
5971 for (unsigned int i = 0; i < n_cells; ++i)
5972 {
5973 for (unsigned int j = 0; j < 8; ++j)
5974 cells[i].vertices[j] = cell_vertices[i][j];
5975 cells[i].material_id = 0;
5976 }
5977
5978 tria.create_triangulation(std::vector<Point<3>>(std::begin(vertices),
5979 std::end(vertices)),
5980 cells,
5981 SubCellData()); // no boundary information
5982
5983 // set boundary indicators for the
5984 // faces at the ends to 1 and 2,
5985 // respectively. note that we also
5986 // have to deal with those lines
5987 // that are purely in the interior
5988 // of the ends. we determine whether
5989 // an edge is purely in the
5990 // interior if one of its vertices
5991 // is at coordinates '+-a' as set
5992 // above
5994
5995 // Tolerance is calculated using the minimal length defining
5996 // the cylinder
5997 const double tolerance = 1e-5 * std::min(radius, half_length);
5998
5999 for (const auto &cell : tria.cell_iterators())
6000 for (const unsigned int i : GeometryInfo<3>::face_indices())
6001 if (cell->at_boundary(i))
6002 {
6003 if (cell->face(i)->center()[0] > half_length - tolerance)
6004 {
6005 cell->face(i)->set_boundary_id(2);
6006 cell->face(i)->set_manifold_id(numbers::flat_manifold_id);
6007
6008 for (unsigned int e = 0; e < GeometryInfo<3>::lines_per_face;
6009 ++e)
6010 if ((std::fabs(cell->face(i)->line(e)->vertex(0)[1]) == a) ||
6011 (std::fabs(cell->face(i)->line(e)->vertex(0)[2]) == a) ||
6012 (std::fabs(cell->face(i)->line(e)->vertex(1)[1]) == a) ||
6013 (std::fabs(cell->face(i)->line(e)->vertex(1)[2]) == a))
6014 {
6015 cell->face(i)->line(e)->set_boundary_id(2);
6016 cell->face(i)->line(e)->set_manifold_id(
6018 }
6019 }
6020 else if (cell->face(i)->center()[0] < -half_length + tolerance)
6021 {
6022 cell->face(i)->set_boundary_id(1);
6023 cell->face(i)->set_manifold_id(numbers::flat_manifold_id);
6024
6025 for (unsigned int e = 0; e < GeometryInfo<3>::lines_per_face;
6026 ++e)
6027 if ((std::fabs(cell->face(i)->line(e)->vertex(0)[1]) == a) ||
6028 (std::fabs(cell->face(i)->line(e)->vertex(0)[2]) == a) ||
6029 (std::fabs(cell->face(i)->line(e)->vertex(1)[1]) == a) ||
6030 (std::fabs(cell->face(i)->line(e)->vertex(1)[2]) == a))
6031 {
6032 cell->face(i)->line(e)->set_boundary_id(1);
6033 cell->face(i)->line(e)->set_manifold_id(
6035 }
6036 }
6037 }
6039 }
6040
6041 // Implementation for 3d only
6042 template <>
6043 void
6045 const double radius,
6046 const double half_length)
6047 {
6048 subdivided_cylinder(tria, 2, radius, half_length);
6049 }
6050
6051 template <>
6052 void
6054 const Point<3> &center,
6055 const double radius)
6056 {
6057 const unsigned int dim = 3;
6058
6059 // the parameters a (intersection on the octant lines from center), b
6060 // (intersection within the octant faces) and c (position inside the
6061 // octant) have been derived by equilibrating the minimal singular value
6062 // of the Jacobian of the four cells around the center point c and, as a
6063 // secondary measure, to minimize the aspect ratios defined as the maximal
6064 // divided by the minimal singular values throughout cells
6065 const double a = 0.528;
6066 const double b = 0.4533;
6067 const double c = 0.3752;
6068 const Point<dim> vertices[15] = {
6069 center + Point<dim>(0, 0, 0) * radius,
6070 center + Point<dim>(+1, 0, 0) * radius,
6071 center + Point<dim>(+1, 0, 0) * (radius * a),
6072 center + Point<dim>(0, +1, 0) * (radius * a),
6073 center + Point<dim>(+1, +1, 0) * (radius * b),
6074 center + Point<dim>(0, +1, 0) * radius,
6075 center + Point<dim>(+1, +1, 0) * radius / std::sqrt(2.0),
6076 center + Point<dim>(0, 0, 1) * radius * a,
6077 center + Point<dim>(+1, 0, 1) * radius / std::sqrt(2.0),
6078 center + Point<dim>(+1, 0, 1) * (radius * b),
6079 center + Point<dim>(0, +1, 1) * (radius * b),
6080 center + Point<dim>(+1, +1, 1) * (radius * c),
6081 center + Point<dim>(0, +1, 1) * radius / std::sqrt(2.0),
6082 center + Point<dim>(+1, +1, 1) * (radius / (std::sqrt(3.0))),
6083 center + Point<dim>(0, 0, 1) * radius};
6084 const int cell_vertices[4][8] = {{0, 2, 3, 4, 7, 9, 10, 11},
6085 {1, 6, 2, 4, 8, 13, 9, 11},
6086 {5, 3, 6, 4, 12, 10, 13, 11},
6087 {7, 9, 10, 11, 14, 8, 12, 13}};
6088
6089 std::vector<CellData<dim>> cells(4, CellData<dim>());
6090
6091 for (unsigned int i = 0; i < 4; ++i)
6092 {
6093 for (unsigned int j = 0; j < 8; ++j)
6094 cells[i].vertices[j] = cell_vertices[i][j];
6095 cells[i].material_id = 0;
6096 }
6097
6098 tria.create_triangulation(std::vector<Point<dim>>(std::begin(vertices),
6099 std::end(vertices)),
6100 cells,
6101 SubCellData()); // no boundary information
6102
6105
6107 while (cell != end)
6108 {
6109 for (const unsigned int i : GeometryInfo<dim>::face_indices())
6110 {
6111 if (cell->face(i)->boundary_id() ==
6113 continue;
6114
6115 // If x,y or z is zero, then this is part of the plane
6116 if (cell->face(i)->center()[0] < center[0] + 1.e-5 * radius ||
6117 cell->face(i)->center()[1] < center[1] + 1.e-5 * radius ||
6118 cell->face(i)->center()[2] < center[2] + 1.e-5 * radius)
6119 {
6120 cell->face(i)->set_boundary_id(1);
6121 cell->face(i)->set_manifold_id(numbers::flat_manifold_id);
6122 // also set the boundary indicators of the bounding lines,
6123 // unless both vertices are on the perimeter
6124 for (unsigned int j = 0; j < GeometryInfo<3>::lines_per_face;
6125 ++j)
6126 {
6127 const Point<3> line_vertices[2] = {
6128 cell->face(i)->line(j)->vertex(0),
6129 cell->face(i)->line(j)->vertex(1)};
6130 if ((std::fabs(line_vertices[0].distance(center) - radius) >
6131 1e-5 * radius) ||
6132 (std::fabs(line_vertices[1].distance(center) - radius) >
6133 1e-5 * radius))
6134 {
6135 cell->face(i)->line(j)->set_boundary_id(1);
6136 cell->face(i)->line(j)->set_manifold_id(
6138 }
6139 }
6140 }
6141 }
6142 ++cell;
6143 }
6144 tria.set_manifold(0, SphericalManifold<3>(center));
6145 }
6146
6147
6148
6149 // Implementation for 3d only
6150 template <>
6151 void
6153 const Point<3> &center,
6154 const double radius)
6155 {
6156 // These are for the two lower squares
6157 const double d = radius / std::sqrt(2.0);
6158 const double a = d / (1 + std::sqrt(2.0));
6159 // These are for the two upper square
6160 const double b = a / 2.0;
6161 const double c = d / 2.0;
6162 // And so are these
6163 const double hb = radius * std::sqrt(3.0) / 4.0;
6164 const double hc = radius * std::sqrt(3.0) / 2.0;
6165
6166 Point<3> vertices[16] = {
6167 center + Point<3>(0, d, -d),
6168 center + Point<3>(0, -d, -d),
6169 center + Point<3>(0, a, -a),
6170 center + Point<3>(0, -a, -a),
6171 center + Point<3>(0, a, a),
6172 center + Point<3>(0, -a, a),
6173 center + Point<3>(0, d, d),
6174 center + Point<3>(0, -d, d),
6175
6176 center + Point<3>(hc, c, -c),
6177 center + Point<3>(hc, -c, -c),
6178 center + Point<3>(hb, b, -b),
6179 center + Point<3>(hb, -b, -b),
6180 center + Point<3>(hb, b, b),
6181 center + Point<3>(hb, -b, b),
6182 center + Point<3>(hc, c, c),
6183 center + Point<3>(hc, -c, c),
6184 };
6185
6186 int cell_vertices[6][8] = {{0, 1, 8, 9, 2, 3, 10, 11},
6187 {0, 2, 8, 10, 6, 4, 14, 12},
6188 {2, 3, 10, 11, 4, 5, 12, 13},
6189 {1, 7, 9, 15, 3, 5, 11, 13},
6190 {6, 4, 14, 12, 7, 5, 15, 13},
6191 {8, 10, 9, 11, 14, 12, 15, 13}};
6192
6193 std::vector<CellData<3>> cells(6, CellData<3>());
6194
6195 for (unsigned int i = 0; i < 6; ++i)
6196 {
6197 for (unsigned int j = 0; j < 8; ++j)
6198 cells[i].vertices[j] = cell_vertices[i][j];
6199 cells[i].material_id = 0;
6200 }
6201
6202 tria.create_triangulation(std::vector<Point<3>>(std::begin(vertices),
6203 std::end(vertices)),
6204 cells,
6205 SubCellData()); // no boundary information
6206
6209
6211
6212 // go over all faces. for the ones on the flat face, set boundary
6213 // indicator for face and edges to one; the rest will remain at
6214 // zero but we have to pay attention to those edges that are
6215 // at the perimeter of the flat face since they should not be
6216 // set to one
6217 while (cell != end)
6218 {
6219 for (const unsigned int i : GeometryInfo<3>::face_indices())
6220 {
6221 if (!cell->at_boundary(i))
6222 continue;
6223
6224 // If the center is on the plane x=0, this is a planar element. set
6225 // its boundary indicator. also set the boundary indicators of the
6226 // bounding faces unless both vertices are on the perimeter
6227 if (cell->face(i)->center()[0] < center[0] + 1.e-5 * radius)
6228 {
6229 cell->face(i)->set_boundary_id(1);
6230 cell->face(i)->set_manifold_id(numbers::flat_manifold_id);
6231 for (unsigned int j = 0; j < GeometryInfo<3>::lines_per_face;
6232 ++j)
6233 {
6234 const Point<3> line_vertices[2] = {
6235 cell->face(i)->line(j)->vertex(0),
6236 cell->face(i)->line(j)->vertex(1)};
6237 if ((std::fabs(line_vertices[0].distance(center) - radius) >
6238 1e-5 * radius) ||
6239 (std::fabs(line_vertices[1].distance(center) - radius) >
6240 1e-5 * radius))
6241 {
6242 cell->face(i)->line(j)->set_boundary_id(1);
6243 cell->face(i)->line(j)->set_manifold_id(
6245 }
6246 }
6247 }
6248 }
6249 ++cell;
6250 }
6251 tria.set_manifold(0, SphericalManifold<3>(center));
6252 }
6253
6254
6255
6256 template <int dim>
6257 void
6259 const Point<dim> &p,
6260 const double radius)
6261 {
6262 // We create the ball by duplicating the information in each dimension at
6263 // a time by appropriate rotations, starting from the quarter ball. The
6264 // rotations make sure we do not generate inverted cells that would appear
6265 // if we tried the slightly simpler approach to simply mirror the cells.
6266 //
6267 // Make the rotations easy by centering at the origin now and shifting by p
6268 // later.
6269
6270 Triangulation<dim> tria_piece;
6272
6273 for (unsigned int round = 0; round < dim; ++round)
6274 {
6275 Triangulation<dim> tria_copy;
6276 tria_copy.copy_triangulation(tria_piece);
6277 tria_piece.clear();
6278 std::vector<Point<dim>> new_points(tria_copy.n_vertices());
6279 if (round == 0)
6280 for (unsigned int v = 0; v < tria_copy.n_vertices(); ++v)
6281 {
6282 // rotate by 90 degrees counterclockwise
6283 new_points[v][0] = -tria_copy.get_vertices()[v][1];
6284 new_points[v][1] = tria_copy.get_vertices()[v][0];
6285 if (dim == 3)
6286 new_points[v][2] = tria_copy.get_vertices()[v][2];
6287 }
6288 else if (round == 1)
6289 {
6290 for (unsigned int v = 0; v < tria_copy.n_vertices(); ++v)
6291 {
6292 // rotate by 180 degrees along the xy plane
6293 new_points[v][0] = -tria_copy.get_vertices()[v][0];
6294 new_points[v][1] = -tria_copy.get_vertices()[v][1];
6295 if (dim == 3)
6296 new_points[v][2] = tria_copy.get_vertices()[v][2];
6297 }
6298 }
6299 else if (round == 2)
6300 for (unsigned int v = 0; v < tria_copy.n_vertices(); ++v)
6301 {
6302 // rotate by 180 degrees along the xz plane
6303 Assert(dim == 3, ExcInternalError());
6304 new_points[v][0] = -tria_copy.get_vertices()[v][0];
6305 new_points[v][1] = tria_copy.get_vertices()[v][1];
6306 new_points[v][2] = -tria_copy.get_vertices()[v][2];
6307 }
6308 else
6310
6311
6312 // the cell data is exactly the same as before
6313 std::vector<CellData<dim>> cells;
6314 cells.reserve(tria_copy.n_cells());
6315 for (const auto &cell : tria_copy.cell_iterators())
6316 {
6318 for (const unsigned int v : GeometryInfo<dim>::vertex_indices())
6319 data.vertices[v] = cell->vertex_index(v);
6320 data.material_id = cell->material_id();
6321 data.manifold_id = cell->manifold_id();
6322 cells.push_back(data);
6323 }
6324
6325 Triangulation<dim> rotated_tria;
6326 rotated_tria.create_triangulation(new_points, cells, SubCellData());
6327
6328 // merge the triangulations - this will make sure that the duplicate
6329 // vertices in the interior are absorbed
6330 if (round == dim - 1)
6331 merge_triangulations(tria_copy, rotated_tria, tria, 1e-12 * radius);
6332 else
6333 merge_triangulations(tria_copy,
6334 rotated_tria,
6335 tria_piece,
6336 1e-12 * radius);
6337 }
6338
6339 for (const auto &cell : tria.cell_iterators())
6340 if (cell->center().norm_square() > 0.4 * radius)
6341 cell->set_manifold_id(1);
6342 else
6343 cell->set_all_manifold_ids(numbers::flat_manifold_id);
6344 GridTools::shift(p, tria);
6345
6347
6350 }
6351
6352 // To work around an internal clang-13 error we need to split up the
6353 // individual hyper shell functions. This has the added bonus of making the
6354 // control flow easier to follow - some hyper shell functions call others.
6355 namespace internal
6356 {
6357 namespace
6358 {
6359 void
6360 hyper_shell_6(Triangulation<3> &tria,
6361 const Point<3> &p,
6362 const double inner_radius,
6363 const double outer_radius)
6364 {
6365 std::vector<Point<3>> vertices;
6366 std::vector<CellData<3>> cells;
6367
6368 const double irad = inner_radius / std::sqrt(3.0);
6369 const double orad = outer_radius / std::sqrt(3.0);
6370
6371 // Corner points of the cube [-1,1]^3
6372 static const std::array<Point<3>, 8> hexahedron = {{{-1, -1, -1}, //
6373 {+1, -1, -1}, //
6374 {-1, +1, -1}, //
6375 {+1, +1, -1}, //
6376 {-1, -1, +1}, //
6377 {+1, -1, +1}, //
6378 {-1, +1, +1}, //
6379 {+1, +1, +1}}};
6380
6381 // Start with the shell bounded by two nested cubes
6382 vertices.reserve(8);
6383 for (unsigned int i = 0; i < 8; ++i)
6384 vertices.push_back(p + hexahedron[i] * irad);
6385 for (unsigned int i = 0; i < 8; ++i)
6386 vertices.push_back(p + hexahedron[i] * orad);
6387
6388 const unsigned int n_cells = 6;
6389 const int cell_vertices[n_cells][8] = {
6390 {8, 9, 10, 11, 0, 1, 2, 3}, // bottom
6391 {9, 11, 1, 3, 13, 15, 5, 7}, // right
6392 {12, 13, 4, 5, 14, 15, 6, 7}, // top
6393 {8, 0, 10, 2, 12, 4, 14, 6}, // left
6394 {8, 9, 0, 1, 12, 13, 4, 5}, // front
6395 {10, 2, 11, 3, 14, 6, 15, 7}}; // back
6396
6397 cells.resize(n_cells, CellData<3>());
6398
6399 for (unsigned int i = 0; i < n_cells; ++i)
6400 {
6401 for (const unsigned int j : GeometryInfo<3>::vertex_indices())
6402 cells[i].vertices[j] = cell_vertices[i][j];
6403 cells[i].material_id = 0;
6404 }
6405
6406 tria.create_triangulation(vertices, cells, SubCellData());
6407 tria.set_all_manifold_ids(0);
6409 }
6410
6411 void
6412 hyper_shell_12(Triangulation<3> &tria,
6413 const Point<3> &p,
6414 const double inner_radius,
6415 const double outer_radius)
6416 {
6417 std::vector<Point<3>> vertices;
6418 std::vector<CellData<3>> cells;
6419
6420 const double irad = inner_radius / std::sqrt(3.0);
6421 const double orad = outer_radius / std::sqrt(3.0);
6422
6423 // A more regular subdivision can be obtained by two nested rhombic
6424 // dodecahedra
6425 //
6426 // Octahedron inscribed in the cube [-1,1]^3
6427 static const std::array<Point<3>, 6> octahedron = {{{-1, 0, 0}, //
6428 {1, 0, 0}, //
6429 {0, -1, 0}, //
6430 {0, 1, 0}, //
6431 {0, 0, -1}, //
6432 {0, 0, 1}}};
6433
6434 // Corner points of the cube [-1,1]^3
6435 static const std::array<Point<3>, 8> hexahedron = {{{-1, -1, -1}, //
6436 {+1, -1, -1}, //
6437 {-1, +1, -1}, //
6438 {+1, +1, -1}, //
6439 {-1, -1, +1}, //
6440 {+1, -1, +1}, //
6441 {-1, +1, +1}, //
6442 {+1, +1, +1}}};
6443
6444 vertices.reserve(8);
6445 for (unsigned int i = 0; i < 8; ++i)
6446 vertices.push_back(p + hexahedron[i] * irad);
6447 for (unsigned int i = 0; i < 6; ++i)
6448 vertices.push_back(p + octahedron[i] * inner_radius);
6449 for (unsigned int i = 0; i < 8; ++i)
6450 vertices.push_back(p + hexahedron[i] * orad);
6451 for (unsigned int i = 0; i < 6; ++i)
6452 vertices.push_back(p + octahedron[i] * outer_radius);
6453
6454 const unsigned int n_cells = 12;
6455 const unsigned int rhombi[n_cells][4] = {{10, 4, 0, 8},
6456 {4, 13, 8, 6},
6457 {10, 5, 4, 13},
6458 {1, 9, 10, 5},
6459 {9, 7, 5, 13},
6460 {7, 11, 13, 6},
6461 {9, 3, 7, 11},
6462 {1, 12, 9, 3},
6463 {12, 2, 3, 11},
6464 {2, 8, 11, 6},
6465 {12, 0, 2, 8},
6466 {1, 10, 12, 0}};
6467
6468 cells.resize(n_cells, CellData<3>());
6469
6470 for (unsigned int i = 0; i < n_cells; ++i)
6471 {
6472 for (unsigned int j = 0; j < 4; ++j)
6473 {
6474 cells[i].vertices[j] = rhombi[i][j];
6475 cells[i].vertices[j + 4] = rhombi[i][j] + 14;
6476 }
6477 cells[i].material_id = 0;
6478 }
6479
6480 tria.create_triangulation(vertices, cells, SubCellData());
6481 tria.set_all_manifold_ids(0);
6483 }
6484
6485 void
6486 hyper_shell_24_48(Triangulation<3> &tria,
6487 const unsigned int n,
6488 const unsigned int n_refinement_steps,
6489 const Point<3> &p,
6490 const double inner_radius,
6491 const double outer_radius)
6492 {
6493 // These two meshes are created by first creating a mesh of the
6494 // 6-cell/12-cell version, refining globally, and removing the outer
6495 // half of the cells. For 192 and more cells, we do this iteratively
6496 // several times, always refining and removing the outer half. Thus, the
6497 // outer radius for the start is larger and set as 2^n_refinement_steps
6498 // such that it exactly gives the desired radius in the end. It would
6499 // have been slightly less code to treat refinement steps recursively
6500 // for 192 cells or beyond, but unfortunately we could end up with the
6501 // 96 cell case which is not what we want. Thus, we need to implement a
6502 // loop manually here.
6503 Triangulation<3> tmp;
6504 const unsigned int outer_radius_factor = 1 << n_refinement_steps;
6505 if (n == 24)
6506 hyper_shell_6(tmp,
6507 p,
6508 inner_radius,
6509 outer_radius_factor * outer_radius -
6510 (outer_radius_factor - 1) * inner_radius);
6511 else if (n == 48)
6512 hyper_shell_12(tmp,
6513 p,
6514 inner_radius,
6515 outer_radius_factor * outer_radius -
6516 (outer_radius_factor - 1) * inner_radius);
6517 else
6518 Assert(n == 24 || n == 48, ExcInternalError());
6519 for (unsigned int r = 0; r < n_refinement_steps; ++r)
6520 {
6521 tmp.refine_global(1);
6522 std::set<Triangulation<3>::active_cell_iterator> cells_to_remove;
6523
6524 // We remove all cells which do not have exactly four vertices
6525 // at the inner radius (plus some tolerance).
6526 for (const auto &cell : tmp.active_cell_iterators())
6527 {
6528 unsigned int n_vertices_inside = 0;
6529 for (const auto v : GeometryInfo<3>::vertex_indices())
6530 if ((cell->vertex(v) - p).norm_square() <
6531 inner_radius * inner_radius * (1 + 1e-12))
6532 ++n_vertices_inside;
6533 if (n_vertices_inside < 4)
6534 cells_to_remove.insert(cell);
6535 }
6536
6537 AssertDimension(cells_to_remove.size(), tmp.n_active_cells() / 2);
6538 if (r == n_refinement_steps - 1)
6540 cells_to_remove,
6541 tria);
6542 else
6543 {
6546 cells_to_remove,
6547 copy);
6548 tmp = std::move(copy);
6549 tmp.set_all_manifold_ids(0);
6551 }
6552 }
6553 tria.set_all_manifold_ids(0);
6555 }
6556
6557 } // namespace
6558 } // namespace internal
6559
6560
6561
6562 template <>
6563 void
6565 const Point<3> &p,
6566 const double inner_radius,
6567 const double outer_radius,
6568 const unsigned int n_cells,
6569 const bool colorize)
6570 {
6571 Assert((inner_radius > 0) && (inner_radius < outer_radius),
6572 ExcInvalidRadii());
6573
6574 unsigned int n_refinement_steps = 0;
6575 unsigned int n_cells_coarsened = n_cells;
6576 if (n_cells != 96 && n_cells > 12)
6577 while (n_cells_coarsened > 12 && n_cells_coarsened % 4 == 0)
6578 {
6579 ++n_refinement_steps;
6580 n_cells_coarsened /= 4;
6581 }
6582 Assert(n_cells == 0 || n_cells == 6 || n_cells == 12 || n_cells == 96 ||
6583 (n_refinement_steps > 0 &&
6584 (n_cells_coarsened == 6 || n_cells_coarsened == 12)),
6585 ExcMessage("Invalid number of coarse mesh cells"));
6586
6587 const unsigned int n = n_refinement_steps > 0 ?
6588 4 * n_cells_coarsened :
6589 ((n_cells == 0) ? 6 : n_cells);
6590
6591 switch (n)
6592 {
6593 case 6:
6594 internal::hyper_shell_6(tria, p, inner_radius, outer_radius);
6595 break;
6596 case 12:
6597 internal::hyper_shell_12(tria, p, inner_radius, outer_radius);
6598 break;
6599 case 24:
6600 case 48:
6601 internal::hyper_shell_24_48(
6602 tria, n, n_refinement_steps, p, inner_radius, outer_radius);
6603 break;
6604 case 96:
6605 {
6606 // create a triangulation based on the 12-cell version. This
6607 // function was needed before SphericalManifold was written: it
6608 // manually adjusted the interior vertices to lie along concentric
6609 // spheres. Nowadays we can just refine globally:
6610 Triangulation<3> tmp;
6611 internal::hyper_shell_12(tmp, p, inner_radius, outer_radius);
6612 tmp.refine_global(1);
6613 flatten_triangulation(tmp, tria);
6614 tria.set_all_manifold_ids(0);
6616 break;
6617 }
6618 default:
6619 {
6620 Assert(false, ExcMessage("Invalid number of coarse mesh cells."));
6621 }
6622 }
6623
6624 if (n_cells > 0)
6625 AssertDimension(tria.n_global_active_cells(), n_cells);
6626
6627 if (colorize)
6628 colorize_hyper_shell(tria, p, inner_radius, outer_radius);
6629 }
6630
6631
6632
6633 // Implementation for 3d only
6634 template <>
6635 void
6637 const Point<3> &center,
6638 const double inner_radius,
6639 const double outer_radius,
6640 const unsigned int /*n_cells*/,
6641 const bool colorize)
6642 {
6643 Assert((inner_radius > 0) && (inner_radius < outer_radius),
6644 ExcInvalidRadii());
6645
6646 // These are for the two lower squares
6647 const double d = outer_radius / std::sqrt(2.0);
6648 const double a = inner_radius / std::sqrt(2.0);
6649 // These are for the two upper square
6650 const double b = a / 2.0;
6651 const double c = d / 2.0;
6652 // And so are these
6653 const double hb = inner_radius * std::sqrt(3.0) / 2.0;
6654 const double hc = outer_radius * std::sqrt(3.0) / 2.0;
6655
6656 Point<3> vertices[16] = {
6657 center + Point<3>(0, d, -d),
6658 center + Point<3>(0, -d, -d),
6659 center + Point<3>(0, a, -a),
6660 center + Point<3>(0, -a, -a),
6661 center + Point<3>(0, a, a),
6662 center + Point<3>(0, -a, a),
6663 center + Point<3>(0, d, d),
6664 center + Point<3>(0, -d, d),
6665
6666 center + Point<3>(hc, c, -c),
6667 center + Point<3>(hc, -c, -c),
6668 center + Point<3>(hb, b, -b),
6669 center + Point<3>(hb, -b, -b),
6670 center + Point<3>(hb, b, b),
6671 center + Point<3>(hb, -b, b),
6672 center + Point<3>(hc, c, c),
6673 center + Point<3>(hc, -c, c),
6674 };
6675
6676 int cell_vertices[5][8] = {{0, 1, 8, 9, 2, 3, 10, 11},
6677 {0, 2, 8, 10, 6, 4, 14, 12},
6678 {1, 7, 9, 15, 3, 5, 11, 13},
6679 {6, 4, 14, 12, 7, 5, 15, 13},
6680 {8, 10, 9, 11, 14, 12, 15, 13}};
6681
6682 std::vector<CellData<3>> cells(5, CellData<3>());
6683
6684 for (unsigned int i = 0; i < 5; ++i)
6685 {
6686 for (unsigned int j = 0; j < 8; ++j)
6687 cells[i].vertices[j] = cell_vertices[i][j];
6688 cells[i].material_id = 0;
6689 }
6690
6691 tria.create_triangulation(std::vector<Point<3>>(std::begin(vertices),
6692 std::end(vertices)),
6693 cells,
6694 SubCellData()); // no boundary information
6695
6696 tria.set_all_manifold_ids(0);
6697 tria.set_manifold(0, SphericalManifold<3>(center));
6698
6699 if (colorize)
6700 {
6701 // We want to use a flat boundary description where
6702 // the boundary is not curved. Hence set boundary id 2
6703 // to all faces in a first step.
6704 for (const auto &cell : tria.cell_iterators())
6705 for (const unsigned int f : cell->face_indices())
6706 if (cell->at_boundary(f))
6707 cell->face(f)->set_all_boundary_ids(2);
6708
6709 // Next look for the curved boundaries. If the x value of the
6710 // center of the face is not equal to center(0), we're on a curved
6711 // boundary. Then decide whether the center is nearer to the inner
6712 // or outer boundary to set the correct boundary id.
6713 for (const auto &cell : tria.cell_iterators())
6714 for (const unsigned int f : cell->face_indices())
6715 if (cell->at_boundary(f))
6716 {
6717 const Triangulation<3>::face_iterator face = cell->face(f);
6718 const Point<3> face_center(face->center(true));
6719
6720 if (std::abs(face_center[0] - center[0]) >
6721 1.e-6 * face_center.norm())
6722 {
6723 if (std::abs((face_center - center).norm() - inner_radius) <
6724 std::abs((face_center - center).norm() - outer_radius))
6725 face->set_all_boundary_ids(0);
6726 else
6727 face->set_all_boundary_ids(1);
6728 }
6729 }
6730 }
6731 }
6732
6733
6734 // Implementation for 3d only
6735 template <>
6736 void
6738 const Point<3> &center,
6739 const double inner_radius,
6740 const double outer_radius,
6741 const unsigned int n,
6742 const bool colorize)
6743 {
6744 Assert((inner_radius > 0) && (inner_radius < outer_radius),
6745 ExcInvalidRadii());
6746 if (n == 0 || n == 3)
6747 {
6748 const double a = inner_radius * std::sqrt(2.0) / 2e0;
6749 const double b = outer_radius * std::sqrt(2.0) / 2e0;
6750 const double c = a * std::sqrt(3.0) / 2e0;
6751 const double d = b * std::sqrt(3.0) / 2e0;
6752 const double e = outer_radius / 2e0;
6753 const double h = inner_radius / 2e0;
6754
6755 std::vector<Point<3>> vertices;
6756
6757 vertices.push_back(center + Point<3>(0, inner_radius, 0)); // 0
6758 vertices.push_back(center + Point<3>(a, a, 0)); // 1
6759 vertices.push_back(center + Point<3>(b, b, 0)); // 2
6760 vertices.push_back(center + Point<3>(0, outer_radius, 0)); // 3
6761 vertices.push_back(center + Point<3>(0, a, a)); // 4
6762 vertices.push_back(center + Point<3>(c, c, h)); // 5
6763 vertices.push_back(center + Point<3>(d, d, e)); // 6
6764 vertices.push_back(center + Point<3>(0, b, b)); // 7
6765 vertices.push_back(center + Point<3>(inner_radius, 0, 0)); // 8
6766 vertices.push_back(center + Point<3>(outer_radius, 0, 0)); // 9
6767 vertices.push_back(center + Point<3>(a, 0, a)); // 10
6768 vertices.push_back(center + Point<3>(b, 0, b)); // 11
6769 vertices.push_back(center + Point<3>(0, 0, inner_radius)); // 12
6770 vertices.push_back(center + Point<3>(0, 0, outer_radius)); // 13
6771
6772 const int cell_vertices[3][8] = {
6773 {0, 1, 3, 2, 4, 5, 7, 6},
6774 {1, 8, 2, 9, 5, 10, 6, 11},
6775 {4, 5, 7, 6, 12, 10, 13, 11},
6776 };
6777 std::vector<CellData<3>> cells(3);
6778
6779 for (unsigned int i = 0; i < 3; ++i)
6780 {
6781 for (unsigned int j = 0; j < 8; ++j)
6782 cells[i].vertices[j] = cell_vertices[i][j];
6783 cells[i].material_id = 0;
6784 }
6785
6786 tria.create_triangulation(vertices,
6787 cells,
6788 SubCellData()); // no boundary information
6789 }
6790 else
6791 {
6793 }
6794
6795 tria.set_all_manifold_ids(0);
6796 tria.set_manifold(0, SphericalManifold<3>(center));
6797
6798 if (colorize)
6799 colorize_quarter_hyper_shell(tria, center, inner_radius, outer_radius);
6800 }
6801
6802
6803 // Implementation for 3d only
6804 template <>
6805 void
6807 const double length,
6808 const double inner_radius,
6809 const double outer_radius,
6810 const unsigned int n_radial_cells,
6811 const unsigned int n_axial_cells,
6812 const bool colorize)
6813 {
6814 Assert((inner_radius > 0) && (inner_radius < outer_radius),
6815 ExcInvalidRadii());
6816
6817 const double pi = numbers::PI;
6818
6819 // determine the number of cells
6820 // for the grid. if not provided by
6821 // the user determine it such that
6822 // the length of each cell on the
6823 // median (in the middle between
6824 // the two circles) is equal to its
6825 // radial extent (which is the
6826 // difference between the two
6827 // radii)
6828 const unsigned int N_r =
6829 (n_radial_cells == 0 ? static_cast<unsigned int>(std::ceil(
6830 (2 * pi * (outer_radius + inner_radius) / 2) /
6831 (outer_radius - inner_radius))) :
6832 n_radial_cells);
6833 const unsigned int N_z =
6834 (n_axial_cells == 0 ?
6835 static_cast<unsigned int>(std::ceil(
6836 length / (2 * pi * (outer_radius + inner_radius) / 2 / N_r))) :
6837 n_axial_cells);
6838
6839 // set up N vertices on the
6840 // outer and N vertices on
6841 // the inner circle. the
6842 // first N ones are on the
6843 // outer one, and all are
6844 // numbered counter-clockwise
6845 std::vector<Point<2>> vertices_2d(2 * N_r);
6846 for (unsigned int i = 0; i < N_r; ++i)
6847 {
6848 vertices_2d[i] =
6849 Point<2>(std::cos(2 * pi * i / N_r), std::sin(2 * pi * i / N_r)) *
6851 vertices_2d[i + N_r] = vertices_2d[i] * (inner_radius / outer_radius);
6852 }
6853
6854 std::vector<Point<3>> vertices_3d;
6855 vertices_3d.reserve(2 * N_r * (N_z + 1));
6856 for (unsigned int j = 0; j <= N_z; ++j)
6857 for (unsigned int i = 0; i < 2 * N_r; ++i)
6858 {
6859 const Point<3> v(vertices_2d[i][0],
6860 vertices_2d[i][1],
6861 j * length / N_z);
6862 vertices_3d.push_back(v);
6863 }
6864
6865 std::vector<CellData<3>> cells(N_r * N_z, CellData<3>());
6866
6867 for (unsigned int j = 0; j < N_z; ++j)
6868 for (unsigned int i = 0; i < N_r; ++i)
6869 {
6870 cells[i + j * N_r].vertices[0] = i + (j + 1) * 2 * N_r;
6871 cells[i + j * N_r].vertices[1] = (i + 1) % N_r + (j + 1) * 2 * N_r;
6872 cells[i + j * N_r].vertices[2] = i + j * 2 * N_r;
6873 cells[i + j * N_r].vertices[3] = (i + 1) % N_r + j * 2 * N_r;
6874
6875 cells[i + j * N_r].vertices[4] = N_r + i + (j + 1) * 2 * N_r;
6876 cells[i + j * N_r].vertices[5] =
6877 N_r + ((i + 1) % N_r) + (j + 1) * 2 * N_r;
6878 cells[i + j * N_r].vertices[6] = N_r + i + j * 2 * N_r;
6879 cells[i + j * N_r].vertices[7] = N_r + ((i + 1) % N_r) + j * 2 * N_r;
6880
6881 cells[i + j * N_r].material_id = 0;
6882 }
6883
6884 tria.create_triangulation(vertices_3d, cells, SubCellData());
6885 tria.set_all_manifold_ids(0);
6887
6888 if (!colorize)
6889 return;
6890
6891 // If colorize, set boundary id on the triangualtion.
6892 // Inner cylinder has boundary id 0
6893 // Outer cylinder has boundary id 1
6894 // Bottom (Z-) part of the cylinder has boundary id 2
6895 // Top (Z+) part of the cylinder has boundary id 3
6896
6897 // Define tolerance to help detect boundary conditions
6898 // First we define the tolerance along the z axis to identify
6899 // bottom and top cells.
6900 double eps_z = 1e-6 * length;
6901
6902 // Gather the inner radius from the faces instead of the argument, this is
6903 // more robust for some aspect ratios. First initialize the outer to 0 and
6904 // the inner to a large value
6905 double face_inner_radius = std::numeric_limits<double>::max();
6906 double face_outer_radius = 0.;
6907
6908 // Loop over the cells once to acquire the min and max radius at the face
6909 // centers. Otherwise, for some cell ratio, the center of the faces can be
6910 // at a radius which is significantly different from the one prescribed.
6911 for (const auto &cell : tria.active_cell_iterators())
6912 for (const unsigned int f : GeometryInfo<3>::face_indices())
6913 {
6914 if (!cell->face(f)->at_boundary())
6915 continue;
6916
6917 const auto face_center = cell->face(f)->center();
6918 const double z = face_center[2];
6919
6920 if ((std::fabs(z) > eps_z) &&
6921 (std::fabs(z - length) > eps_z)) // Not a zmin or zmax boundary
6922 {
6923 const double radius = std::sqrt(face_center[0] * face_center[0] +
6924 face_center[1] * face_center[1]);
6925 face_inner_radius = std::min(face_inner_radius, radius);
6926 face_outer_radius = std::max(face_outer_radius, radius);
6927 }
6928 }
6929
6930 double mid_radial_distance = 0.5 * (face_outer_radius - face_inner_radius);
6931
6932 for (const auto &cell : tria.active_cell_iterators())
6933 for (const unsigned int f : GeometryInfo<3>::face_indices())
6934 {
6935 if (cell->face(f)->at_boundary())
6936 {
6937 const auto face_center = cell->face(f)->center();
6938
6939 const double radius = std::sqrt(face_center[0] * face_center[0] +
6940 face_center[1] * face_center[1]);
6941
6942 const double z = face_center[2];
6943
6944 if (std::fabs(z) < eps_z) // z = 0 set boundary 2
6945 {
6946 cell->face(f)->set_boundary_id(2);
6947 }
6948 else if (std::fabs(z - length) <
6949 eps_z) // z = length set boundary 3
6950 {
6951 cell->face(f)->set_boundary_id(3);
6952 }
6953 else if (std::fabs(radius - face_inner_radius) >
6954 mid_radial_distance) // r = outer_radius set boundary 1
6955 {
6956 cell->face(f)->set_boundary_id(1);
6957 }
6958 else if (std::fabs(radius - face_inner_radius) <
6959 mid_radial_distance) // r = inner_radius set boundary 0
6960 {
6961 cell->face(f)->set_boundary_id(0);
6962 }
6963 else
6965 }
6966 }
6967 }
6968
6969
6970 template <int dim, int spacedim>
6971 void
6973 const std::vector<const Triangulation<dim, spacedim> *> &triangulations,
6975 const double duplicated_vertex_tolerance,
6976 const bool copy_manifold_ids,
6977 const bool copy_boundary_ids)
6978 {
6979 std::vector<Point<spacedim>> vertices;
6980 std::vector<CellData<dim>> cells;
6981 SubCellData subcell_data;
6982
6983 unsigned int n_accumulated_vertices = 0;
6984 for (const auto triangulation : triangulations)
6985 {
6986 Assert(triangulation->n_levels() == 1,
6987 ExcMessage("The input triangulations must be non-empty "
6988 "and must not be refined."));
6989
6990 auto [tria_vertices, tria_cells, tria_subcell_data] =
6992 Assert(tria_vertices.size() == triangulation->n_vertices(),
6994 Assert(tria_cells.size() == triangulation->n_cells(),
6996
6997 // Copy the vertices of the current triangulation into the merged list,
6998 // and then let the vertex indices of the cells refer to those in
6999 // the merged list:
7000 vertices.insert(vertices.end(),
7001 tria_vertices.begin(),
7002 tria_vertices.end());
7003 for (CellData<dim> &cell_data : tria_cells)
7004 {
7005 for (unsigned int &vertex_n : cell_data.vertices)
7006 vertex_n += n_accumulated_vertices;
7007 cells.push_back(cell_data);
7008 }
7009
7010 // Skip copying lines with no manifold information.
7011 if (copy_manifold_ids)
7012 {
7013 for (CellData<1> &line_data : tria_subcell_data.boundary_lines)
7014 {
7015 if (line_data.manifold_id == numbers::flat_manifold_id)
7016 continue;
7017 for (unsigned int &vertex_n : line_data.vertices)
7018 vertex_n += n_accumulated_vertices;
7019 line_data.boundary_id =
7021 subcell_data.boundary_lines.push_back(line_data);
7022 }
7023
7024 for (CellData<2> &quad_data : tria_subcell_data.boundary_quads)
7025 {
7026 if (quad_data.manifold_id == numbers::flat_manifold_id)
7027 continue;
7028 for (unsigned int &vertex_n : quad_data.vertices)
7029 vertex_n += n_accumulated_vertices;
7030 quad_data.boundary_id =
7032 subcell_data.boundary_quads.push_back(quad_data);
7033 }
7034 }
7035
7036 n_accumulated_vertices += triangulation->n_vertices();
7037 }
7038
7039 // throw out duplicated vertices
7040 std::vector<unsigned int> considered_vertices;
7042 cells,
7043 subcell_data,
7044 considered_vertices,
7045 duplicated_vertex_tolerance);
7046
7047 // reorder the cells to ensure that they satisfy the convention for
7048 // edge and face directions
7049 if (std::all_of(cells.begin(), cells.end(), [](const auto &cell) {
7050 return cell.vertices.size() ==
7051 ReferenceCells::get_hypercube<dim>().n_vertices();
7052 }))
7054 result.clear();
7055 result.create_triangulation(vertices, cells, subcell_data);
7056
7057 if (!copy_manifold_ids)
7059
7060 if (copy_boundary_ids)
7061 {
7062 auto result_cell = result.begin();
7063 for (const auto &tria : triangulations)
7064 {
7065 for (const auto &cell : tria->cell_iterators())
7066 {
7067 for (const auto &f : cell->face_indices())
7068 if (result_cell->face(f)->at_boundary())
7069 result_cell->face(f)->set_boundary_id(
7070 cell->face(f)->boundary_id());
7071 ++result_cell;
7072 }
7073 }
7074 }
7075
7076 Assert(duplicated_vertex_tolerance > 0.0 ||
7077 n_accumulated_vertices == result.n_vertices(),
7079 }
7080
7081
7082
7083 template <int dim, int spacedim>
7084 void
7086 const Triangulation<dim, spacedim> &triangulation_2,
7088 const double duplicated_vertex_tolerance,
7089 const bool copy_manifold_ids,
7090 const bool copy_boundary_ids)
7091 {
7092 // if either Triangulation is empty then merging is just a copy.
7093 if (triangulation_1.n_cells() == 0)
7094 {
7095 if (&result != &triangulation_2)
7096 result.copy_triangulation(triangulation_2);
7097 }
7098 else if (triangulation_2.n_cells() == 0)
7099 {
7100 if (&result != &triangulation_1)
7101 result.copy_triangulation(triangulation_1);
7102 }
7103 else
7104 merge_triangulations({&triangulation_1, &triangulation_2},
7105 result,
7106 duplicated_vertex_tolerance,
7107 copy_manifold_ids,
7108 copy_boundary_ids);
7109 }
7110
7111
7112
7113 namespace
7114 {
7136 template <int structdim>
7137 void
7138 delete_duplicated_objects(std::vector<CellData<structdim>> &subcell_data)
7139 {
7140 static_assert(structdim == 1 || structdim == 2,
7141 "This function is only implemented for lines and "
7142 "quadrilaterals.");
7143 // start by making sure that all objects representing the same vertices
7144 // are numbered in the same way by canonicalizing the numberings. This
7145 // makes it possible to detect duplicates.
7146 for (CellData<structdim> &cell_data : subcell_data)
7147 {
7148 if (structdim == 1)
7149 std::sort(std::begin(cell_data.vertices),
7150 std::end(cell_data.vertices));
7151 else if (structdim == 2)
7152 {
7153 // rotate the vertex numbers so that the lowest one is first
7154 std::array<unsigned int, 4> renumbering{};
7155 std::copy(std::begin(cell_data.vertices),
7156 std::end(cell_data.vertices),
7157 renumbering.begin());
7158
7159 // convert to old style vertex numbering. This makes the
7160 // permutations easy since the valid configurations are
7161 //
7162 // 3 2 2 1 1 0 0 3
7163 // 0 1 3 0 2 3 1 2
7164 // (0123) (3012) (2310) (1230)
7165 //
7166 // rather than the lexical ordering which is harder to permute
7167 // by rotation.
7168 std::swap(renumbering[2], renumbering[3]);
7169 std::rotate(renumbering.begin(),
7170 std::min_element(renumbering.begin(),
7171 renumbering.end()),
7172 renumbering.end());
7173 // convert to new style
7174 std::swap(renumbering[2], renumbering[3]);
7175 // deal with cases where we might have
7176 //
7177 // 3 2 1 2
7178 // 0 1 0 3
7179 //
7180 // by forcing the second vertex (in lexical ordering) to be
7181 // smaller than the third
7182 if (renumbering[1] > renumbering[2])
7183 std::swap(renumbering[1], renumbering[2]);
7184 std::copy(renumbering.begin(),
7185 renumbering.end(),
7186 std::begin(cell_data.vertices));
7187 }
7188 }
7189
7190 // Now that all cell objects have been canonicalized they can be sorted:
7191 auto compare = [](const CellData<structdim> &a,
7192 const CellData<structdim> &b) {
7193 return std::lexicographical_compare(std::begin(a.vertices),
7194 std::end(a.vertices),
7195 std::begin(b.vertices),
7196 std::end(b.vertices));
7197 };
7198 std::sort(subcell_data.begin(), subcell_data.end(), compare);
7199
7200 // Finally, determine which objects are duplicates. Duplicates are
7201 // assumed to be interior objects, so delete all but one and change the
7202 // boundary id:
7203 auto left = subcell_data.begin();
7204 while (left != subcell_data.end())
7205 {
7206 const auto right =
7207 std::upper_bound(left, subcell_data.end(), *left, compare);
7208 // if the range has more than one item, then there are duplicates -
7209 // set all boundary ids in the range to the internal boundary id
7210 if (left + 1 != right)
7211 for (auto it = left; it != right; ++it)
7212 {
7213 it->boundary_id = numbers::internal_face_boundary_id;
7214 Assert(it->manifold_id == left->manifold_id,
7215 ExcMessage(
7216 "In the process of grid generation a single "
7217 "line or quadrilateral has been assigned two "
7218 "different manifold ids. This can happen when "
7219 "a Triangulation is copied, e.g., via "
7220 "GridGenerator::replicate_triangulation() and "
7221 "not all external boundary faces have the same "
7222 "manifold id. Double check that all faces "
7223 "which you expect to be merged together have "
7224 "the same manifold id."));
7225 }
7226 left = right;
7227 }
7228
7229 subcell_data.erase(std::unique(subcell_data.begin(), subcell_data.end()),
7230 subcell_data.end());
7231 }
7232 } // namespace
7233
7234
7235
7236 template <int dim, int spacedim>
7237 void
7239 const std::vector<unsigned int> &extents,
7241 {
7242 AssertDimension(dim, extents.size());
7243 if constexpr (running_in_debug_mode())
7244 {
7245 for (const auto &extent : extents)
7246 Assert(0 < extent,
7247 ExcMessage(
7248 "The Triangulation must be copied at least one time in "
7249 "each coordinate dimension."));
7250 }
7251 const BoundingBox<spacedim> bbox(input.get_vertices());
7252 const auto &min = bbox.get_boundary_points().first;
7253 const auto &max = bbox.get_boundary_points().second;
7254
7255 std::array<Tensor<1, spacedim>, dim> offsets;
7256 for (unsigned int d = 0; d < dim; ++d)
7257 offsets[d][d] = max[d] - min[d];
7258
7259 Triangulation<dim, spacedim> tria_to_replicate;
7260 tria_to_replicate.copy_triangulation(input);
7261 for (unsigned int d = 0; d < dim; ++d)
7262 {
7263 auto [input_vertices, input_cell_data, input_subcell_data] =
7264 GridTools::get_coarse_mesh_description(tria_to_replicate);
7265
7266 std::vector<Point<spacedim>> output_vertices = input_vertices;
7267 std::vector<CellData<dim>> output_cell_data = input_cell_data;
7268 SubCellData output_subcell_data = input_subcell_data;
7269
7270 for (unsigned int k = 1; k < extents[d]; ++k)
7271 {
7272 const std::size_t vertex_offset = k * input_vertices.size();
7273 // vertices
7274 for (const Point<spacedim> &point : input_vertices)
7275 output_vertices.push_back(point + double(k) * offsets[d]);
7276 // cell data
7277 for (const CellData<dim> &cell_data : input_cell_data)
7278 {
7279 output_cell_data.push_back(cell_data);
7280 for (unsigned int &vertex : output_cell_data.back().vertices)
7281 vertex += vertex_offset;
7282 }
7283 // subcell data
7284 for (const CellData<1> &boundary_line :
7285 input_subcell_data.boundary_lines)
7286 {
7287 output_subcell_data.boundary_lines.push_back(boundary_line);
7288 for (unsigned int &vertex :
7289 output_subcell_data.boundary_lines.back().vertices)
7290 vertex += vertex_offset;
7291 }
7292 for (const CellData<2> &boundary_quad :
7293 input_subcell_data.boundary_quads)
7294 {
7295 output_subcell_data.boundary_quads.push_back(boundary_quad);
7296 for (unsigned int &vertex :
7297 output_subcell_data.boundary_quads.back().vertices)
7298 vertex += vertex_offset;
7299 }
7300 }
7301 // check all vertices: since the grid is coarse, most will be on the
7302 // boundary anyway
7303 std::vector<unsigned int> boundary_vertices;
7305 output_vertices,
7306 output_cell_data,
7307 output_subcell_data,
7308 boundary_vertices,
7309 1e-6 * input.begin_active()->diameter());
7310 // delete_duplicated_vertices also deletes any unused vertices
7311 // deal with any reordering issues created by delete_duplicated_vertices
7312 GridTools::consistently_order_cells(output_cell_data);
7313 // clean up the boundary ids of the boundary objects: note that we
7314 // have to do this after delete_duplicated_vertices so that boundary
7315 // objects are actually duplicated at this point
7316 if (dim == 2)
7317 delete_duplicated_objects(output_subcell_data.boundary_lines);
7318 else if (dim == 3)
7319 {
7320 delete_duplicated_objects(output_subcell_data.boundary_quads);
7321 for (CellData<1> &boundary_line :
7322 output_subcell_data.boundary_lines)
7323 // set boundary lines to the default value - let
7324 // create_triangulation figure out the rest.
7326 }
7327
7328 tria_to_replicate.clear();
7329 tria_to_replicate.create_triangulation(output_vertices,
7330 output_cell_data,
7331 output_subcell_data);
7332 }
7333
7334 result.copy_triangulation(tria_to_replicate);
7335 }
7336
7337
7338
7339 template <int dim, int spacedim>
7340 void
7342 const Triangulation<dim, spacedim> &triangulation_1,
7343 const Triangulation<dim, spacedim> &triangulation_2,
7345 {
7346 Assert(GridTools::have_same_coarse_mesh(triangulation_1, triangulation_2),
7347 ExcMessage("The two input triangulations are not derived from "
7348 "the same coarse mesh as required."));
7349 Assert((dynamic_cast<
7351 &triangulation_1) == nullptr) &&
7352 (dynamic_cast<
7354 &triangulation_2) == nullptr),
7355 ExcMessage("The source triangulations for this function must both "
7356 "be available entirely locally, and not be distributed "
7357 "triangulations."));
7358
7359 // first copy triangulation_1, and
7360 // then do as many iterations as
7361 // there are levels in
7362 // triangulation_2 to refine
7363 // additional cells. since this is
7364 // the maximum number of
7365 // refinements to get from the
7366 // coarse grid to triangulation_2,
7367 // it is clear that this is also
7368 // the maximum number of
7369 // refinements to get from any cell
7370 // on triangulation_1 to
7371 // triangulation_2
7372 result.clear();
7373 result.copy_triangulation(triangulation_1);
7374 for (unsigned int iteration = 0; iteration < triangulation_2.n_levels();
7375 ++iteration)
7376 {
7378 intergrid_map.make_mapping(result, triangulation_2);
7379
7380 bool any_cell_flagged = false;
7381 for (const auto &result_cell : result.active_cell_iterators())
7382 if (intergrid_map[result_cell]->has_children())
7383 {
7384 any_cell_flagged = true;
7385 result_cell->set_refine_flag();
7386 }
7387
7388 if (any_cell_flagged == false)
7389 break;
7390 else
7392 }
7393 }
7394
7395
7396
7397 template <int dim, int spacedim>
7398 void
7400 const Triangulation<dim, spacedim> &input_triangulation,
7402 &cells_to_remove,
7404 {
7405 // simply copy the vertices; we will later strip those
7406 // that turn out to be unused
7407 std::vector<Point<spacedim>> vertices = input_triangulation.get_vertices();
7408
7409 // the loop through the cells and copy stuff, excluding
7410 // the ones we are to remove
7411 std::vector<CellData<dim>> cells;
7412 for (const auto &cell : input_triangulation.active_cell_iterators())
7413 if (cells_to_remove.find(cell) == cells_to_remove.end())
7414 {
7415 Assert(static_cast<unsigned int>(cell->level()) ==
7416 input_triangulation.n_levels() - 1,
7417 ExcMessage(
7418 "Your input triangulation appears to have "
7419 "adaptively refined cells. This is not allowed. You can "
7420 "only call this function on a triangulation in which "
7421 "all cells are on the same refinement level."));
7422
7423 CellData<dim> this_cell;
7424 for (const unsigned int v : GeometryInfo<dim>::vertex_indices())
7425 this_cell.vertices[v] = cell->vertex_index(v);
7426 this_cell.material_id = cell->material_id();
7427 cells.push_back(this_cell);
7428 }
7429
7430 // throw out duplicated vertices from the two meshes, reorder vertices as
7431 // necessary and create the triangulation
7432 SubCellData subcell_data;
7433 std::vector<unsigned int> considered_vertices;
7435 cells,
7436 subcell_data,
7437 considered_vertices);
7438
7439 // then clear the old triangulation and create the new one
7440 result.clear();
7441 result.create_triangulation(vertices, cells, subcell_data);
7442 }
7443
7444
7445
7446 void
7448 const Triangulation<2, 2> &input,
7449 const unsigned int n_slices,
7450 const double height,
7451 Triangulation<3, 3> &result,
7452 const bool copy_manifold_ids,
7453 const std::vector<types::manifold_id> &manifold_priorities)
7454 {
7455 Assert(input.n_levels() == 1,
7456 ExcMessage(
7457 "The input triangulation must be a coarse mesh, i.e., it must "
7458 "not have been refined."));
7459 Assert(result.n_cells() == 0,
7460 ExcMessage("The output triangulation object needs to be empty."));
7461 Assert(height > 0,
7462 ExcMessage("The given height for extrusion must be positive."));
7463 Assert(n_slices >= 2,
7464 ExcMessage(
7465 "The number of slices for extrusion must be at least 2."));
7466
7467 const double delta_h = height / (n_slices - 1);
7468 std::vector<double> slices_z_values;
7469 slices_z_values.reserve(n_slices);
7470 for (unsigned int i = 0; i < n_slices; ++i)
7471 slices_z_values.push_back(i * delta_h);
7473 input, slices_z_values, result, copy_manifold_ids, manifold_priorities);
7474 }
7475
7476
7477
7478 void
7480 const Triangulation<2, 2> &input,
7481 const unsigned int n_slices,
7482 const double height,
7483 Triangulation<2, 2> &result,
7484 const bool copy_manifold_ids,
7485 const std::vector<types::manifold_id> &manifold_priorities)
7486 {
7487 (void)input;
7488 (void)n_slices;
7489 (void)height;
7490 (void)result;
7491 (void)copy_manifold_ids;
7492 (void)manifold_priorities;
7493
7494 AssertThrow(false,
7495 ExcMessage(
7496 "GridTools::extrude_triangulation() is only available "
7497 "for Triangulation<3, 3> as output triangulation."));
7498 }
7499
7500
7501
7502 void
7504 const Triangulation<2, 2> &input,
7505 const std::vector<double> &slice_coordinates,
7506 Triangulation<3, 3> &result,
7507 const bool copy_manifold_ids,
7508 const std::vector<types::manifold_id> &manifold_priorities)
7509 {
7510 Assert(input.n_levels() == 1,
7511 ExcMessage(
7512 "The input triangulation must be a coarse mesh, i.e., it must "
7513 "not have been refined."));
7514 Assert(result.n_cells() == 0,
7515 ExcMessage("The output triangulation object needs to be empty."));
7516 Assert(slice_coordinates.size() >= 2,
7517 ExcMessage(
7518 "The number of slices for extrusion must be at least 2."));
7519 Assert(std::is_sorted(slice_coordinates.begin(), slice_coordinates.end()),
7520 ExcMessage("Slice z-coordinates should be in ascending order"));
7522 ExcMessage(
7523 "This function is only implemented for quadrilateral meshes."));
7524
7525 const auto priorities = [&]() -> std::vector<types::manifold_id> {
7526 // if a non-empty (i.e., not the default) vector is given for
7527 // manifold_priorities then use it (but check its validity in debug
7528 // mode)
7529 if (0 < manifold_priorities.size())
7530 {
7531 if constexpr (running_in_debug_mode())
7532 {
7533 // check that the provided manifold_priorities is valid
7534 std::vector<types::manifold_id> sorted_manifold_priorities =
7535 manifold_priorities;
7536 std::sort(sorted_manifold_priorities.begin(),
7537 sorted_manifold_priorities.end());
7538 Assert(std::unique(sorted_manifold_priorities.begin(),
7539 sorted_manifold_priorities.end()) ==
7540 sorted_manifold_priorities.end(),
7541 ExcMessage(
7542 "The given vector of manifold ids may not contain any "
7543 "duplicated entries."));
7544 std::vector<types::manifold_id> sorted_manifold_ids =
7545 input.get_manifold_ids();
7546 std::sort(sorted_manifold_ids.begin(), sorted_manifold_ids.end());
7547 if (sorted_manifold_priorities != sorted_manifold_ids)
7548 {
7549 std::ostringstream message;
7550 message << "The given triangulation has manifold ids {";
7551 for (const types::manifold_id manifold_id :
7552 sorted_manifold_ids)
7553 if (manifold_id != sorted_manifold_ids.back())
7554 message << manifold_id << ", ";
7555 message << sorted_manifold_ids.back() << "}, but \n"
7556 << " the given vector of manifold ids is {";
7557 for (const types::manifold_id manifold_id :
7558 manifold_priorities)
7559 if (manifold_id != manifold_priorities.back())
7560 message << manifold_id << ", ";
7561 message
7562 << manifold_priorities.back() << "}.\n"
7563 << " These vectors should contain the same elements.\n";
7564 const std::string m = message.str();
7565 Assert(false, ExcMessage(m));
7566 }
7567 }
7568 return manifold_priorities;
7569 }
7570 // otherwise use the default ranking: ascending order, but TFI manifolds
7571 // are at the end.
7572 std::vector<types::manifold_id> default_priorities =
7573 input.get_manifold_ids();
7574 const auto first_tfi_it = std::partition(
7575 default_priorities.begin(),
7576 default_priorities.end(),
7577 [&input](const types::manifold_id &id) {
7578 return dynamic_cast<const TransfiniteInterpolationManifold<2, 2> *>(
7579 &input.get_manifold(id)) == nullptr;
7580 });
7581 std::sort(default_priorities.begin(), first_tfi_it);
7582 std::sort(first_tfi_it, default_priorities.end());
7583
7584 return default_priorities;
7585 }();
7586
7587 const std::size_t n_slices = slice_coordinates.size();
7588 std::vector<Point<3>> points(n_slices * input.n_vertices());
7589 std::vector<CellData<3>> cells;
7590 cells.reserve((n_slices - 1) * input.n_active_cells());
7591
7592 // copy the array of points as many times as there will be slices,
7593 // one slice at a time. The z-axis value are defined in slices_coordinates
7594 for (std::size_t slice_n = 0; slice_n < n_slices; ++slice_n)
7595 {
7596 for (std::size_t vertex_n = 0; vertex_n < input.n_vertices();
7597 ++vertex_n)
7598 {
7599 const Point<2> vertex = input.get_vertices()[vertex_n];
7600 points[slice_n * input.n_vertices() + vertex_n] =
7601 Point<3>(vertex[0], vertex[1], slice_coordinates[slice_n]);
7602 }
7603 }
7604
7605 // then create the cells of each of the slices, one stack at a
7606 // time
7607 for (const auto &cell : input.active_cell_iterators())
7608 {
7609 for (std::size_t slice_n = 0; slice_n < n_slices - 1; ++slice_n)
7610 {
7611 CellData<3> this_cell;
7612 for (const unsigned int vertex_n :
7614 {
7615 this_cell.vertices[vertex_n] =
7616 cell->vertex_index(vertex_n) + slice_n * input.n_vertices();
7617 this_cell
7619 cell->vertex_index(vertex_n) +
7620 (slice_n + 1) * input.n_vertices();
7621 }
7622
7623 this_cell.material_id = cell->material_id();
7624 if (copy_manifold_ids)
7625 this_cell.manifold_id = cell->manifold_id();
7626 cells.push_back(this_cell);
7627 }
7628 }
7629
7630 // Next, create face data for all faces that are orthogonal to the x-y
7631 // plane
7632 SubCellData subcell_data;
7633 std::vector<CellData<2>> &quads = subcell_data.boundary_quads;
7634 types::boundary_id max_boundary_id = 0;
7635 quads.reserve(input.n_active_lines() * (n_slices - 1) +
7636 input.n_active_cells() * 2);
7637 for (const auto &face : input.active_face_iterators())
7638 {
7639 CellData<2> quad;
7640 quad.boundary_id = face->boundary_id();
7641 if (face->at_boundary())
7642 max_boundary_id = std::max(max_boundary_id, quad.boundary_id);
7643 if (copy_manifold_ids)
7644 quad.manifold_id = face->manifold_id();
7645 for (std::size_t slice_n = 0; slice_n < n_slices - 1; ++slice_n)
7646 {
7647 quad.vertices[0] =
7648 face->vertex_index(0) + slice_n * input.n_vertices();
7649 quad.vertices[1] =
7650 face->vertex_index(1) + slice_n * input.n_vertices();
7651 quad.vertices[2] =
7652 face->vertex_index(0) + (slice_n + 1) * input.n_vertices();
7653 quad.vertices[3] =
7654 face->vertex_index(1) + (slice_n + 1) * input.n_vertices();
7655 quads.push_back(quad);
7656 }
7657 }
7658
7659 // if necessary, create face data for faces parallel to the x-y
7660 // plane. This is only necessary if we need to set manifolds.
7661 if (copy_manifold_ids)
7662 for (const auto &cell : input.active_cell_iterators())
7663 {
7664 CellData<2> quad;
7666 quad.manifold_id = cell->manifold_id(); // check is outside loop
7667 for (std::size_t slice_n = 1; slice_n < n_slices - 1; ++slice_n)
7668 {
7669 quad.vertices[0] =
7670 cell->vertex_index(0) + slice_n * input.n_vertices();
7671 quad.vertices[1] =
7672 cell->vertex_index(1) + slice_n * input.n_vertices();
7673 quad.vertices[2] =
7674 cell->vertex_index(2) + slice_n * input.n_vertices();
7675 quad.vertices[3] =
7676 cell->vertex_index(3) + slice_n * input.n_vertices();
7677 quads.push_back(quad);
7678 }
7679 }
7680
7681 // then mark the bottom and top boundaries of the extruded mesh
7682 // with max_boundary_id+1 and max_boundary_id+2. check that this
7683 // remains valid
7684 Assert((max_boundary_id != numbers::invalid_boundary_id) &&
7685 (max_boundary_id + 1 != numbers::invalid_boundary_id) &&
7686 (max_boundary_id + 2 != numbers::invalid_boundary_id),
7687 ExcMessage(
7688 "The input triangulation to this function is using boundary "
7689 "indicators in a range that do not allow using "
7690 "max_boundary_id+1 and max_boundary_id+2 as boundary "
7691 "indicators for the bottom and top faces of the "
7692 "extruded triangulation."));
7693 const types::boundary_id bottom_boundary_id = max_boundary_id + 1;
7694 const types::boundary_id top_boundary_id = max_boundary_id + 2;
7695 for (const auto &cell : input.active_cell_iterators())
7696 {
7697 CellData<2> quad;
7698 quad.boundary_id = bottom_boundary_id;
7699 quad.vertices[0] = cell->vertex_index(0);
7700 quad.vertices[1] = cell->vertex_index(1);
7701 quad.vertices[2] = cell->vertex_index(2);
7702 quad.vertices[3] = cell->vertex_index(3);
7703 if (copy_manifold_ids)
7704 quad.manifold_id = cell->manifold_id();
7705 quads.push_back(quad);
7706
7707 quad.boundary_id = top_boundary_id;
7708 for (unsigned int &vertex : quad.vertices)
7709 vertex += (n_slices - 1) * input.n_vertices();
7710 if (copy_manifold_ids)
7711 quad.manifold_id = cell->manifold_id();
7712 quads.push_back(quad);
7713 }
7714
7715 // use all of this to finally create the extruded 3d
7716 // triangulation. it is not necessary to call
7717 // GridTools::consistently_order_cells() because the cells we have
7718 // constructed above are automatically correctly oriented. this is
7719 // because the 2d base mesh is always correctly oriented, and
7720 // extruding it automatically yields a correctly oriented 3d mesh,
7721 // as discussed in the edge orientation paper mentioned in the
7722 // introduction to the @ref reordering "reordering module".
7723 result.create_triangulation(points, cells, subcell_data);
7724
7725 for (auto manifold_id_it = priorities.rbegin();
7726 manifold_id_it != priorities.rend();
7727 ++manifold_id_it)
7728 for (const auto &face : result.active_face_iterators())
7729 if (face->manifold_id() == *manifold_id_it)
7730 for (unsigned int line_n = 0;
7731 line_n < GeometryInfo<3>::lines_per_face;
7732 ++line_n)
7733 face->line(line_n)->set_manifold_id(*manifold_id_it);
7734 }
7735
7736
7737
7738 void
7740 const Triangulation<2, 2> &input,
7741 const std::vector<double> &slice_coordinates,
7742 Triangulation<2, 2> &result,
7743 const bool copy_manifold_ids,
7744 const std::vector<types::manifold_id> &manifold_priorities)
7745 {
7746 (void)input;
7747 (void)slice_coordinates;
7748 (void)result;
7749 (void)copy_manifold_ids;
7750 (void)manifold_priorities;
7751
7752 AssertThrow(false,
7753 ExcMessage(
7754 "GridTools::extrude_triangulation() is only available "
7755 "for Triangulation<3, 3> as output triangulation."));
7756 }
7757
7758
7759
7760 template <>
7761 void
7763 const double,
7764 const double,
7765 const double,
7766 const unsigned int,
7767 const bool)
7768 {
7770 }
7771
7772
7773
7774 template <int spacedim>
7775 void
7776 hyper_cube_with_cylindrical_hole_2D(Triangulation<2, spacedim> &triangulation,
7777 const double inner_radius,
7778 const double outer_radius,
7779 const double, // width,
7780 const unsigned int, // width_repetition,
7781 const bool colorize)
7782 {
7783 const int dim = 2;
7784
7785 Assert(inner_radius < outer_radius,
7786 ExcMessage("outer_radius has to be bigger than inner_radius."));
7787
7788 const Point<spacedim> center;
7789
7790 // We create a hyper_shell (i.e., an annulus) in two dimensions, and then we
7791 // modify it by pulling the vertices on the diagonals out to where the
7792 // corners of a square would be:
7793 hyper_shell(triangulation, center, inner_radius, outer_radius, 8);
7795 std::vector<bool> treated_vertices(triangulation.n_vertices(), false);
7796 for (const auto &cell : triangulation.active_cell_iterators())
7797 {
7798 for (auto f : GeometryInfo<dim>::face_indices())
7799 if (cell->face(f)->at_boundary())
7800 for (const unsigned int v : cell->face(f)->vertex_indices())
7801 if (/* is the vertex on the outer ring? */
7802 (std::fabs(cell->face(f)->vertex(v).norm() - outer_radius) <
7803 1e-12 * outer_radius)
7804 /* and */
7805 &&
7806 /* is the vertex on one of the two diagonals? */
7807 (std::fabs(std::fabs(cell->face(f)->vertex(v)[0]) -
7808 std::fabs(cell->face(f)->vertex(v)[1])) <
7809 1e-12 * outer_radius))
7810 cell->face(f)->vertex(v) *= std::sqrt(2.);
7811 }
7812 const double eps = 1e-3 * outer_radius;
7813 for (const auto &cell : triangulation.active_cell_iterators())
7814 {
7815 for (const unsigned int f : cell->face_indices())
7816 if (cell->face(f)->at_boundary())
7817 {
7818 const double dx = cell->face(f)->center()[0] - center[0];
7819 const double dy = cell->face(f)->center()[1] - center[1];
7820 if (colorize)
7821 {
7822 if (std::abs(dx + outer_radius) < eps)
7823 cell->face(f)->set_boundary_id(0);
7824 else if (std::abs(dx - outer_radius) < eps)
7825 cell->face(f)->set_boundary_id(1);
7826 else if (std::abs(dy + outer_radius) < eps)
7827 cell->face(f)->set_boundary_id(2);
7828 else if (std::abs(dy - outer_radius) < eps)
7829 cell->face(f)->set_boundary_id(3);
7830 else
7831 {
7832 cell->face(f)->set_boundary_id(4);
7833 cell->face(f)->set_manifold_id(0);
7834 }
7835 }
7836 else
7837 {
7838 const double d = (cell->face(f)->center() - center).norm();
7839 if (d - inner_radius < 0)
7840 {
7841 cell->face(f)->set_boundary_id(1);
7842 cell->face(f)->set_manifold_id(0);
7843 }
7844 else
7845 cell->face(f)->set_boundary_id(0);
7846 }
7847 }
7848 }
7849 triangulation.set_manifold(0, PolarManifold<2, spacedim>(center));
7850 }
7851
7852
7853
7854 template <>
7855 void
7857 const double inner_radius,
7858 const double outer_radius,
7859 const double width,
7860 const unsigned int width_repetition,
7861 const bool colorize)
7862 {
7863 hyper_cube_with_cylindrical_hole_2D(triangulation,
7864 inner_radius,
7866 width,
7867 width_repetition,
7868 colorize);
7869 }
7870
7871
7872
7873 template <>
7874 void
7876 const double inner_radius,
7877 const double outer_radius,
7878 const double width,
7879 const unsigned int width_repetition,
7880 const bool colorize)
7881 {
7882 hyper_cube_with_cylindrical_hole_2D(triangulation,
7883 inner_radius,
7885 width,
7886 width_repetition,
7887 colorize);
7888 }
7889
7890
7891
7892 template <int dim>
7893 void
7895 const Point<dim> &center,
7896 const double inner_radius,
7897 const double outer_radius,
7898 const unsigned int n_shells,
7899 const double skewness,
7900 const unsigned int n_cells,
7901 const bool colorize)
7902 {
7903 Assert(dim == 2 || dim == 3, ExcNotImplemented());
7904 Assert(inner_radius < outer_radius,
7905 ExcMessage("outer_radius has to be bigger than inner_radius."));
7906 if (n_shells == 0)
7907 return; // empty Triangulation
7908
7909 std::vector<double> radii;
7910 radii.push_back(inner_radius);
7911 for (unsigned int shell_n = 1; shell_n < n_shells; ++shell_n)
7912 if (skewness == 0.0)
7913 // same as below, but works in the limiting case of zero skewness
7914 radii.push_back(inner_radius +
7915 (outer_radius - inner_radius) *
7916 (1.0 - (1.0 - double(shell_n) / n_shells)));
7917 else
7918 radii.push_back(
7919 inner_radius +
7920 (outer_radius - inner_radius) *
7921 (1.0 - std::tanh(skewness * (1.0 - double(shell_n) / n_shells)) /
7922 std::tanh(skewness)));
7923 radii.push_back(outer_radius);
7924
7925 double grid_vertex_tolerance = 0.0;
7926 for (unsigned int shell_n = 0; shell_n < radii.size() - 1; ++shell_n)
7927 {
7928 Triangulation<dim> current_shell;
7929 GridGenerator::hyper_shell(current_shell,
7930 center,
7931 radii[shell_n],
7932 radii[shell_n + 1],
7933 n_cells == 0 ? (dim == 2 ? 8 : 12) :
7934 n_cells);
7935
7936 // The innermost shell has the smallest cells: use that to set the
7937 // vertex merging tolerance
7938 if (grid_vertex_tolerance == 0.0)
7939 grid_vertex_tolerance =
7940 0.5 * internal::minimal_vertex_distance(current_shell);
7941
7942 Triangulation<dim> temp(std::move(triangulation));
7943 triangulation.clear();
7945 temp,
7946 triangulation,
7947 grid_vertex_tolerance);
7948 }
7949
7951 triangulation.set_all_manifold_ids(manifold_id);
7952 if (dim == 2)
7953 triangulation.set_manifold(manifold_id, PolarManifold<dim>(center));
7954 else if (dim == 3)
7955 triangulation.set_manifold(manifold_id, SphericalManifold<dim>(center));
7956
7957 // We use boundary vertex positions to see if things are on the inner or
7958 // outer boundary.
7959 constexpr double radial_vertex_tolerance =
7960 100.0 * std::numeric_limits<double>::epsilon();
7961 auto assert_vertex_distance_within_tolerance =
7962 [center, radial_vertex_tolerance](
7963 const TriaIterator<TriaAccessor<dim - 1, dim, dim>> &face,
7964 const double radius) {
7965 (void)center;
7966 (void)radial_vertex_tolerance;
7967 for (unsigned int vertex_n = 0;
7968 vertex_n < GeometryInfo<dim>::vertices_per_face;
7969 ++vertex_n)
7970 {
7971 Assert(std::abs((face->vertex(vertex_n) - center).norm() - radius) <
7972 (center.norm() + radius) * radial_vertex_tolerance,
7974 }
7975 };
7976 if (colorize)
7977 for (const auto &cell : triangulation.active_cell_iterators())
7978 for (const unsigned int face_n : GeometryInfo<dim>::face_indices())
7979 {
7980 auto face = cell->face(face_n);
7981 if (face->at_boundary())
7982 {
7983 if (((face->vertex(0) - center).norm() - inner_radius) <
7984 (center.norm() + inner_radius) * radial_vertex_tolerance)
7985 {
7986 // we must be at an inner face, but check
7987 assert_vertex_distance_within_tolerance(face, inner_radius);
7988 face->set_all_boundary_ids(0);
7989 }
7990 else
7991 {
7992 // we must be at an outer face, but check
7993 assert_vertex_distance_within_tolerance(face, outer_radius);
7994 face->set_all_boundary_ids(1);
7995 }
7996 }
7997 }
7998 }
7999
8000
8001
8002 template <>
8003 void
8005 const double inner_radius,
8006 const double outer_radius,
8007 const double L,
8008 const unsigned int Nz,
8009 const bool colorize)
8010 {
8011 const int dim = 3;
8012
8013 Assert(inner_radius < outer_radius,
8014 ExcMessage("outer_radius has to be bigger than inner_radius."));
8015 Assert(L > 0, ExcMessage("Must give positive extension L"));
8016 Assert(Nz >= 1, ExcLowerRange(1, Nz));
8017
8018 // Start with a cylinder shell with the correct inner and outer radius
8019 // and as many layers as requested
8020 cylinder_shell(triangulation, L, inner_radius, outer_radius, 8, Nz, false);
8022
8023 // Then loop over all vertices that are at the boundary (by looping
8024 // over all cells, their faces, and if the face is at the boundary,
8025 // their vertices. If we haven't touched that vertex yet, see if
8026 // we need to move it from its cylinder mantle position to the
8027 // outer boundary of the box.
8028 std::vector<bool> treated_vertices(triangulation.n_vertices(), false);
8029 for (const auto &cell : triangulation.active_cell_iterators())
8030 {
8031 for (const auto f : cell->face_indices())
8032 if (cell->face(f)->at_boundary())
8033 {
8034 for (const unsigned int v : cell->face(f)->vertex_indices())
8035 {
8036 const unsigned int vv = cell->face(f)->vertex_index(v);
8037 if (treated_vertices[vv] == false)
8038 {
8039 treated_vertices[vv] = true;
8040
8041 // The vertices we have to treat are the ones that
8042 // have x=y or x=-y and are at the outer ring -- that is,
8043 // they are on the diagonal in the x-y plane and radius
8044 // equal to outer_radius. These need to be pulled out to
8045 // the corner point of the square, i.e., their x and y
8046 // coordinates need to be multiplied by sqrt(2),
8047 // whereas the z coordinate remains unchanged:
8048 const Point<dim> vertex_location =
8049 cell->face(f)->vertex(v);
8050 if ((std::fabs(std::fabs(vertex_location[0]) -
8051 std::fabs(vertex_location[1])) <
8052 1e-12 * outer_radius) &&
8053 (std::fabs(vertex_location[0] * vertex_location[0] +
8054 vertex_location[1] * vertex_location[1] -
8056 1e-12 * outer_radius))
8057 cell->face(f)->vertex(v) =
8058 Point<3>(vertex_location[0] * std::sqrt(2.0),
8059 vertex_location[1] * std::sqrt(2.0),
8060 vertex_location[2]);
8061 }
8062 }
8063 }
8064 }
8065 double eps = 1e-3 * outer_radius;
8066 for (const auto &cell : triangulation.active_cell_iterators())
8067 {
8068 for (const unsigned int f : cell->face_indices())
8069 if (cell->face(f)->at_boundary())
8070 {
8071 const double dx = cell->face(f)->center()[0];
8072 const double dy = cell->face(f)->center()[1];
8073 const double dz = cell->face(f)->center()[2];
8074
8075 if (colorize)
8076 {
8077 if (std::abs(dx + outer_radius) < eps)
8078 cell->face(f)->set_boundary_id(0);
8079
8080 else if (std::abs(dx - outer_radius) < eps)
8081 cell->face(f)->set_boundary_id(1);
8082
8083 else if (std::abs(dy + outer_radius) < eps)
8084 cell->face(f)->set_boundary_id(2);
8085
8086 else if (std::abs(dy - outer_radius) < eps)
8087 cell->face(f)->set_boundary_id(3);
8088
8089 else if (std::abs(dz) < eps)
8090 cell->face(f)->set_boundary_id(4);
8091
8092 else if (std::abs(dz - L) < eps)
8093 cell->face(f)->set_boundary_id(5);
8094
8095 else
8096 {
8097 cell->face(f)->set_all_boundary_ids(6);
8098 cell->face(f)->set_all_manifold_ids(0);
8099 }
8100 }
8101 else
8102 {
8103 Point<dim> c = cell->face(f)->center();
8104 c[2] = 0;
8105 const double d = c.norm();
8106 if (d - inner_radius < 0)
8107 {
8108 cell->face(f)->set_all_boundary_ids(1);
8109 cell->face(f)->set_all_manifold_ids(0);
8110 }
8111 else
8112 cell->face(f)->set_boundary_id(0);
8113 }
8114 }
8115 }
8116 triangulation.set_manifold(0, CylindricalManifold<3>(2));
8117 }
8118
8119
8120
8121 template <int dim, int spacedim1, int spacedim2>
8122 void
8125 {
8126 Assert((dynamic_cast<
8128 &in_tria) == nullptr),
8129 ExcMessage(
8130 "This function cannot be used on "
8131 "parallel::distributed::Triangulation objects as inputs."));
8132 Assert(in_tria.has_hanging_nodes() == false,
8133 ExcMessage("This function does not work for meshes that have "
8134 "hanging nodes."));
8135
8136
8137 const unsigned int spacedim = std::min(spacedim1, spacedim2);
8138 const std::vector<Point<spacedim1>> &in_vertices = in_tria.get_vertices();
8139
8140 // Create an array of vertices, with components either truncated
8141 // or extended by zeroes.
8142 std::vector<Point<spacedim2>> v(in_vertices.size());
8143 for (unsigned int i = 0; i < in_vertices.size(); ++i)
8144 for (unsigned int d = 0; d < spacedim; ++d)
8145 v[i][d] = in_vertices[i][d];
8146
8147 std::vector<CellData<dim>> cells(in_tria.n_active_cells());
8148 for (const auto &cell : in_tria.active_cell_iterators())
8149 {
8150 const unsigned int id = cell->active_cell_index();
8151
8152 cells[id].vertices.resize(cell->n_vertices());
8153 for (const auto i : cell->vertex_indices())
8154 cells[id].vertices[i] = cell->vertex_index(i);
8155 cells[id].material_id = cell->material_id();
8156 cells[id].manifold_id = cell->manifold_id();
8157 }
8158
8159 SubCellData subcelldata;
8160 switch (dim)
8161 {
8162 case 1:
8163 {
8164 // Nothing to do in 1d
8165 break;
8166 }
8167
8168 case 2:
8169 {
8170 std::vector<bool> user_flags_line;
8171 in_tria.save_user_flags_line(user_flags_line);
8172 const_cast<Triangulation<dim, spacedim1> &>(in_tria)
8173 .clear_user_flags_line();
8174
8175 // Loop over all the faces of the triangulation and create
8176 // objects that describe their boundary and manifold ids.
8177 for (const auto &face : in_tria.active_face_iterators())
8178 {
8179 if (face->at_boundary())
8180 {
8181 CellData<1> boundary_line;
8182
8183 boundary_line.vertices.resize(face->n_vertices());
8184 for (const auto i : face->vertex_indices())
8185 boundary_line.vertices[i] = face->vertex_index(i);
8186 boundary_line.boundary_id = face->boundary_id();
8187 boundary_line.manifold_id = face->manifold_id();
8188
8189 subcelldata.boundary_lines.emplace_back(
8190 std::move(boundary_line));
8191 }
8192 else
8193 // The face is not at the boundary. We won't have to set
8194 // boundary_ids (that is not possible for interior faces), but
8195 // we need to do something if the manifold-id is not the
8196 // default.
8197 //
8198 // We keep track via the user flags whether we have already
8199 // dealt with a face or not. (We need to do that here because
8200 // we will return to interior faces twice, once for each
8201 // neighbor, whereas we only touch each of the boundary faces
8202 // above once.)
8203 if ((face->user_flag_set() == false) &&
8204 (face->manifold_id() != numbers::flat_manifold_id))
8205 {
8206 CellData<1> boundary_line;
8207
8208 boundary_line.vertices.resize(face->n_vertices());
8209 for (const auto i : face->vertex_indices())
8210 boundary_line.vertices[i] = face->vertex_index(i);
8211 boundary_line.boundary_id =
8213 boundary_line.manifold_id = face->manifold_id();
8214
8215 subcelldata.boundary_lines.emplace_back(
8216 std::move(boundary_line));
8217
8218 face->set_user_flag();
8219 }
8220 }
8221
8222 // Reset the user flags to their previous values:
8223 const_cast<Triangulation<dim, spacedim1> &>(in_tria)
8224 .load_user_flags_line(user_flags_line);
8225
8226 break;
8227 }
8228
8229 case 3:
8230 {
8231 std::vector<bool> user_flags_line;
8232 in_tria.save_user_flags_line(user_flags_line);
8233 const_cast<Triangulation<dim, spacedim1> &>(in_tria)
8234 .clear_user_flags_line();
8235
8236 std::vector<bool> user_flags_quad;
8237 in_tria.save_user_flags_quad(user_flags_quad);
8238 const_cast<Triangulation<dim, spacedim1> &>(in_tria)
8239 .clear_user_flags_quad();
8240
8241 // Loop over all the faces of the triangulation and create
8242 // objects that describe their boundary and manifold ids.
8243 for (const auto &face : in_tria.active_face_iterators())
8244 {
8245 if (face->at_boundary())
8246 {
8247 CellData<2> boundary_face;
8248
8249 boundary_face.vertices.resize(face->n_vertices());
8250 for (const auto i : face->vertex_indices())
8251 boundary_face.vertices[i] = face->vertex_index(i);
8252 boundary_face.boundary_id = face->boundary_id();
8253 boundary_face.manifold_id = face->manifold_id();
8254
8255 subcelldata.boundary_quads.emplace_back(
8256 std::move(boundary_face));
8257
8258 // Then also loop over the edges and do the same. We would
8259 // accidentally create duplicates for edges that are part of
8260 // two boundary faces. To avoid this, use the user_flag on
8261 // edges to mark those that we have already visited. (Note
8262 // how we save and restore those above and below.)
8263 for (unsigned int e = 0; e < face->n_lines(); ++e)
8264 if (face->line(e)->user_flag_set() == false)
8265 {
8266 const typename Triangulation<dim,
8267 spacedim1>::line_iterator
8268 edge = face->line(e);
8269 CellData<1> boundary_edge;
8270
8271 boundary_edge.vertices.resize(edge->n_vertices());
8272 for (const auto i : edge->vertex_indices())
8273 boundary_edge.vertices[i] = edge->vertex_index(i);
8274 boundary_edge.boundary_id = edge->boundary_id();
8275 boundary_edge.manifold_id = edge->manifold_id();
8276
8277 subcelldata.boundary_lines.emplace_back(
8278 std::move(boundary_edge));
8279
8280 edge->set_user_flag();
8281 }
8282 }
8283 else
8284 // The face is not at the boundary. We won't have to set
8285 // boundary_ids (that is not possible for interior faces), but
8286 // we need to do something if the manifold-id is not the
8287 // default.
8288 //
8289 // We keep track via the user flags whether we have already
8290 // dealt with a face or not. (We need to do that here because
8291 // we will return to interior faces twice, once for each
8292 // neighbor, whereas we only touch each of the boundary faces
8293 // above once.)
8294 //
8295 // Note that if we have already dealt with a face, then we
8296 // have also already dealt with the edges and don't have
8297 // to worry about that any more separately.
8298 if (face->user_flag_set() == false)
8299 {
8300 if (face->manifold_id() != numbers::flat_manifold_id)
8301 {
8302 CellData<2> boundary_face;
8303
8304 boundary_face.vertices.resize(face->n_vertices());
8305 for (const auto i : face->vertex_indices())
8306 boundary_face.vertices[i] = face->vertex_index(i);
8307 boundary_face.boundary_id =
8309 boundary_face.manifold_id = face->manifold_id();
8310
8311 subcelldata.boundary_quads.emplace_back(
8312 std::move(boundary_face));
8313
8314 face->set_user_flag();
8315 }
8316
8317 // Then also loop over the edges of this face. Because
8318 // every boundary edge must also be a part of a boundary
8319 // face, we can ignore these. But it is possible that we
8320 // have already encountered an interior edge through a
8321 // previous face, and in that case we have to just ignore
8322 // it
8323 for (unsigned int e = 0; e < face->n_lines(); ++e)
8324 if (face->line(e)->at_boundary() == false)
8325 if (face->line(e)->user_flag_set() == false)
8326 {
8327 const typename Triangulation<dim, spacedim1>::
8328 line_iterator edge = face->line(e);
8329 CellData<1> boundary_edge;
8330
8331 boundary_edge.vertices.resize(edge->n_vertices());
8332 for (const auto i : edge->vertex_indices())
8333 boundary_edge.vertices[i] =
8334 edge->vertex_index(i);
8335 boundary_edge.boundary_id =
8337 boundary_edge.manifold_id = edge->manifold_id();
8338
8339 subcelldata.boundary_lines.emplace_back(
8340 std::move(boundary_edge));
8341
8342 edge->set_user_flag();
8343 }
8344 }
8345 }
8346
8347 // Reset the user flags to their previous values:
8348 const_cast<Triangulation<dim, spacedim1> &>(in_tria)
8349 .load_user_flags_line(user_flags_line);
8350 const_cast<Triangulation<dim, spacedim1> &>(in_tria)
8351 .load_user_flags_quad(user_flags_quad);
8352
8353 break;
8354 }
8355 default:
8357 }
8358
8359 out_tria.create_triangulation(v, cells, subcelldata);
8360
8361 for (const auto i : out_tria.get_manifold_ids())
8363 out_tria.set_manifold(i, FlatManifold<dim, spacedim2>());
8364 }
8365
8366
8367
8368 template <int dim, int spacedim>
8369 void
8372 const unsigned int n_divisions)
8373 {
8374 if constexpr (dim == 1)
8375 {
8376 out_tria.copy_triangulation(in_tria);
8377 return;
8378 }
8379 else if constexpr (dim == 2)
8381 n_divisions == 2 || n_divisions == 8,
8382 ExcMessage(
8383 "Quadrilaterals must be split into either 2 or 8 triangles."));
8384 else if constexpr (dim == 3)
8385 AssertThrow(n_divisions == 6 || n_divisions == 24,
8386 ExcMessage(
8387 "Hexahedra must be split into either 6 or 24 tetrahedra."));
8388
8390 ExcMessage(
8391 "GridGenerator::convert_hypercube_to_simplex_mesh() expects "
8392 "a Triangulation that consists only of quads/hexes."));
8393
8395 if (in_tria.n_global_levels() > 1)
8396 {
8398 flatten_triangulation(in_tria, temp_tria);
8399 }
8400 const Triangulation<dim, spacedim> &ref_tria =
8401 in_tria.n_global_levels() > 1 ? temp_tria : in_tria;
8402
8403 // static tables with the definitions of cells, faces and edges by its
8404 // vertices for 2d and 3d. For the inheritance of the manifold_id,
8405 // definitions of inner-faces and boundary-faces are required. In case of
8406 // 3d, also inner-edges and boundary-edges need to be defined.
8407
8408 // Cell definition 2d:
8409 static const ndarray<unsigned int, 2, 3> vertex_ids_for_cells_2d_2 = {
8410 {{{0, 1, 2}}, {{3, 2, 1}}}};
8411 static const ndarray<unsigned int, 8, 3> vertex_ids_for_cells_2d_8 = {
8412 {{{0, 6, 4}},
8413 {{8, 4, 6}},
8414 {{8, 6, 5}},
8415 {{1, 5, 6}},
8416 {{2, 4, 7}},
8417 {{8, 7, 4}},
8418 {{8, 5, 7}},
8419 {{3, 7, 5}}}};
8420 [[maybe_unused]] const auto vertex_ids_for_cells_2d =
8421 n_divisions == 2 ? make_array_view(vertex_ids_for_cells_2d_2) :
8422 make_array_view(vertex_ids_for_cells_2d_8);
8423
8424 // Cell definition 3d:
8425 static const ndarray<unsigned int, 6, 4> vertex_ids_for_cells_3d_6 = {
8426 {{{0, 1, 3, 7}},
8427 {{0, 1, 7, 5}},
8428 {{0, 7, 3, 2}},
8429 {{2, 6, 0, 7}},
8430 {{4, 7, 5, 0}},
8431 {{4, 6, 7, 0}}}};
8432 static const ndarray<unsigned int, 24, 4> vertex_ids_for_cells_3d_24 = {
8433 {{{0, 1, 12, 10}}, {{2, 3, 11, 12}}, {{7, 6, 11, 13}},
8434 {{5, 4, 13, 10}}, {{0, 2, 8, 12}}, {{4, 6, 13, 8}},
8435 {{5, 13, 7, 9}}, {{1, 9, 3, 12}}, {{0, 8, 4, 10}},
8436 {{1, 5, 9, 10}}, {{3, 7, 11, 9}}, {{2, 6, 8, 11}},
8437 {{12, 13, 10, 9}}, {{12, 13, 9, 11}}, {{12, 13, 11, 8}},
8438 {{12, 13, 8, 10}}, {{13, 8, 10, 4}}, {{13, 10, 9, 5}},
8439 {{13, 9, 11, 7}}, {{13, 11, 8, 6}}, {{10, 12, 9, 1}},
8440 {{9, 12, 11, 3}}, {{11, 12, 8, 2}}, {{8, 12, 10, 0}}}};
8441
8442 [[maybe_unused]] const auto vertex_ids_for_cells_3d =
8443 n_divisions == 6 ? make_array_view(vertex_ids_for_cells_3d_6) :
8444 make_array_view(vertex_ids_for_cells_3d_24);
8445
8446 // Boundary-faces 2d:
8447 // For 2 new Triangles the lines are identical to the original.
8448 static const std::
8449 array<std::pair<unsigned int, std::array<unsigned int, 2>>, 4>
8450 vertex_ids_for_boundary_faces_2d_2 = {
8451 {{0, {{0, 2}}}, {1, {{1, 3}}}, {2, {{0, 1}}}, {3, {{2, 3}}}}};
8452 // After converting, each of the 4 quadrilateral faces is defined by faces
8453 // of 2 different triangles, i.e., lines. The first value in each pair is
8454 // the original face index and the second is the new line.
8455 static const std::
8456 array<std::pair<unsigned int, std::array<unsigned int, 2>>, 8>
8457 vertex_ids_for_boundary_faces_2d_8 = {{{0, {{0, 4}}},
8458 {0, {{4, 2}}},
8459 {1, {{1, 5}}},
8460 {1, {{5, 3}}},
8461 {2, {{0, 6}}},
8462 {2, {{6, 1}}},
8463 {3, {{2, 7}}},
8464 {3, {{7, 3}}}}};
8465 [[maybe_unused]] const auto vertex_ids_for_boundary_faces_2d =
8466 n_divisions == 2 ? make_array_view(vertex_ids_for_boundary_faces_2d_2) :
8467 make_array_view(vertex_ids_for_boundary_faces_2d_8);
8468
8469 // Boundary-faces 3d:
8470 // The minimal split creates two new triangles on each face.
8471 static const std::
8472 array<std::pair<unsigned int, std::array<unsigned int, 3>>, 12>
8473 vertex_ids_for_boundary_faces_3d_6 = {{{0, {{2, 6, 0}}},
8474 {0, {{6, 4, 0}}},
8475 {1, {{3, 1, 7}}},
8476 {1, {{7, 1, 5}}},
8477 {2, {{1, 0, 5}}},
8478 {2, {{4, 5, 0}}},
8479 {3, {{6, 2, 7}}},
8480 {3, {{3, 7, 2}}},
8481 {4, {{0, 1, 3}}},
8482 {4, {{0, 3, 2}}},
8483 {5, {{4, 6, 7}}},
8484 {5, {{4, 7, 5}}}}};
8485 // After converting, each of the 6 hexahedron faces corresponds to faces of
8486 // 4 different tetrahedron faces, i.e., triangles. Note that a triangle is
8487 // defined by 3 vertices.
8488 static const std::
8489 array<std::pair<unsigned int, std::array<unsigned int, 3>>, 24>
8490 vertex_ids_for_boundary_faces_3d_24 = {
8491 {{0, {{0, 4, 8}}}, {0, {{4, 8, 6}}}, {0, {{8, 6, 2}}},
8492 {0, {{0, 2, 8}}}, {1, {{1, 3, 9}}}, {1, {{3, 9, 7}}},
8493 {1, {{9, 7, 5}}}, {1, {{1, 9, 5}}}, {2, {{0, 1, 10}}},
8494 {2, {{1, 10, 5}}}, {2, {{10, 5, 4}}}, {2, {{0, 10, 4}}},
8495 {3, {{2, 3, 11}}}, {3, {{3, 11, 7}}}, {3, {{11, 7, 6}}},
8496 {3, {{2, 11, 6}}}, {4, {{0, 1, 12}}}, {4, {{1, 12, 3}}},
8497 {4, {{12, 3, 2}}}, {4, {{0, 12, 2}}}, {5, {{4, 5, 13}}},
8498 {5, {{5, 13, 7}}}, {5, {{13, 7, 6}}}, {5, {{4, 13, 6}}}}};
8499 [[maybe_unused]] const auto vertex_ids_for_boundary_faces_3d =
8500 n_divisions == 6 ? make_array_view(vertex_ids_for_boundary_faces_3d_6) :
8501 make_array_view(vertex_ids_for_boundary_faces_3d_24);
8502
8503 // Inner-faces 2d:
8504 // With a single split there is only one new internal face.
8505 static const ndarray<unsigned int, 1, 2> vertex_ids_for_inner_faces_2d_2 = {
8506 {{{1, 2}}}};
8507 // The converted triangulation based on simplices has 8 faces that do not
8508 // form the boundary, i.e. inner-faces, each defined by 2 vertices.
8509 static const ndarray<unsigned int, 8, 2> vertex_ids_for_inner_faces_2d_8 = {
8510 {{{6, 4}},
8511 {{6, 8}},
8512 {{6, 5}},
8513 {{4, 8}},
8514 {{8, 5}},
8515 {{7, 4}},
8516 {{7, 8}},
8517 {{7, 5}}}};
8518 [[maybe_unused]] const auto vertex_ids_for_inner_faces_2d =
8519 n_divisions == 2 ? make_array_view(vertex_ids_for_inner_faces_2d_2) :
8520 make_array_view(vertex_ids_for_inner_faces_2d_8);
8521
8522 // Inner-faces 3d:
8523 // Note that all inner faces include vertices 0 and 7.
8524 static const ndarray<unsigned int, 6, 3> vertex_ids_for_inner_faces_3d_6 = {
8525 {
8526 {{1, 0, 7}},
8527 {{7, 0, 2}},
8528 {{0, 7, 5}},
8529 {{0, 3, 7}},
8530 {{7, 4, 0}},
8531 {{0, 6, 7}},
8532 }};
8533 // The converted triangulation based on simplices has 72 faces that do not
8534 // form the boundary, i.e. inner-faces, each defined by 3 vertices.
8535 static const ndarray<unsigned int, 72, 3> vertex_ids_for_inner_faces_3d_24 =
8536 {{
8537 {{0, 12, 10}}, {{12, 1, 10}}, {{12, 1, 9}}, {{12, 3, 9}},
8538 {{12, 2, 11}}, {{12, 3, 11}}, {{12, 0, 8}}, {{12, 2, 8}},
8539 {{9, 13, 5}}, {{13, 7, 9}}, {{11, 7, 13}}, {{11, 6, 13}},
8540 {{4, 8, 13}}, {{6, 8, 13}}, {{4, 13, 10}}, {{13, 5, 10}},
8541 {{10, 9, 5}}, {{10, 9, 1}}, {{11, 9, 7}}, {{11, 9, 3}},
8542 {{8, 11, 2}}, {{8, 11, 6}}, {{8, 10, 0}}, {{8, 10, 4}},
8543 {{12, 3, 9}}, {{12, 9, 11}}, {{12, 3, 11}}, {{3, 9, 11}},
8544 {{2, 12, 8}}, {{2, 12, 11}}, {{2, 11, 8}}, {{8, 12, 11}},
8545 {{0, 12, 10}}, {{0, 12, 8}}, {{0, 8, 10}}, {{8, 10, 12}},
8546 {{12, 1, 10}}, {{12, 1, 9}}, {{1, 10, 9}}, {{10, 9, 12}},
8547 {{10, 8, 4}}, {{10, 8, 13}}, {{4, 13, 8}}, {{4, 13, 10}},
8548 {{10, 9, 13}}, {{10, 9, 5}}, {{13, 5, 10}}, {{13, 5, 9}},
8549 {{13, 7, 9}}, {{13, 7, 11}}, {{9, 11, 13}}, {{9, 11, 7}},
8550 {{8, 11, 13}}, {{8, 11, 6}}, {{6, 13, 8}}, {{6, 13, 11}},
8551 {{12, 13, 10}}, {{12, 13, 8}}, {{8, 10, 13}}, {{8, 10, 12}},
8552 {{12, 13, 10}}, {{12, 13, 9}}, {{10, 9, 13}}, {{10, 9, 12}},
8553 {{12, 13, 9}}, {{12, 13, 11}}, {{9, 11, 13}}, {{9, 11, 12}},
8554 {{12, 13, 11}}, {{12, 13, 8}}, {{8, 11, 13}}, {{8, 11, 12}},
8555 }};
8556 [[maybe_unused]] const auto vertex_ids_for_inner_faces_3d =
8557 n_divisions == 6 ? make_array_view(vertex_ids_for_inner_faces_3d_6) :
8558 make_array_view(vertex_ids_for_inner_faces_3d_24);
8559
8560 // Inner-edges 3d:
8561 // This split only requires a single new internal line.
8562 static const ndarray<unsigned int, 1, 2> vertex_ids_for_inner_edges_3d_6 = {
8563 {{{0, 7}}}};
8564 // The converted triangulation based on simplices has 60 edges that do not
8565 // coincide with the boundary, i.e. inner-edges, each defined by 2 vertices.
8566 static const ndarray<unsigned int, 60, 2> vertex_ids_for_inner_edges_3d_24 =
8567 {{{{12, 10}}, {{12, 9}}, {{12, 11}}, {{12, 8}}, {{9, 13}}, {{11, 13}},
8568 {{8, 13}}, {{10, 13}}, {{10, 9}}, {{9, 11}}, {{11, 8}}, {{8, 10}},
8569 {{12, 9}}, {{12, 11}}, {{11, 9}}, {{12, 8}}, {{12, 11}}, {{11, 8}},
8570 {{12, 8}}, {{12, 10}}, {{10, 8}}, {{12, 10}}, {{12, 9}}, {{9, 10}},
8571 {{13, 10}}, {{13, 8}}, {{8, 10}}, {{13, 10}}, {{13, 9}}, {{9, 10}},
8572 {{13, 11}}, {{13, 9}}, {{11, 9}}, {{13, 11}}, {{13, 8}}, {{11, 8}},
8573 {{12, 13}}, {{8, 10}}, {{8, 13}}, {{10, 13}}, {{8, 12}}, {{10, 12}},
8574 {{12, 13}}, {{10, 9}}, {{10, 13}}, {{9, 13}}, {{10, 12}}, {{9, 12}},
8575 {{12, 13}}, {{9, 11}}, {{9, 13}}, {{11, 13}}, {{9, 12}}, {{11, 12}},
8576 {{12, 13}}, {{11, 8}}, {{11, 13}}, {{8, 13}}, {{11, 12}}, {{8, 12}}}};
8577 [[maybe_unused]] const auto vertex_ids_for_inner_edges_3d =
8578 n_divisions == 6 ? make_array_view(vertex_ids_for_inner_edges_3d_6) :
8579 make_array_view(vertex_ids_for_inner_edges_3d_24);
8580
8581 // Boundary-edges 3d:
8582 //
8583 // All implemented conversions re-use the existing 12 lines of each
8584 // hexahedron so those are not included in these tables.
8585 //
8586 // Since each boundary face has two tetrahedron faces, there is just one new
8587 // line per face.
8588 static const std::
8589 array<std::pair<unsigned int, std::array<unsigned int, 2>>, 6>
8590 vertex_ids_for_new_boundary_edges_3d_6 = {{{0, {{0, 6}}},
8591 {1, {{1, 7}}},
8592 {2, {{0, 5}}},
8593 {3, {{2, 7}}},
8594 {4, {{0, 3}}},
8595 {5, {{4, 7}}}}};
8596 // For each of the 6 boundary-faces of the hexahedron, there are 8 edges (of
8597 // different tetrahedrons) that coincide with the boundary, i.e.
8598 // boundary-edges. Each boundary-edge is defined by 2 vertices. 4 of these
8599 // edges are new (they are placed in the middle of a presently existing
8600 // face); the other 4 coincide with edges present in the hexahedral
8601 // triangulation. The new 4 edges inherit the manifold id of the relevant
8602 // face, but the other 4 need to be copied from the input and thus do not
8603 // require a lookup table.
8604 static const std::
8605 array<std::pair<unsigned int, std::array<unsigned int, 2>>, 24>
8606 vertex_ids_for_new_boundary_edges_3d_24 = {
8607 {{0, {{4, 8}}}, {0, {{6, 8}}}, {0, {{0, 8}}}, {0, {{2, 8}}},
8608 {1, {{5, 9}}}, {1, {{7, 9}}}, {1, {{1, 9}}}, {1, {{3, 9}}},
8609 {2, {{4, 10}}}, {2, {{5, 10}}}, {2, {{0, 10}}}, {2, {{1, 10}}},
8610 {3, {{6, 11}}}, {3, {{7, 11}}}, {3, {{2, 11}}}, {3, {{3, 11}}},
8611 {4, {{2, 12}}}, {4, {{3, 12}}}, {4, {{0, 12}}}, {4, {{1, 12}}},
8612 {5, {{6, 13}}}, {5, {{7, 13}}}, {5, {{4, 13}}}, {5, {{5, 13}}}}};
8613 [[maybe_unused]] const auto vertex_ids_for_new_boundary_edges_3d =
8614 n_divisions == 6 ?
8615 make_array_view(vertex_ids_for_new_boundary_edges_3d_6) :
8616 make_array_view(vertex_ids_for_new_boundary_edges_3d_24);
8617
8618 std::vector<Point<spacedim>> vertices;
8619 std::vector<CellData<dim>> cells;
8620 SubCellData subcell_data;
8621
8622 // store for each vertex and face the assigned index so that we only
8623 // assign them a value once
8624 std::vector<unsigned int> old_to_new_vertex_indices(
8626 std::vector<unsigned int> face_to_new_vertex_indices(
8628
8629 // We first have to create all of the new vertices. To do this, we loop over
8630 // all cells and on each cell
8631 // (i) copy the existing vertex locations (and record their new indices in
8632 // the 'old_to_new_vertex_indices' vector),
8633 // (ii) create new midpoint vertex locations for each face (and record their
8634 // new indices in the 'face_to_new_vertex_indices' vector),
8635 // (iii) create new midpoint vertex locations for each cell (dim = 2 only)
8636 for (const auto &cell : ref_tria.cell_iterators())
8637 {
8638 // temporary array storing the global indices of each cell entity in the
8639 // sequence: vertices, edges/faces, cell
8640 std::array<unsigned int, dim == 2 ? 9 : 14> local_vertex_indices;
8641 local_vertex_indices.fill(numbers::invalid_unsigned_int);
8642
8643 // (i) copy the existing vertex locations
8644 for (const auto v : cell->vertex_indices())
8645 {
8646 const auto v_global = cell->vertex_index(v);
8647
8648 if (old_to_new_vertex_indices[v_global] ==
8650 {
8651 old_to_new_vertex_indices[v_global] = vertices.size();
8652 vertices.push_back(cell->vertex(v));
8653 }
8654
8655 AssertIndexRange(v, local_vertex_indices.size());
8656 local_vertex_indices[v] = old_to_new_vertex_indices[v_global];
8657 }
8658
8659 // (ii) create new midpoint vertex locations for each face
8660 if constexpr (dim > 1)
8661 for (const auto f : cell->face_indices())
8662 {
8663 const auto f_global = cell->face_index(f);
8664
8665 if (face_to_new_vertex_indices[f_global] ==
8667 {
8668 face_to_new_vertex_indices[f_global] = vertices.size();
8669 vertices.push_back(
8670 cell->face(f)->center(/*respect_manifold*/ true));
8671 }
8672
8673 AssertIndexRange(cell->n_vertices() + f,
8674 local_vertex_indices.size());
8675 local_vertex_indices[cell->n_vertices() + f] =
8676 face_to_new_vertex_indices[f_global];
8677 }
8678
8679 // (iii) create new midpoint vertex locations for each cell
8680 if (dim == 2)
8681 {
8682 AssertIndexRange(cell->n_vertices() + cell->n_faces(),
8683 local_vertex_indices.size());
8684 local_vertex_indices[cell->n_vertices() + cell->n_faces()] =
8685 vertices.size();
8686 vertices.push_back(cell->center(/*respect_manifold*/ true));
8687 }
8688
8689 // helper function for creating cells and subcells
8690 [[maybe_unused]] const auto add_cell =
8691 [&](const unsigned int struct_dim,
8692 const auto &index_vertices,
8693 const unsigned int material_or_boundary_id,
8694 const unsigned int manifold_id = 0) {
8695 // sub-cell data only has to be stored if the information differs
8696 // from the default
8697 if (struct_dim < dim && (material_or_boundary_id ==
8699 manifold_id == numbers::flat_manifold_id))
8700 return;
8701
8702 if (struct_dim == dim) // cells
8703 {
8704 AssertDimension(index_vertices.size(), dim + 1);
8705
8706 CellData<dim> cell_data(index_vertices.size());
8707 cell_data.material_id =
8708 material_or_boundary_id; // inherit material id
8709 cell_data.manifold_id = manifold_id; // inherit cell-manifold id
8710 for (unsigned int i = 0; i < cell_data.vertices.size(); ++i)
8711 {
8712 AssertIndexRange(index_vertices[i],
8713 local_vertex_indices.size());
8714 cell_data.vertices[i] =
8715 local_vertex_indices[index_vertices[i]];
8716 }
8717 cells.emplace_back(std::move(cell_data));
8718 }
8719 else if (dim == 2 && struct_dim == 1) // an edge of a simplex
8720 {
8721 Assert(index_vertices.size() == 2, ExcInternalError());
8722
8723 CellData<1> boundary_line(2);
8724 boundary_line.boundary_id = material_or_boundary_id;
8725 boundary_line.manifold_id = manifold_id;
8726 for (unsigned int i = 0; i < boundary_line.vertices.size(); ++i)
8727 {
8728 AssertIndexRange(index_vertices[i],
8729 local_vertex_indices.size());
8730 boundary_line.vertices[i] =
8731 local_vertex_indices[index_vertices[i]];
8732 }
8733 subcell_data.boundary_lines.emplace_back(
8734 std::move(boundary_line));
8735 }
8736 else if (dim == 3 && struct_dim == 2) // a face of a tetrahedron
8737 {
8738 Assert(index_vertices.size() == 3, ExcInternalError());
8739
8740 CellData<2> boundary_quad(3);
8741 boundary_quad.material_id = material_or_boundary_id;
8742 boundary_quad.manifold_id = manifold_id;
8743 for (unsigned int i = 0; i < boundary_quad.vertices.size(); ++i)
8744 {
8745 AssertIndexRange(index_vertices[i],
8746 local_vertex_indices.size());
8747 boundary_quad.vertices[i] =
8748 local_vertex_indices[index_vertices[i]];
8749 }
8750 subcell_data.boundary_quads.emplace_back(
8751 std::move(boundary_quad));
8752 }
8753 else if (dim == 3 && struct_dim == 1) // an edge of a tetrahedron
8754 {
8755 Assert(index_vertices.size() == 2, ExcInternalError());
8756
8757 CellData<1> boundary_line(2);
8758 boundary_line.boundary_id = material_or_boundary_id;
8759 boundary_line.manifold_id = manifold_id;
8760 for (unsigned int i = 0; i < boundary_line.vertices.size(); ++i)
8761 {
8762 AssertIndexRange(index_vertices[i],
8763 local_vertex_indices.size());
8764 boundary_line.vertices[i] =
8765 local_vertex_indices[index_vertices[i]];
8766 }
8767 subcell_data.boundary_lines.emplace_back(
8768 std::move(boundary_line));
8769 }
8770 else
8771 {
8773 }
8774 };
8775
8776 // create cells one by one
8777 if constexpr (dim == 2)
8778 {
8779 // get info from current quad cell:
8780 const auto manifold_id_cell = cell->manifold_id();
8781 const auto material_id_cell = cell->material_id();
8782
8783 // inherit cell manifold
8784 for (const auto &cell_vertices : vertex_ids_for_cells_2d)
8785 add_cell(dim, cell_vertices, material_id_cell, manifold_id_cell);
8786
8787 // inherit inner manifold (faces)
8788 for (const auto &face_vertices : vertex_ids_for_inner_faces_2d)
8789 // set inner tri-faces according to cell-manifold of quad
8790 // element, set invalid b_id, since we do not want to modify
8791 // b_id inside
8792 add_cell(1,
8793 face_vertices,
8795 manifold_id_cell);
8796 }
8797 else if constexpr (dim == 3)
8798 {
8799 // get info from current quad cell:
8800 const auto manifold_id_cell = cell->manifold_id();
8801 const auto material_id_cell = cell->material_id();
8802
8803 for (const auto &cell_vertices : vertex_ids_for_cells_3d)
8804 add_cell(dim, cell_vertices, material_id_cell, manifold_id_cell);
8805
8806 // set manifold of inner FACES of tets according to
8807 // hex-cell-manifold
8808 for (const auto &face_vertices : vertex_ids_for_inner_faces_3d)
8809 add_cell(2,
8810 face_vertices,
8812 manifold_id_cell);
8813
8814 // set manifold of inner EDGES of tets according to
8815 // hex-cell-manifold
8816 for (const auto &edge_vertices : vertex_ids_for_inner_edges_3d)
8817 add_cell(1,
8818 edge_vertices,
8820 manifold_id_cell);
8821 }
8822 else
8824
8825 // Set up sub-cell data.
8826 if constexpr (dim == 2)
8827 {
8828 for (const auto &[quad_face_no, tri_face_vertices] :
8829 vertex_ids_for_boundary_faces_2d)
8830 {
8831 const auto face = cell->face(quad_face_no);
8832 add_cell(1,
8833 tri_face_vertices,
8834 face->boundary_id(),
8835 face->manifold_id());
8836 }
8837 }
8838 else if constexpr (dim == 3)
8839 {
8840 for (const auto &[hex_face_no, tet_face_vertices] :
8841 vertex_ids_for_boundary_faces_3d)
8842 {
8843 const auto face = cell->face(hex_face_no);
8844 add_cell(2,
8845 tet_face_vertices,
8846 face->boundary_id(),
8847 face->manifold_id());
8848 }
8849
8850 for (const auto &[hex_face_no, tet_edge_vertices] :
8851 vertex_ids_for_new_boundary_edges_3d)
8852 {
8853 const auto face = cell->face(hex_face_no);
8854 add_cell(1,
8855 tet_edge_vertices,
8856 face->boundary_id(),
8857 face->manifold_id());
8858 }
8859 }
8860 else
8862
8863 // set manifold ids of edges that were already present in the
8864 // triangulation.
8865 if constexpr (dim == 3)
8866 {
8867 for (const auto e : cell->line_indices())
8868 {
8869 auto edge = cell->line(e);
8870 // Rather than use add_cell(), which does additional index
8871 // translation, just add edges directly into subcell_data since
8872 // we already know the correct global vertex indices.
8873 CellData<1> edge_data;
8874 edge_data.vertices[0] =
8875 old_to_new_vertex_indices[edge->vertex_index(0)];
8876 edge_data.vertices[1] =
8877 old_to_new_vertex_indices[edge->vertex_index(1)];
8878 edge_data.boundary_id = edge->boundary_id();
8879 edge_data.manifold_id = edge->manifold_id();
8880
8881 subcell_data.boundary_lines.emplace_back(std::move(edge_data));
8882 }
8883 }
8884 }
8885
8886 out_tria.clear();
8887 out_tria.create_triangulation(vertices, cells, subcell_data);
8888
8889 for (const auto i : out_tria.get_manifold_ids())
8891 out_tria.set_manifold(i, FlatManifold<dim, spacedim>());
8892 }
8893
8894
8895
8896 template <int dim, int spacedim>
8897 void
8900 {
8901 if constexpr (dim == 1)
8902 {
8903 out_tria.copy_triangulation(in_tria);
8904 return;
8905 }
8906 else
8907 {
8910 ExcMessage(
8911 "GridGenerator::convert_simplex_to_hypercube_mesh() expects "
8912 "a Triangulation that consists only of triangles/tetrahedra."));
8913
8915 if (in_tria.n_global_levels() > 1)
8916 {
8918 flatten_triangulation(in_tria, temp_tria);
8919 }
8920 const Triangulation<dim, spacedim> &ref_tria =
8921 in_tria.n_global_levels() > 1 ? temp_tria : in_tria;
8922
8923 // First set up all of the vertices of the new mesh, along with
8924 // a map that maps from edges/faces/cells to their new midpoints
8925 std::vector<Point<spacedim>> vertices = ref_tria.get_vertices();
8926 vertices.reserve(ref_tria.get_vertices().size() + ref_tria.n_lines() +
8927 (dim > 2 ? ref_tria.n_quads() : 0) +
8928 ref_tria.n_cells());
8929 std::map<typename Triangulation<dim, spacedim>::line_iterator,
8930 unsigned int>
8931 line_to_midpoint_vertex_map;
8932 std::map<typename Triangulation<dim, spacedim>::face_iterator,
8933 unsigned int>
8934 face_to_midpoint_vertex_map;
8935 std::map<typename Triangulation<dim, spacedim>::cell_iterator,
8936 unsigned int>
8937 cell_to_midpoint_vertex_map;
8938 for (const auto &cell : ref_tria.active_cell_iterators())
8939 {
8940 // First the faces that bound the cell. Make sure we touch
8941 // every face only once
8942 for (const auto &face : cell->face_iterators())
8943 if (face_to_midpoint_vertex_map.find(face) ==
8944 face_to_midpoint_vertex_map.end())
8945 {
8946 // Add the new vertex to the list of vertices
8947 {
8948 const Point<spacedim> new_vertex = face->center();
8949 vertices.push_back(new_vertex);
8950 }
8951
8952 // Also keep track of the index given to this vertex:
8953 face_to_midpoint_vertex_map[face] = vertices.size() - 1;
8954
8955 // If we're in 3d, we also need to separately deal with the
8956 // edges:
8957 if constexpr (dim == 3)
8958 {
8959 for (unsigned int l = 0; l < face->n_lines(); ++l)
8960 {
8961 const typename Triangulation<dim,
8962 spacedim>::line_iterator
8963 line = face->line(l);
8964 if (line_to_midpoint_vertex_map.find(line) ==
8965 line_to_midpoint_vertex_map.end())
8966 {
8967 // Add the new vertex to the list of vertices
8968 {
8969 const Point<spacedim> new_vertex =
8970 line->center();
8971 vertices.push_back(new_vertex);
8972 }
8973
8974 // Also keep track of the index given to this
8975 // vertex:
8976 line_to_midpoint_vertex_map[line] =
8977 vertices.size() - 1;
8978 }
8979 }
8980 }
8981 }
8982
8983 // Now for the cell midpoint:
8984 // Add the new vertex to the list of vertices
8985 {
8986 const Point<spacedim> new_vertex = cell->center();
8987 vertices.push_back(new_vertex);
8988 }
8989
8990 // Also keep track of the index given to this vertex:
8991 cell_to_midpoint_vertex_map[cell] = vertices.size() - 1;
8992 }
8993 // Check that we counted correctly:
8994 Assert(vertices.size() ==
8995 ref_tria.get_vertices().size() + ref_tria.n_lines() +
8996 (dim > 2 ? ref_tria.n_quads() : 0) + ref_tria.n_cells(),
8998
8999 // Now create new cells. Each cell will be subdivided into (dim+1)
9000 // new cells (i.e., one new cell adjacent to each vertex of the old
9001 // cell).
9002 std::vector<CellData<dim>> cell_data;
9003 cell_data.reserve(ref_tria.n_cells() * (dim + 1));
9004
9005 for (const auto &cell : ref_tria.active_cell_iterators())
9006 for (unsigned int v = 0; v < cell->n_vertices(); ++v)
9007 {
9008 CellData<dim> new_cell;
9009
9010 if constexpr (dim == 2)
9011 {
9012 // In triangles, lines 'v' and 'v+2' (mod 3) are adjacent
9013 // to vertex v. Make a quad out of the old vertex, the
9014 // line midpoints, and the new cell midpoint
9015 new_cell.vertices = {
9016 cell->vertex_index(v),
9017 face_to_midpoint_vertex_map[cell->line(v)],
9018 face_to_midpoint_vertex_map[cell->line((v + 2) % 3)],
9019 cell_to_midpoint_vertex_map[cell]};
9020 }
9021 else
9022 {
9023 if (v == 0)
9024 {
9025 new_cell.vertices = {
9026 cell->vertex_index(0),
9027 line_to_midpoint_vertex_map[cell->line(0)],
9028 line_to_midpoint_vertex_map[cell->line(2)],
9029 face_to_midpoint_vertex_map[cell->face(0)],
9030 line_to_midpoint_vertex_map[cell->line(3)],
9031 face_to_midpoint_vertex_map[cell->face(1)],
9032 face_to_midpoint_vertex_map[cell->face(2)],
9033 cell_to_midpoint_vertex_map[cell]};
9034 }
9035 else if (v == 1)
9036 {
9037 new_cell.vertices = {
9038 line_to_midpoint_vertex_map[cell->line(0)],
9039 cell->vertex_index(1),
9040 face_to_midpoint_vertex_map[cell->face(0)],
9041 line_to_midpoint_vertex_map[cell->line(1)],
9042 face_to_midpoint_vertex_map[cell->face(1)],
9043 line_to_midpoint_vertex_map[cell->line(4)],
9044 cell_to_midpoint_vertex_map[cell],
9045 face_to_midpoint_vertex_map[cell->face(3)]};
9046 }
9047 else if (v == 2)
9048 {
9049 new_cell.vertices = {
9050 line_to_midpoint_vertex_map[cell->line(2)],
9051 face_to_midpoint_vertex_map[cell->face(0)],
9052 cell->vertex_index(2),
9053 line_to_midpoint_vertex_map[cell->line(1)],
9054 face_to_midpoint_vertex_map[cell->face(2)],
9055 cell_to_midpoint_vertex_map[cell],
9056 line_to_midpoint_vertex_map[cell->line(5)],
9057 face_to_midpoint_vertex_map[cell->face(3)]};
9058 }
9059 else if (v == 3)
9060 {
9061 new_cell.vertices = {
9062 line_to_midpoint_vertex_map[cell->line(3)],
9063 face_to_midpoint_vertex_map[cell->face(1)],
9064 face_to_midpoint_vertex_map[cell->face(2)],
9065 cell_to_midpoint_vertex_map[cell],
9066 cell->vertex_index(3),
9067 line_to_midpoint_vertex_map[cell->line(4)],
9068 line_to_midpoint_vertex_map[cell->line(5)],
9069 face_to_midpoint_vertex_map[cell->face(3)]};
9070 }
9071 else
9073 }
9074
9075 new_cell.material_id = cell->material_id();
9076 new_cell.manifold_id = cell->manifold_id();
9077
9078 cell_data.emplace_back(std::move(new_cell));
9079 }
9080
9081 // Next also collect information about boundary indicators:
9082 SubCellData subcell_data;
9083 for (const auto &cell : ref_tria.active_cell_iterators())
9084 for (const auto &face : cell->face_iterators())
9085 if (face->at_boundary() && (face->boundary_id() != 0))
9086 // The face is at the boundary and doesn't have the default
9087 // boundary id:
9088 {
9089 if constexpr (dim == 2)
9090 {
9091 CellData<dim - 1> child_face_1, child_face_2;
9092 child_face_1.vertices = {face->vertex_index(0),
9093 face_to_midpoint_vertex_map[face]};
9094 child_face_1.boundary_id = face->boundary_id();
9095 child_face_2.vertices = {face_to_midpoint_vertex_map[face],
9096 face->vertex_index(1)};
9097 child_face_2.boundary_id = face->boundary_id();
9098 subcell_data.boundary_lines.emplace_back(
9099 std::move(child_face_1));
9100 subcell_data.boundary_lines.emplace_back(
9101 std::move(child_face_2));
9102 }
9103 else
9104 {
9105 CellData<dim - 1> child_face_1, child_face_2, child_face_3;
9106
9107 child_face_1.vertices = {
9108 face->vertex_index(0),
9109 line_to_midpoint_vertex_map[face->line(0)],
9110 line_to_midpoint_vertex_map[face->line(2)],
9111 face_to_midpoint_vertex_map[face]};
9112 child_face_1.boundary_id = face->boundary_id();
9113
9114 child_face_2.vertices = {
9115 line_to_midpoint_vertex_map[face->line(0)],
9116 face->vertex_index(1),
9117 face_to_midpoint_vertex_map[face],
9118 line_to_midpoint_vertex_map[face->line(1)]};
9119
9120 child_face_2.boundary_id = face->boundary_id();
9121
9122 child_face_3.vertices = {
9123 line_to_midpoint_vertex_map[face->line(2)],
9124 face_to_midpoint_vertex_map[face],
9125 face->vertex_index(2),
9126 line_to_midpoint_vertex_map[face->line(1)]};
9127 child_face_3.boundary_id = face->boundary_id();
9128
9129 subcell_data.boundary_quads.emplace_back(
9130 std::move(child_face_1));
9131 subcell_data.boundary_quads.emplace_back(
9132 std::move(child_face_2));
9133 subcell_data.boundary_quads.emplace_back(
9134 std::move(child_face_3));
9135 }
9136 }
9137
9138 // In a final step, actually create the triangulation and reset manifold
9139 // ids (because manifolds cannot be reliably transferred from hypercube
9140 // meshes to simplex meshes, given that manifolds object are typically
9141 // specific for one cell type.
9142 out_tria.clear();
9143
9144 if constexpr (dim == 3)
9145 {
9148 }
9149
9150 out_tria.create_triangulation(vertices, cell_data, subcell_data);
9151
9152 for (const auto i : out_tria.get_manifold_ids())
9154 out_tria.set_manifold(i, FlatManifold<dim, spacedim>());
9155 }
9156 }
9157
9158
9159 template <int dim, int spacedim>
9160 void
9163 {
9165 if (in_tria.n_global_levels() > 1)
9166 {
9168 GridGenerator::flatten_triangulation(in_tria, temp_tria);
9169 }
9170 const Triangulation<dim, spacedim> &ref_tria =
9171 in_tria.n_global_levels() > 1 ? temp_tria : in_tria;
9172
9173 // Three triangles connecting to barycenter with vertex index 3:
9174 static const ndarray<unsigned int, 3, 3> vertex_ids_for_cells_2d = {
9175 {{{0, 1, 3}}, {{1, 2, 3}}, {{2, 0, 3}}}};
9176
9177 // Boundary-faces 2d:
9178 // Each face of the original simplex is defined by the following vertices:
9180 vertex_ids_for_boundary_faces_2d = {
9181 {{{{{0, 1}}}}, {{{{1, 2}}}}, {{{{2, 0}}}}}};
9182
9183 // Three tetrahedra connecting to barycenter with vertex index 4:
9184 static const ndarray<unsigned int, 4, 4> vertex_ids_for_cells_3d = {
9185 {{{0, 1, 2, 4}}, {{1, 0, 3, 4}}, {{0, 2, 3, 4}}, {{2, 1, 3, 4}}}};
9186
9187 // Boundary-faces 3d:
9188 // Each face of the original simplex is defined by the following vertices:
9190 vertex_ids_for_boundary_faces_3d = {
9191 {{{{{0, 1, 2}}}}, {{{{1, 0, 3}}}}, {{{{0, 2, 3}}}}, {{{{2, 1, 3}}}}}};
9192
9193 // Boundary-lines 3d:
9194 // Each line/edge of the original simplex is defined by the following
9195 // vertices:
9197 vertex_ids_for_boundary_lines_3d = {{{{{{0, 1}}}},
9198 {{{{1, 2}}}},
9199 {{{{2, 0}}}},
9200 {{{{0, 3}}}},
9201 {{{{1, 3}}}},
9202 {{{{2, 3}}}}}};
9203
9204 std::vector<Point<spacedim>> vertices;
9205 std::vector<CellData<dim>> cells;
9206 SubCellData subcell_data;
9207
9208 // for each vertex we store the assigned index so that we only
9209 // assign them a value once
9210 std::vector<unsigned int> old_to_new_vertex_indices(
9212
9213 // We first have to create all of the new vertices. To do this, we loop
9214 // over all cells and on each cell (i) copy the existing vertex locations
9215 // (and record their new indices in the 'old_to_new_vertex_indices'
9216 // vector), (ii) create new barycenter vertex location
9217 for (const auto &cell : ref_tria.cell_iterators())
9218 {
9220 cell->reference_cell().is_simplex(),
9221 ExcMessage(
9222 "Cell with invalid ReferenceCell encountered. GridGenerator::alfeld_split_of_simplex_mesh() "
9223 "only supports simplex meshes as input."));
9224
9225 // temporary array storing the global indices of each cell entity in
9226 // the sequence: vertices, edges/faces, cell
9227 std::array<unsigned int, (dim == 3) ? 5 : 4> local_vertex_indices;
9228
9229 // (i) copy the existing vertex locations
9230 Point<spacedim> barycenter;
9231 for (const auto v : cell->vertex_indices())
9232 {
9233 const auto v_global = cell->vertex_index(v);
9234
9235 if (old_to_new_vertex_indices[v_global] ==
9237 {
9238 old_to_new_vertex_indices[v_global] = vertices.size();
9239 vertices.push_back(cell->vertex(v));
9240 }
9241
9242 AssertIndexRange(v, local_vertex_indices.size());
9243 local_vertex_indices[v] = old_to_new_vertex_indices[v_global];
9244
9245 barycenter += vertices[local_vertex_indices[v]] - Point<spacedim>();
9246 }
9247
9248 // (ii) barycenter:
9249 local_vertex_indices[local_vertex_indices.size() - 1] = vertices.size();
9250 vertices.push_back(barycenter / static_cast<double>(dim + 1));
9251
9252 // helper function for creating cells and subcells
9253 const auto add_cell = [&](const unsigned int struct_dim,
9254 const auto &index_vertices,
9255 const unsigned int material_or_boundary_id,
9256 const unsigned int manifold_id = 0) {
9257 // sub-cell data only has to be stored if the information differs
9258 // from the default
9259 if (struct_dim < dim &&
9260 (material_or_boundary_id == numbers::internal_face_boundary_id &&
9261 manifold_id == numbers::flat_manifold_id))
9262 return;
9263
9264 if (struct_dim == dim) // cells
9265 {
9266 AssertDimension(index_vertices.size(), dim + 1);
9267
9268 CellData<dim> cell_data(index_vertices.size());
9269 for (unsigned int i = 0; i < index_vertices.size(); ++i)
9270 {
9271 AssertIndexRange(index_vertices[i],
9272 local_vertex_indices.size());
9273 cell_data.vertices[i] =
9274 local_vertex_indices[index_vertices[i]];
9275 }
9276 cell_data.material_id =
9277 material_or_boundary_id; // inherit material id
9278 cell_data.manifold_id = manifold_id; // inherit cell-manifold id
9279 cells.emplace_back(std::move(cell_data));
9280 }
9281 else if (dim == 2 && struct_dim == 1) // an edge of a simplex
9282 {
9283 Assert(index_vertices.size() == 2, ExcInternalError());
9284
9285 CellData<1> boundary_line(2);
9286 boundary_line.boundary_id = material_or_boundary_id;
9287 boundary_line.manifold_id = manifold_id;
9288 for (unsigned int i = 0; i < boundary_line.vertices.size(); ++i)
9289 {
9290 AssertIndexRange(index_vertices[i],
9291 local_vertex_indices.size());
9292 boundary_line.vertices[i] =
9293 local_vertex_indices[index_vertices[i]];
9294 }
9295 subcell_data.boundary_lines.emplace_back(
9296 std::move(boundary_line));
9297 }
9298 else if (dim == 3 && struct_dim == 2) // a face of a tetrahedron
9299 {
9300 Assert(index_vertices.size() == 3, ExcInternalError());
9301
9302 CellData<2> boundary_quad(3);
9303 boundary_quad.material_id = material_or_boundary_id;
9304 boundary_quad.manifold_id = manifold_id;
9305 for (unsigned int i = 0; i < boundary_quad.vertices.size(); ++i)
9306 {
9307 AssertIndexRange(index_vertices[i],
9308 local_vertex_indices.size());
9309 boundary_quad.vertices[i] =
9310 local_vertex_indices[index_vertices[i]];
9311 }
9312 subcell_data.boundary_quads.emplace_back(
9313 std::move(boundary_quad));
9314 }
9315 else if (dim == 3 && struct_dim == 1) // an edge of a tetrahedron
9316 {
9317 Assert(index_vertices.size() == 2, ExcInternalError());
9318
9319 CellData<1> boundary_line(2);
9320 boundary_line.boundary_id = material_or_boundary_id;
9321 boundary_line.manifold_id = manifold_id;
9322 for (unsigned int i = 0; i < boundary_line.vertices.size(); ++i)
9323 {
9324 AssertIndexRange(index_vertices[i],
9325 local_vertex_indices.size());
9326 boundary_line.vertices[i] =
9327 local_vertex_indices[index_vertices[i]];
9328 }
9329 subcell_data.boundary_lines.emplace_back(
9330 std::move(boundary_line));
9331 }
9332 else
9333 {
9335 }
9336 };
9337
9338 const auto material_id_cell = cell->material_id();
9339 const auto manifold_id_cell = cell->manifold_id();
9340
9341 // create cells one by one
9342 if (dim == 2)
9343 {
9344 for (const auto &cell_vertices : vertex_ids_for_cells_2d)
9345 add_cell(dim, cell_vertices, material_id_cell, manifold_id_cell);
9346 }
9347 else if (dim == 3)
9348 {
9349 for (const auto &cell_vertices : vertex_ids_for_cells_3d)
9350 add_cell(dim, cell_vertices, material_id_cell, manifold_id_cell);
9351 }
9352 else
9354
9355 // Set up sub-cell data.
9356 for (const auto f : cell->face_indices())
9357 {
9358 const auto bid = cell->face(f)->boundary_id();
9359 const auto mid = cell->face(f)->manifold_id();
9360
9361 // process boundary-faces: set boundary and manifold ids
9362 if (dim == 2) // 2d boundary-faces
9363 {
9364 for (const auto &face_vertices :
9365 vertex_ids_for_boundary_faces_2d[f])
9366 add_cell(1, face_vertices, bid, mid);
9367 }
9368 else if (dim == 3)
9369 {
9370 for (const auto &face_vertices :
9371 vertex_ids_for_boundary_faces_3d[f])
9372 add_cell(2, face_vertices, bid, mid);
9373 }
9374 else
9376 }
9377
9378 // In 3D we need to treat boundary lines separately.
9379 if (dim == 3)
9380 {
9381 for (const auto l : cell->line_indices())
9382 {
9383 const auto bid = cell->line(l)->boundary_id();
9384 const auto mid = cell->line(l)->manifold_id();
9385
9386 for (const auto &line_vertices :
9387 vertex_ids_for_boundary_lines_3d[l])
9388 add_cell(1, line_vertices, bid, mid);
9389 }
9390 }
9391 }
9392
9393 out_tria.clear();
9394 out_tria.create_triangulation(vertices, cells, subcell_data);
9395
9396 for (const auto i : out_tria.get_manifold_ids())
9398 out_tria.set_manifold(i, FlatManifold<dim, spacedim>());
9399 }
9400
9401
9402
9403 template <template <int, int> class MeshType, int dim, int spacedim>
9405 (concepts::is_triangulation_or_dof_handler<MeshType<dim, spacedim>>))
9406# ifndef _MSC_VER
9407 std::map<typename MeshType<dim - 1, spacedim>::cell_iterator,
9408 typename MeshType<dim, spacedim>::face_iterator>
9409# else
9410 typename ExtractBoundaryMesh<MeshType, dim, spacedim>::return_type
9411# endif
9412 extract_boundary_mesh(const MeshType<dim, spacedim> &volume_mesh,
9413 MeshType<dim - 1, spacedim> &surface_mesh,
9414 const std::set<types::boundary_id> &boundary_ids)
9415 {
9416 Assert((dynamic_cast<
9418 &volume_mesh.get_triangulation()) == nullptr),
9420
9421 // This function works using the following assumption:
9422 // Triangulation::create_triangulation(...) will create cells that
9423 // preserve the order of cells passed in using the CellData argument;
9424 // also, that it will not reorder the vertices.
9425
9426 // dimension of the boundary mesh
9427 constexpr unsigned int boundary_dim = dim - 1;
9428
9429 // temporary map for level==0
9430 // iterator to face is stored along with face number
9431 // (this is required by the algorithm to adjust the normals of the
9432 // cells of the boundary mesh)
9433 std::vector<
9434 std::pair<typename MeshType<dim, spacedim>::face_iterator, unsigned int>>
9435 temporary_mapping_level0;
9436
9437 // vector indicating whether a vertex of the volume mesh has
9438 // already been visited (necessary to avoid duplicate vertices in
9439 // boundary mesh)
9440 std::vector<bool> touched(volume_mesh.get_triangulation().n_vertices(),
9441 false);
9442
9443 // data structures required for creation of boundary mesh
9444 std::vector<CellData<boundary_dim>> cells;
9445 SubCellData subcell_data;
9446 std::vector<Point<spacedim>> vertices;
9447
9448 // volume vertex indices to surf ones
9449 std::map<unsigned int, unsigned int> map_vert_index;
9450
9451 // define swapping of vertices to get proper normal orientation of
9452 // boundary mesh; the entry (i,j) of swap_matrix stores the index of the
9453 // vertex of the boundary cell corresponding to the j-th vertex on the
9454 // i-th face of the underlying volume cell if e.g. face 3 of a volume cell
9455 // is considered and vertices 1 and 2 of the corresponding boundary cell
9456 // are swapped to get proper normal orientation, swap_matrix[3]=( 0, 2, 1,
9457 // 3 )
9458 //
9459 // Permutation depends on the volume cell's reference cell. Currently only
9460 // hypercube and simplex meshes are supported.
9462 (volume_mesh.get_triangulation().is_mixed_mesh() == false),
9464 "Boundary extraction for mixed meshes is currently not supported."));
9466 volume_mesh.begin(0)->reference_cell();
9467
9468 // Size the table to the maximum number of vertices that can occur on any
9469 // face of the cell (4 for hypercubes, 3 for simplices). Unused entries
9470 // are left at the identity.
9471 const unsigned int max_n_face_vertices =
9472 reference_cell.face_reference_cell(0).n_vertices();
9473
9474 Table<2, unsigned int> swap_matrix(reference_cell.n_faces(),
9475 max_n_face_vertices);
9476 for (unsigned int f = 0; f < reference_cell.n_faces(); ++f)
9477 for (unsigned int j = 0; j < max_n_face_vertices; ++j)
9478 swap_matrix[f][j] = j;
9479
9480 if (reference_cell.is_hyper_cube())
9481 {
9482 // Hard-coded permutations for hypercube cells
9483 if (dim == 3)
9484 {
9485 std::swap(swap_matrix[0][1], swap_matrix[0][2]);
9486 std::swap(swap_matrix[2][1], swap_matrix[2][2]);
9487 std::swap(swap_matrix[4][1], swap_matrix[4][2]);
9488 }
9489 else if (dim == 2)
9490 {
9491 std::swap(swap_matrix[1][0], swap_matrix[1][1]);
9492 std::swap(swap_matrix[2][0], swap_matrix[2][1]);
9493 }
9494 }
9495 else if (reference_cell == ReferenceCells::get_simplex<dim>())
9496 {
9497 // Hard-coded permutations for simplex cells.
9498 // For both triangles (3 faces) and tetrahedra (4 faces) we swap the
9499 // first two face vertices so that the resulting boundary cell's normal
9500 // points outward.
9501 for (unsigned int f = 0; f < reference_cell.n_faces(); ++f)
9502 std::swap(swap_matrix[f][0], swap_matrix[f][1]);
9503 }
9504 else
9505 {
9506 // Wedges, pyramids, and mixed meshes are not currently supported.
9508 }
9509
9510 // Create boundary mesh and mapping
9511 // from only level(0) cells of volume_mesh
9512 for (typename MeshType<dim, spacedim>::cell_iterator cell =
9513 volume_mesh.begin(0);
9514 cell != volume_mesh.end(0);
9515 ++cell)
9516 for (const unsigned int i : cell->reference_cell().face_indices())
9517 {
9518 const typename MeshType<dim, spacedim>::face_iterator face =
9519 cell->face(i);
9520
9521 if (face->at_boundary() &&
9522 (boundary_ids.empty() ||
9523 (boundary_ids.find(face->boundary_id()) != boundary_ids.end())))
9524 {
9525 CellData<boundary_dim> c_data(face->n_vertices());
9526
9527 for (const unsigned int j : face->vertex_indices())
9528 {
9529 const unsigned int v_index = face->vertex_index(j);
9530
9531 if (!touched[v_index])
9532 {
9533 vertices.push_back(face->vertex(j));
9534 map_vert_index[v_index] = vertices.size() - 1;
9535 touched[v_index] = true;
9536 }
9537
9538 c_data.vertices[swap_matrix[i][j]] = map_vert_index[v_index];
9539 }
9540 c_data.material_id =
9541 static_cast<types::material_id>(face->boundary_id());
9542 c_data.manifold_id = face->manifold_id();
9543
9544
9545 // in 3d, we need to make sure we copy the manifold
9546 // indicators from the edges of the volume mesh to the
9547 // edges of the surface mesh
9548 //
9549 // we set default boundary ids for boundary lines
9550 // and numbers::internal_face_boundary_id for internal lines
9551 if (dim == 3)
9552 for (unsigned int e = 0; e < face->n_lines(); ++e)
9553 {
9554 // see if we already saw this edge from a
9555 // neighboring face, either in this or the reverse
9556 // orientation. if so, skip it.
9557 {
9558 bool edge_found = false;
9559 for (auto &boundary_line : subcell_data.boundary_lines)
9560 if (((boundary_line.vertices[0] ==
9561 map_vert_index[face->line(e)->vertex_index(0)]) &&
9562 (boundary_line.vertices[1] ==
9563 map_vert_index[face->line(e)->vertex_index(
9564 1)])) ||
9565 ((boundary_line.vertices[0] ==
9566 map_vert_index[face->line(e)->vertex_index(1)]) &&
9567 (boundary_line.vertices[1] ==
9568 map_vert_index[face->line(e)->vertex_index(0)])))
9569 {
9570 boundary_line.boundary_id =
9572 edge_found = true;
9573 break;
9574 }
9575 if (edge_found == true)
9576 // try next edge of current face
9577 continue;
9578 }
9579
9580 CellData<1> edge;
9581 edge.vertices[0] =
9582 map_vert_index[face->line(e)->vertex_index(0)];
9583 edge.vertices[1] =
9584 map_vert_index[face->line(e)->vertex_index(1)];
9585 edge.boundary_id = 0;
9586 edge.manifold_id = face->line(e)->manifold_id();
9587
9588 subcell_data.boundary_lines.emplace_back(std::move(edge));
9589 }
9590
9591 cells.emplace_back(std::move(c_data));
9592 temporary_mapping_level0.push_back(std::make_pair(face, i));
9593 }
9594 }
9595
9596 // create level 0 surface triangulation
9597 Assert(cells.size() > 0, ExcMessage("No boundary faces selected"));
9598 const_cast<Triangulation<dim - 1, spacedim> &>(
9599 surface_mesh.get_triangulation())
9600 .create_triangulation(vertices, cells, subcell_data);
9601
9602 // in 2d: set default boundary ids for "boundary vertices"
9603 if (dim == 2)
9604 {
9605 for (const auto &cell : surface_mesh.active_cell_iterators())
9606 for (unsigned int vertex = 0; vertex < 2; ++vertex)
9607 if (cell->face(vertex)->at_boundary())
9608 cell->face(vertex)->set_boundary_id(0);
9609 }
9610
9611 // Make mapping for level 0
9612
9613 // temporary map between cells on the boundary and corresponding faces of
9614 // domain mesh (each face is characterized by an iterator to the face and
9615 // the face number within the underlying cell)
9616 std::vector<std::pair<
9617 const typename MeshType<dim - 1, spacedim>::cell_iterator,
9618 std::pair<typename MeshType<dim, spacedim>::face_iterator, unsigned int>>>
9619 temporary_map_boundary_cell_face;
9620 for (const auto &cell : surface_mesh.active_cell_iterators())
9621 temporary_map_boundary_cell_face.push_back(
9622 std::make_pair(cell, temporary_mapping_level0.at(cell->index())));
9623
9624
9625 // refine the boundary mesh according to the refinement of the underlying
9626 // volume mesh,
9627 // algorithm:
9628 // (1) check which cells on refinement level i need to be refined
9629 // (2) do refinement (yields cells on level i+1)
9630 // (3) repeat for the next level (i+1->i) until refinement is completed
9631
9632 // stores the index into temporary_map_boundary_cell_face at which
9633 // presently deepest refinement level of boundary mesh begins
9634 unsigned int index_cells_deepest_level = 0;
9635 do
9636 {
9637 bool changed = false;
9638
9639 // vector storing cells which have been marked for
9640 // refinement
9641 std::vector<unsigned int> cells_refined;
9642
9643 // loop over cells of presently deepest level of boundary
9644 // triangulation
9645 for (unsigned int cell_n = index_cells_deepest_level;
9646 cell_n < temporary_map_boundary_cell_face.size();
9647 cell_n++)
9648 {
9649 // mark boundary cell for refinement if underlying volume face has
9650 // children
9651 if (temporary_map_boundary_cell_face[cell_n]
9652 .second.first->has_children())
9653 {
9654 // algorithm only works for
9655 // isotropic refinement!
9656 Assert(temporary_map_boundary_cell_face[cell_n]
9657 .second.first->refinement_case() ==
9660 temporary_map_boundary_cell_face[cell_n]
9661 .first->set_refine_flag();
9662 cells_refined.push_back(cell_n);
9663 changed = true;
9664 }
9665 }
9666
9667 // if cells have been marked for refinement (i.e., presently deepest
9668 // level is not the deepest level of the volume mesh)
9669 if (changed)
9670 {
9671 // do actual refinement
9672 const_cast<Triangulation<dim - 1, spacedim> &>(
9673 surface_mesh.get_triangulation())
9674 .execute_coarsening_and_refinement();
9675
9676 // add new level of cells to temporary_map_boundary_cell_face
9677 index_cells_deepest_level = temporary_map_boundary_cell_face.size();
9678 for (const auto &refined_cell_n : cells_refined)
9679 {
9680 const typename MeshType<dim - 1, spacedim>::cell_iterator
9681 refined_cell =
9682 temporary_map_boundary_cell_face[refined_cell_n].first;
9683 const typename MeshType<dim,
9684 spacedim>::face_iterator refined_face =
9685 temporary_map_boundary_cell_face[refined_cell_n].second.first;
9686 const unsigned int refined_face_number =
9687 temporary_map_boundary_cell_face[refined_cell_n]
9688 .second.second;
9689 for (unsigned int child_n = 0;
9690 child_n < refined_cell->n_children();
9691 ++child_n)
9692 // at this point, the swapping of vertices done earlier must
9693 // be taken into account to get the right association
9694 // between volume faces and boundary cells!
9695 temporary_map_boundary_cell_face.push_back(
9696 std::make_pair(refined_cell->child(
9697 swap_matrix[refined_face_number][child_n]),
9698 std::make_pair(refined_face->child(child_n),
9699 refined_face_number)));
9700 }
9701 }
9702 // we are at the deepest level of refinement of the volume mesh
9703 else
9704 break;
9705 }
9706 while (true);
9707
9708 // generate the final mapping from the temporary mapping
9709 std::map<typename MeshType<dim - 1, spacedim>::cell_iterator,
9710 typename MeshType<dim, spacedim>::face_iterator>
9711 surface_to_volume_mapping;
9712 for (unsigned int i = 0; i < temporary_map_boundary_cell_face.size(); ++i)
9713 surface_to_volume_mapping[temporary_map_boundary_cell_face[i].first] =
9714 temporary_map_boundary_cell_face[i].second.first;
9715
9716 // TODO: we attach flat manifolds here; one should attach submanifolds
9717 // here
9718 const auto attached_mids =
9719 surface_mesh.get_triangulation().get_manifold_ids();
9720 for (const auto i : volume_mesh.get_triangulation().get_manifold_ids())
9721 if (i != numbers::flat_manifold_id &&
9722 std::find(attached_mids.begin(), attached_mids.end(), i) ==
9723 attached_mids.end())
9724 const_cast<Triangulation<dim - 1, spacedim> &>(
9725 surface_mesh.get_triangulation())
9726 .set_manifold(i, FlatManifold<dim - 1, spacedim>());
9727
9728 return surface_to_volume_mapping;
9729 }
9730
9731
9732
9733 template <int dim, int spacedim>
9734 void
9737 const std::vector<unsigned int> &repetitions,
9738 const Point<dim> &p1,
9739 const Point<dim> &p2,
9740 const bool colorize)
9741 {
9742 AssertDimension(dim, spacedim);
9743
9744 std::vector<Point<spacedim>> vertices;
9745 std::vector<CellData<dim>> cells;
9746
9747 if (dim == 2)
9748 {
9749 // determine cell sizes
9750 const Point<dim> dx((p2[0] - p1[0]) / repetitions[0],
9751 (p2[1] - p1[1]) / repetitions[1]);
9752
9753 // create vertices
9754 for (unsigned int j = 0; j <= repetitions[1]; ++j)
9755 for (unsigned int i = 0; i <= repetitions[0]; ++i)
9756 vertices.push_back(
9757 Point<spacedim>(p1[0] + dx[0] * i, p1[1] + dx[1] * j));
9758
9759 // create cells
9760 for (unsigned int j = 0; j < repetitions[1]; ++j)
9761 for (unsigned int i = 0; i < repetitions[0]; ++i)
9762 {
9763 // create reference QUAD cell
9764 std::array<unsigned int, 4> quad{{
9765 (j + 0) * (repetitions[0] + 1) + i + 0, //
9766 (j + 0) * (repetitions[0] + 1) + i + 1, //
9767 (j + 1) * (repetitions[0] + 1) + i + 0, //
9768 (j + 1) * (repetitions[0] + 1) + i + 1 //
9769 }}; //
9770
9771 // TRI cell 0
9772 {
9773 CellData<dim> tri;
9774 tri.vertices = {quad[0], quad[1], quad[2]};
9775 cells.emplace_back(std::move(tri));
9776 }
9777
9778 // TRI cell 1
9779 {
9780 CellData<dim> tri;
9781 tri.vertices = {quad[3], quad[2], quad[1]};
9782 cells.emplace_back(std::move(tri));
9783 }
9784 }
9785 }
9786 else if (dim == 3)
9787 {
9788 // determine cell sizes
9789 const Point<dim> dx((p2[0] - p1[0]) / repetitions[0],
9790 (p2[1] - p1[1]) / repetitions[1],
9791 (p2[2] - p1[2]) / repetitions[2]);
9792
9793 // create vertices
9794 for (unsigned int k = 0; k <= repetitions[2]; ++k)
9795 for (unsigned int j = 0; j <= repetitions[1]; ++j)
9796 for (unsigned int i = 0; i <= repetitions[0]; ++i)
9797 vertices.push_back(Point<spacedim>(p1[0] + dx[0] * i,
9798 p1[1] + dx[1] * j,
9799 p1[2] + dx[2] * k));
9800
9801 // create cells
9802 for (unsigned int k = 0; k < repetitions[2]; ++k)
9803 for (unsigned int j = 0; j < repetitions[1]; ++j)
9804 for (unsigned int i = 0; i < repetitions[0]; ++i)
9805 {
9806 // create reference HEX cell
9807 std::array<unsigned int, 8> quad{
9808 {(k + 0) * (repetitions[0] + 1) * (repetitions[1] + 1) +
9809 (j + 0) * (repetitions[0] + 1) + i + 0,
9810 (k + 0) * (repetitions[0] + 1) * (repetitions[1] + 1) +
9811 (j + 0) * (repetitions[0] + 1) + i + 1,
9812 (k + 0) * (repetitions[0] + 1) * (repetitions[1] + 1) +
9813 (j + 1) * (repetitions[0] + 1) + i + 0,
9814 (k + 0) * (repetitions[0] + 1) * (repetitions[1] + 1) +
9815 (j + 1) * (repetitions[0] + 1) + i + 1,
9816 (k + 1) * (repetitions[0] + 1) * (repetitions[1] + 1) +
9817 (j + 0) * (repetitions[0] + 1) + i + 0,
9818 (k + 1) * (repetitions[0] + 1) * (repetitions[1] + 1) +
9819 (j + 0) * (repetitions[0] + 1) + i + 1,
9820 (k + 1) * (repetitions[0] + 1) * (repetitions[1] + 1) +
9821 (j + 1) * (repetitions[0] + 1) + i + 0,
9822 (k + 1) * (repetitions[0] + 1) * (repetitions[1] + 1) +
9823 (j + 1) * (repetitions[0] + 1) + i + 1}};
9824
9825 // TET cell 0
9826 {
9827 CellData<dim> cell;
9828 if (((i % 2) + (j % 2) + (k % 2)) % 2 == 0)
9829 cell.vertices = {{quad[0], quad[1], quad[2], quad[4]}};
9830 else
9831 cell.vertices = {{quad[0], quad[1], quad[3], quad[5]}};
9832
9833 cells.emplace_back(std::move(cell));
9834 }
9835
9836 // TET cell 1
9837 {
9838 CellData<dim> cell;
9839 if (((i % 2) + (j % 2) + (k % 2)) % 2 == 0)
9840 cell.vertices = {{quad[2], quad[1], quad[3], quad[7]}};
9841 else
9842 cell.vertices = {{quad[0], quad[3], quad[2], quad[6]}};
9843 cells.emplace_back(std::move(cell));
9844 }
9845
9846 // TET cell 2
9847 {
9848 CellData<dim> cell;
9849 if (((i % 2) + (j % 2) + (k % 2)) % 2 == 0)
9850 cell.vertices = {{quad[1], quad[4], quad[5], quad[7]}};
9851 else
9852 cell.vertices = {{quad[0], quad[4], quad[5], quad[6]}};
9853 cells.emplace_back(std::move(cell));
9854 }
9855
9856 // TET cell 3
9857 {
9858 CellData<dim> cell;
9859 if (((i % 2) + (j % 2) + (k % 2)) % 2 == 0)
9860 cell.vertices = {{quad[2], quad[4], quad[7], quad[6]}};
9861 else
9862 cell.vertices = {{quad[3], quad[5], quad[7], quad[6]}};
9863 cells.emplace_back(std::move(cell));
9864 }
9865
9866 // TET cell 4
9867 {
9868 CellData<dim> cell;
9869 if (((i % 2) + (j % 2) + (k % 2)) % 2 == 0)
9870 cell.vertices = {{quad[1], quad[2], quad[4], quad[7]}};
9871 else
9872 cell.vertices = {{quad[0], quad[3], quad[6], quad[5]}};
9873 cells.emplace_back(std::move(cell));
9874 }
9875 }
9876 }
9877 else
9878 {
9880 }
9881
9882 // actually create triangulation
9883 tria.create_triangulation(vertices, cells, SubCellData());
9884
9885 if (colorize)
9886 {
9887 // to colorize, run through all
9888 // faces of all cells and set
9889 // boundary indicator to the
9890 // correct value if it was 0.
9891
9892 // use a large epsilon to
9893 // compare numbers to avoid
9894 // roundoff problems.
9895 double epsilon = std::numeric_limits<double>::max();
9896 for (unsigned int i = 0; i < dim; ++i)
9897 epsilon = std::min(epsilon,
9898 0.01 * (std::abs(p2[i] - p1[i]) / repetitions[i]));
9899 Assert(epsilon > 0,
9900 ExcMessage(
9901 "The distance between corner points must be positive."));
9902
9903 // actual code is external since
9904 // 1-D is different from 2/3d.
9905 colorize_subdivided_hyper_rectangle(tria, p1, p2, epsilon);
9906 }
9907 }
9908
9909
9910
9911 template <int dim, int spacedim>
9912 void
9914 const unsigned int repetitions,
9915 const double left,
9916 const double right,
9917 const bool colorize)
9918 {
9919 if (dim == 2)
9920 {
9922 {{repetitions, repetitions}},
9923 {left, left},
9924 {right, right},
9925 colorize);
9926 }
9927 else if (dim == 3)
9928 {
9930 tria,
9931 {{repetitions, repetitions, repetitions}},
9932 {left, left, left},
9933 {right, right, right},
9934 colorize);
9935 }
9936 else
9937 {
9939 }
9940 }
9941
9942
9943
9944 template <>
9945 void
9947 const double inner_radius,
9948 const double outer_radius,
9949 const bool colorize)
9950 {
9951 AssertThrow(inner_radius > 0, ExcMessage("inner_radius must be positive."));
9952 AssertThrow(inner_radius < outer_radius,
9953 ExcMessage("outer_radius has to be bigger than inner_radius."));
9954
9955 const Point<3> center;
9956
9957 hyper_shell(tria, center, inner_radius, outer_radius, 6);
9958 for (const auto &cell : tria.active_cell_iterators())
9959 {
9960 for (auto f : GeometryInfo<3>::face_indices())
9961 if (cell->face(f)->at_boundary())
9962 for (const unsigned int v : cell->face(f)->vertex_indices())
9963 if (std::fabs(cell->face(f)->vertex(v).norm() - outer_radius) <
9964 1e-12 * outer_radius)
9965 {
9966 const double ax = std::fabs(cell->face(f)->vertex(v)[0]);
9967 const double ay = std::fabs(cell->face(f)->vertex(v)[1]);
9968 const double az = std::fabs(cell->face(f)->vertex(v)[2]);
9969 const double max_coord = std::max({ax, ay, az});
9970 cell->face(f)->vertex(v) *= outer_radius / max_coord;
9971 }
9972 }
9973
9974 // Set boundary and manifold IDs
9975 const double eps = 1e-6 * outer_radius;
9977
9978 for (const auto &cell : tria.active_cell_iterators())
9979 for (const unsigned int f : cell->face_indices())
9980 if (cell->face(f)->at_boundary())
9981 {
9982 bool vertex_on_sphere = true;
9983 for (const unsigned int v : cell->face(f)->vertex_indices())
9984 if (std::abs(cell->face(f)->vertex(v).norm() - inner_radius) >
9985 eps)
9986 {
9987 vertex_on_sphere = false;
9988 break;
9989 }
9990
9991 if (vertex_on_sphere)
9992 {
9993 cell->face(f)->set_all_manifold_ids(0);
9994 cell->face(f)->set_boundary_id(colorize ? 6 : 1);
9995 }
9996 else if (colorize)
9997 {
9998 const Point<3> fc = cell->face(f)->center();
9999 if (std::abs(fc[0] + outer_radius) < eps)
10000 cell->face(f)->set_boundary_id(0);
10001 else if (std::abs(fc[0] - outer_radius) < eps)
10002 cell->face(f)->set_boundary_id(1);
10003 else if (std::abs(fc[1] + outer_radius) < eps)
10004 cell->face(f)->set_boundary_id(2);
10005 else if (std::abs(fc[1] - outer_radius) < eps)
10006 cell->face(f)->set_boundary_id(3);
10007 else if (std::abs(fc[2] + outer_radius) < eps)
10008 cell->face(f)->set_boundary_id(4);
10009 else if (std::abs(fc[2] - outer_radius) < eps)
10010 cell->face(f)->set_boundary_id(5);
10011 }
10012 }
10014 }
10015
10016
10017
10018 template <>
10019 void
10021 const double,
10022 const double,
10023 const bool)
10024 {
10026 }
10027
10028
10029 template <>
10030 void
10032 const double,
10033 const double,
10034 const bool)
10035 {
10037 }
10038
10039
10040 template <>
10041 void
10043 Triangulation<3> &tria,
10044 const std::vector<unsigned int> &lengths_heights_widths,
10045 const double inner_radius,
10046 const double outer_radius,
10047 const bool use_transfinite_region,
10048 const bool colorize)
10049 {
10050 AssertThrow(inner_radius > 0, ExcMessage("inner_radius must be positive."));
10051 AssertThrow(inner_radius < outer_radius,
10052 ExcMessage("outer_radius has to be bigger than inner_radius."));
10053
10054 // Distances
10055 const double length_pre = lengths_heights_widths[0] * 2 * outer_radius;
10056 const double length_post = lengths_heights_widths[1] * 2 * outer_radius;
10057 const double height_below = lengths_heights_widths[2] * 2 * outer_radius;
10058 const double height_above = lengths_heights_widths[3] * 2 * outer_radius;
10059 const double width_front = lengths_heights_widths[4] * 2 * outer_radius;
10060 const double width_back = lengths_heights_widths[5] * 2 * outer_radius;
10061
10062 // Repetitions for resp. length, height and width
10063 const unsigned int length_pre_repetitions = lengths_heights_widths[0];
10064 const unsigned int length_post_repetitions = lengths_heights_widths[1];
10065 const unsigned int height_below_repetitions = lengths_heights_widths[2];
10066 const unsigned int height_above_repetitions = lengths_heights_widths[3];
10067 const unsigned int width_front_repetitions = lengths_heights_widths[4];
10068 const unsigned int width_back_repetitions = lengths_heights_widths[5];
10069
10070 Triangulation<3> bulk_tria;
10072 bulk_tria,
10073 {length_pre_repetitions + length_post_repetitions + 1,
10074 height_below_repetitions + height_above_repetitions + 1,
10075 width_front_repetitions + width_back_repetitions + 1},
10076 Point<3>(-double(length_pre + outer_radius),
10077 -double(height_below + outer_radius),
10078 -double(width_front + outer_radius)),
10079 Point<3>(double(length_post + outer_radius),
10080 double(height_above + outer_radius),
10081 double(width_back + outer_radius)));
10082
10083 Triangulation<3> tria_without_cylinder;
10084 std::set<Triangulation<3>::active_cell_iterator> cells_to_remove;
10085 for (const auto &cell : bulk_tria.active_cell_iterators())
10086 {
10087 const Point<3> center = cell->center();
10088 if (std::abs(center[0]) < outer_radius &&
10089 std::abs(center[1]) < outer_radius &&
10090 std::abs(center[2]) < outer_radius)
10091 cells_to_remove.insert(cell);
10092 }
10093
10094 Triangulation<3> tria_without_sphere;
10096 cells_to_remove,
10097 tria_without_sphere);
10098
10099 Triangulation<3> sphere_tria;
10100 const double shell_radius = 0.5 * (inner_radius + outer_radius);
10102 shell_radius,
10104 false);
10105
10106 Triangulation<3> shell_tria;
10108 shell_tria, Point<3>(), inner_radius, shell_radius, 1, 0.1, 6, false);
10109
10110 const double vertex_tol =
10111 std::min(internal::minimal_vertex_distance(shell_tria),
10112 internal::minimal_vertex_distance(sphere_tria)) *
10113 0.5;
10114
10115 Triangulation<3> temp;
10117 shell_tria, sphere_tria, temp, vertex_tol, true);
10118 sphere_tria = std::move(temp);
10119
10120 const double vertex_tolerance =
10121 std::min(internal::minimal_vertex_distance(tria_without_sphere),
10122 internal::minimal_vertex_distance(sphere_tria)) /
10123 10;
10124
10126 tria_without_sphere, sphere_tria, tria, vertex_tolerance, true);
10127
10128 tria.reset_all_manifolds();
10129 tria.set_all_manifold_ids(0);
10130
10131 for (const auto &cell : tria.cell_iterators())
10132 {
10133 for (const auto &face : cell->face_iterators())
10134 {
10135 bool face_spherical = true;
10136 for (const auto v : face->vertex_indices())
10137 {
10138 if (face->vertex(v).norm_square() > std::sqrt(inner_radius))
10139 face_spherical = false;
10140 }
10141 if (face_spherical)
10142 face->set_all_manifold_ids(1);
10143 }
10144 }
10145
10147
10148 if (use_transfinite_region)
10149 {
10150 TransfiniteInterpolationManifold<3> transfinite_manifold;
10151 transfinite_manifold.initialize(tria);
10152 tria.set_manifold(0, transfinite_manifold);
10153 }
10154 else
10155 tria.set_manifold(0, FlatManifold<3>());
10156
10157 // Set boundary IDs
10158 if (colorize)
10159 {
10160 const double tol = 1e-6;
10161 for (const auto &face : tria.active_face_iterators())
10162 if (face->at_boundary())
10163 {
10164 const Point<3> center = face->center();
10165 // Left
10166 if (std::abs(center[0] - (-static_cast<double>(
10167 length_pre + outer_radius))) < tol)
10168 face->set_boundary_id(0);
10169 // Right
10170 else if (std::abs(center[0] - static_cast<double>(length_post +
10171 outer_radius)) <
10172 tol)
10173 face->set_boundary_id(1);
10174 // Sphere boundary
10175 else if (face->manifold_id() == 1)
10176 face->set_boundary_id(2);
10177 // Bottom
10178 else if (std::abs(center[1] - (-static_cast<double>(
10179 height_below + outer_radius))) <
10180 tol)
10181 face->set_boundary_id(3);
10182 // Top
10183 else if (std::abs(center[1] - static_cast<double>(height_above +
10184 outer_radius)) <
10185 tol)
10186 face->set_boundary_id(4);
10187 // Front (z negative)
10188 else if (std::abs(center[2] - (-static_cast<double>(
10189 width_front + outer_radius))) <
10190 tol)
10191 face->set_boundary_id(5);
10192 // Back (z positive)
10193 else if (std::abs(center[2] - static_cast<double>(
10194 width_back + outer_radius)) < tol)
10195 face->set_boundary_id(6);
10196 }
10197 }
10198 }
10199
10200
10201
10202 template <>
10203 void
10205 const std::vector<unsigned int> &,
10206 const double,
10207 const double,
10208 const bool,
10209 const bool)
10210 {
10212 }
10213
10214
10215
10216 template <>
10217 void
10219 const std::vector<unsigned int> &,
10220 const double,
10221 const double,
10222 const bool,
10223 const bool)
10224 {
10226 }
10227} // namespace GridGenerator
10228
10229// explicit instantiations
10230# include "grid/grid_generator.inst"
10231
10232#endif // DOXYGEN
10233
*  iterator end()
*  const Number height
*  *  for(const auto &cell :triangulation.active_cell_iterators())
*  const Number radius
*  *  iterator begin()
*  *  const Number outer_radius
***mech_lbc_system increment_interpolation_handlers push_back(scale_z_handler)
ArrayView< std::remove_reference_t< typename std::iterator_traits< Iterator >::reference >, MemorySpaceType > make_array_view(const Iterator begin, const Iterator end)
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
void initialize(const Triangulation< dim, spacedim > &triangulation)
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:4609
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:249
#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()
Point< 2 > second
Definition grid_out.cc:4640
bool colorize
Definition grid_out.cc:4641
Point< 2 > first
Definition grid_out.cc:4639
unsigned int vertex_indices[2]
unsigned int cell_index
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)
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 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
std::vector< index_type > data
Definition mpi.cc:734
std::size_t size
Definition mpi.cc:733
CGAL::Exact_predicates_exact_constructions_kernel_with_sqrt K
Expression fabs(const Expression &x)
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:190
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)
*  *  if(update_pressure &update_flags) *  compute_pressure(constitutive_request
*  *  *  *  std::vector< Number > ThermoPlasticMaterial< dim, ViscoplasticYieldLaw, Number >::get_state_parameters   const
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:15808
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:228
constexpr types::boundary_id internal_face_boundary_id
Definition types.h:319
constexpr types::boundary_id invalid_boundary_id
Definition types.h:299
constexpr types::manifold_id flat_manifold_id
Definition types.h:332
constexpr types::material_id invalid_material_id
Definition types.h:284
STL namespace.
::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 manifold_id
Definition types.h:171
unsigned int material_id
Definition types.h:182
unsigned int boundary_id
Definition types.h:159
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)
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 > &)