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