Processing math: 0%
 deal.II version GIT relicensing-3087-ga35b476a78 2025-04-19 20:30:01+00:00
\newcommand{\dealvcentcolon}{\mathrel{\mathop{:}}} \newcommand{\dealcoloneq}{\dealvcentcolon\mathrel{\mkern-1.2mu}=} \newcommand{\jump}[1]{\left[\!\left[ #1 \right]\!\right]} \newcommand{\average}[1]{\left\{\!\left\{ #1 \right\}\!\right\}}
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages Concepts
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 - 2024 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
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) -
673 0.1036 * Utilities::fixed_power<4>(
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) ?
690 m / Utilities::fixed_power<2>(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) -
704 0.1036 * Utilities::fixed_power<4>(
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 (void)count;
1436 Assert(count == 48, ExcInternalError());
1437 }
1438 else
1440 }
1441
1442
1443
1449 void
1450 colorize_quarter_hyper_shell(Triangulation<3> &tria,
1451 const Point<3> &center,
1452 const double inner_radius,
1453 const double outer_radius)
1454 {
1455 if (tria.n_cells() != 3)
1457
1458 double middle = (outer_radius - inner_radius) / 2e0 + inner_radius;
1459 double eps = 1e-3 * middle;
1461
1462 for (; cell != tria.end(); ++cell)
1463 for (const unsigned int f : GeometryInfo<3>::face_indices())
1464 {
1465 if (!cell->face(f)->at_boundary())
1466 continue;
1467
1468 double radius = cell->face(f)->center().norm() - center.norm();
1469 if (std::fabs(cell->face(f)->center()[0]) <
1470 eps) // x = 0 set boundary 2
1471 {
1472 cell->face(f)->set_boundary_id(2);
1473 for (unsigned int j = 0; j < GeometryInfo<3>::lines_per_face;
1474 ++j)
1475 if (cell->face(f)->line(j)->at_boundary())
1476 if (std::fabs(cell->face(f)->line(j)->vertex(0).norm() -
1477 cell->face(f)->line(j)->vertex(1).norm()) >
1478 eps)
1479 cell->face(f)->line(j)->set_boundary_id(2);
1480 }
1481 else if (std::fabs(cell->face(f)->center()[1]) <
1482 eps) // y = 0 set boundary 3
1483 {
1484 cell->face(f)->set_boundary_id(3);
1485 for (unsigned int j = 0; j < GeometryInfo<3>::lines_per_face;
1486 ++j)
1487 if (cell->face(f)->line(j)->at_boundary())
1488 if (std::fabs(cell->face(f)->line(j)->vertex(0).norm() -
1489 cell->face(f)->line(j)->vertex(1).norm()) >
1490 eps)
1491 cell->face(f)->line(j)->set_boundary_id(3);
1492 }
1493 else if (std::fabs(cell->face(f)->center()[2]) <
1494 eps) // z = 0 set boundary 4
1495 {
1496 cell->face(f)->set_boundary_id(4);
1497 for (unsigned int j = 0; j < GeometryInfo<3>::lines_per_face;
1498 ++j)
1499 if (cell->face(f)->line(j)->at_boundary())
1500 if (std::fabs(cell->face(f)->line(j)->vertex(0).norm() -
1501 cell->face(f)->line(j)->vertex(1).norm()) >
1502 eps)
1503 cell->face(f)->line(j)->set_boundary_id(4);
1504 }
1505 else if (radius < middle) // inner radius set boundary 0
1506 {
1507 cell->face(f)->set_boundary_id(0);
1508 for (unsigned int j = 0; j < GeometryInfo<3>::lines_per_face;
1509 ++j)
1510 if (cell->face(f)->line(j)->at_boundary())
1511 if (std::fabs(cell->face(f)->line(j)->vertex(0).norm() -
1512 cell->face(f)->line(j)->vertex(1).norm()) <
1513 eps)
1514 cell->face(f)->line(j)->set_boundary_id(0);
1515 }
1516 else if (radius > middle) // outer radius set boundary 1
1517 {
1518 cell->face(f)->set_boundary_id(1);
1519 for (unsigned int j = 0; j < GeometryInfo<3>::lines_per_face;
1520 ++j)
1521 if (cell->face(f)->line(j)->at_boundary())
1522 if (std::fabs(cell->face(f)->line(j)->vertex(0).norm() -
1523 cell->face(f)->line(j)->vertex(1).norm()) <
1524 eps)
1525 cell->face(f)->line(j)->set_boundary_id(1);
1526 }
1527 else
1529 }
1530 }
1531
1532 } // namespace
1533
1534
1535 template <int dim, int spacedim>
1536 void
1538 const Point<dim> &p_1,
1539 const Point<dim> &p_2,
1540 const bool colorize)
1541 {
1542 // First, extend dimensions from dim to spacedim and
1543 // normalize such that p1 is lower in all coordinate
1544 // directions. Additional entries will be 0.
1545 Point<spacedim> p1, p2;
1546 for (unsigned int i = 0; i < dim; ++i)
1547 {
1548 p1[i] = std::min(p_1[i], p_2[i]);
1549 p2[i] = std::max(p_1[i], p_2[i]);
1550 }
1551
1552 std::vector<Point<spacedim>> vertices(GeometryInfo<dim>::vertices_per_cell);
1553 switch (dim)
1554 {
1555 case 1:
1556 vertices[0] = p1;
1557 vertices[1] = p2;
1558 break;
1559 case 2:
1560 vertices[0] = vertices[1] = p1;
1561 vertices[2] = vertices[3] = p2;
1562
1563 vertices[1][0] = p2[0];
1564 vertices[2][0] = p1[0];
1565 break;
1566 case 3:
1567 vertices[0] = vertices[1] = vertices[2] = vertices[3] = p1;
1568 vertices[4] = vertices[5] = vertices[6] = vertices[7] = p2;
1569
1570 vertices[1][0] = p2[0];
1571 vertices[2][1] = p2[1];
1572 vertices[3][0] = p2[0];
1573 vertices[3][1] = p2[1];
1574
1575 vertices[4][0] = p1[0];
1576 vertices[4][1] = p1[1];
1577 vertices[5][1] = p1[1];
1578 vertices[6][0] = p1[0];
1579
1580 break;
1581 default:
1583 }
1584
1585 // Prepare cell data
1586 std::vector<CellData<dim>> cells(1);
1587 for (const unsigned int i : GeometryInfo<dim>::vertex_indices())
1588 cells[0].vertices[i] = i;
1589 cells[0].material_id = 0;
1590
1591 tria.create_triangulation(vertices, cells, SubCellData());
1592
1593 // Assign boundary indicators
1594 if (colorize)
1595 colorize_hyper_rectangle(tria);
1596 }
1597
1598
1599
1600 template <int dim, int spacedim>
1601 void
1603 const double left,
1604 const double right,
1605 const bool colorize)
1606 {
1607 Assert(left < right,
1608 ExcMessage("Invalid left-to-right bounds of hypercube"));
1609
1610 Point<dim> p1, p2;
1611 for (unsigned int i = 0; i < dim; ++i)
1612 {
1613 p1[i] = left;
1614 p2[i] = right;
1615 }
1616 hyper_rectangle(tria, p1, p2, colorize);
1617 }
1618
1619
1620
1621 template <int dim>
1622 void
1623 simplex(Triangulation<dim> &tria, const std::vector<Point<dim>> &vertices)
1624 {
1625 AssertDimension(vertices.size(), dim + 1);
1626 Assert(dim > 1, ExcNotImplemented());
1627 Assert(dim < 4, ExcNotImplemented());
1628
1629 if constexpr (running_in_debug_mode())
1630 {
1631 Tensor<2, dim> vector_matrix;
1632 for (unsigned int d = 0; d < dim; ++d)
1633 for (unsigned int c = 1; c <= dim; ++c)
1634 vector_matrix[c - 1][d] = vertices[c][d] - vertices[0][d];
1635 Assert(determinant(vector_matrix) > 0.,
1636 ExcMessage(
1637 "Vertices of simplex must form a right handed system"));
1638 }
1639
1640 // Set up the vertices by first copying into points.
1641 std::vector<Point<dim>> points = vertices;
1642 Point<dim> center;
1643 // Compute the edge midpoints and add up everything to compute the
1644 // center point.
1645 for (unsigned int i = 0; i <= dim; ++i)
1646 {
1647 points.push_back(0.5 * (points[i] + points[(i + 1) % (dim + 1)]));
1648 center += points[i];
1649 }
1650 if (dim > 2)
1651 {
1652 // In 3d, we have some more edges to deal with
1653 for (unsigned int i = 1; i < dim; ++i)
1654 points.push_back(0.5 * (points[i - 1] + points[i + 1]));
1655 // And we need face midpoints
1656 for (unsigned int i = 0; i <= dim; ++i)
1657 points.push_back(1. / 3. *
1658 (points[i] + points[(i + 1) % (dim + 1)] +
1659 points[(i + 2) % (dim + 1)]));
1660 }
1661 points.push_back((1. / (dim + 1)) * center);
1662
1663 std::vector<CellData<dim>> cells(dim + 1);
1664 switch (dim)
1665 {
1666 case 2:
1667 AssertDimension(points.size(), 7);
1668 cells[0].vertices[0] = 0;
1669 cells[0].vertices[1] = 3;
1670 cells[0].vertices[2] = 5;
1671 cells[0].vertices[3] = 6;
1672 cells[0].material_id = 0;
1673
1674 cells[1].vertices[0] = 3;
1675 cells[1].vertices[1] = 1;
1676 cells[1].vertices[2] = 6;
1677 cells[1].vertices[3] = 4;
1678 cells[1].material_id = 0;
1679
1680 cells[2].vertices[0] = 5;
1681 cells[2].vertices[1] = 6;
1682 cells[2].vertices[2] = 2;
1683 cells[2].vertices[3] = 4;
1684 cells[2].material_id = 0;
1685 break;
1686 case 3:
1687 AssertDimension(points.size(), 15);
1688 cells[0].vertices[0] = 0;
1689 cells[0].vertices[1] = 4;
1690 cells[0].vertices[2] = 8;
1691 cells[0].vertices[3] = 10;
1692 cells[0].vertices[4] = 7;
1693 cells[0].vertices[5] = 13;
1694 cells[0].vertices[6] = 12;
1695 cells[0].vertices[7] = 14;
1696 cells[0].material_id = 0;
1697
1698 cells[1].vertices[0] = 4;
1699 cells[1].vertices[1] = 1;
1700 cells[1].vertices[2] = 10;
1701 cells[1].vertices[3] = 5;
1702 cells[1].vertices[4] = 13;
1703 cells[1].vertices[5] = 9;
1704 cells[1].vertices[6] = 14;
1705 cells[1].vertices[7] = 11;
1706 cells[1].material_id = 0;
1707
1708 cells[2].vertices[0] = 8;
1709 cells[2].vertices[1] = 10;
1710 cells[2].vertices[2] = 2;
1711 cells[2].vertices[3] = 5;
1712 cells[2].vertices[4] = 12;
1713 cells[2].vertices[5] = 14;
1714 cells[2].vertices[6] = 6;
1715 cells[2].vertices[7] = 11;
1716 cells[2].material_id = 0;
1717
1718 cells[3].vertices[0] = 7;
1719 cells[3].vertices[1] = 13;
1720 cells[3].vertices[2] = 12;
1721 cells[3].vertices[3] = 14;
1722 cells[3].vertices[4] = 3;
1723 cells[3].vertices[5] = 9;
1724 cells[3].vertices[6] = 6;
1725 cells[3].vertices[7] = 11;
1726 cells[3].material_id = 0;
1727 break;
1728 default:
1730 }
1731 tria.create_triangulation(points, cells, SubCellData());
1732 }
1733
1734
1735
1736 template <int dim, int spacedim>
1737 void
1739 const ReferenceCell &reference_cell)
1740 {
1741 AssertDimension(dim, reference_cell.get_dimension());
1742
1743 if (reference_cell == ReferenceCells::get_hypercube<dim>())
1744 {
1745 GridGenerator::hyper_cube(tria, 0, 1);
1746 }
1747 else
1748 {
1749 // Create an array that contains the vertices of the reference cell.
1750 // We can query these points from ReferenceCell, but then we have
1751 // to embed them into the spacedim-dimensional space.
1752 std::vector<Point<spacedim>> vertices(reference_cell.n_vertices());
1753 for (const unsigned int v : reference_cell.vertex_indices())
1754 {
1755 const Point<dim> this_vertex = reference_cell.vertex<dim>(v);
1756 for (unsigned int d = 0; d < dim; ++d)
1757 vertices[v][d] = this_vertex[d];
1758 // Point<spacedim> initializes everything to zero, so any remaining
1759 // elements are left at zero and we don't have to explicitly pad
1760 // from 'dim' to 'spacedim' here.
1761 }
1762
1763 // Then make one cell out of these vertices. They are ordered correctly
1764 // already, so we just need to enumerate them
1765 std::vector<CellData<dim>> cells(1);
1766 cells[0].vertices.resize(reference_cell.n_vertices());
1767 for (const unsigned int v : reference_cell.vertex_indices())
1768 cells[0].vertices[v] = v;
1769
1770 // Turn all of this into a triangulation
1771 tria.create_triangulation(vertices, cells, {});
1772 }
1773 }
1774
1775 void
1777 const unsigned int n_cells,
1778 const unsigned int n_rotations,
1779 const double R,
1780 const double r)
1781 {
1782 const unsigned int dim = 3;
1783 Assert(n_cells > 4,
1784 ExcMessage(
1785 "More than 4 cells are needed to create a moebius grid."));
1786 Assert(r > 0 && R > 0,
1787 ExcMessage("Outer and inner radius must be positive."));
1788 Assert(R > r,
1789 ExcMessage("Outer radius must be greater than inner radius."));
1790
1791
1792 std::vector<Point<dim>> vertices(4 * n_cells);
1793 double beta_step = n_rotations * numbers::PI / 2.0 / n_cells;
1794 double alpha_step = 2.0 * numbers::PI / n_cells;
1795
1796 for (unsigned int i = 0; i < n_cells; ++i)
1797 for (unsigned int j = 0; j < 4; ++j)
1798 {
1799 vertices[4 * i + j][0] =
1800 R * std::cos(i * alpha_step) +
1801 r * std::cos(i * beta_step + j * numbers::PI / 2.0) *
1802 std::cos(i * alpha_step);
1803 vertices[4 * i + j][1] =
1804 R * std::sin(i * alpha_step) +
1805 r * std::cos(i * beta_step + j * numbers::PI / 2.0) *
1806 std::sin(i * alpha_step);
1807 vertices[4 * i + j][2] =
1808 r * std::sin(i * beta_step + j * numbers::PI / 2.0);
1809 }
1810
1811 unsigned int offset = 0;
1812
1813 // This Triangulation is constructed using a numbering scheme in which
1814 // the front face is first and the back face is second,
1815 // which is more convenient for creating a Moebius loop
1816 static constexpr std::array<unsigned int, 8> local_vertex_numbering{
1817 {0, 1, 5, 4, 2, 3, 7, 6}};
1818 std::vector<CellData<dim>> cells(n_cells);
1819 for (unsigned int i = 0; i < n_cells; ++i)
1820 {
1821 for (unsigned int j = 0; j < 2; ++j)
1822 {
1823 cells[i].vertices[local_vertex_numbering[0 + 4 * j]] =
1824 offset + 0 + 4 * j;
1825 cells[i].vertices[local_vertex_numbering[1 + 4 * j]] =
1826 offset + 3 + 4 * j;
1827 cells[i].vertices[local_vertex_numbering[2 + 4 * j]] =
1828 offset + 2 + 4 * j;
1829 cells[i].vertices[local_vertex_numbering[3 + 4 * j]] =
1830 offset + 1 + 4 * j;
1831 }
1832 offset += 4;
1833 cells[i].material_id = 0;
1834 }
1835
1836 // now correct the last four vertices
1837 cells[n_cells - 1].vertices[local_vertex_numbering[4]] =
1838 (0 + n_rotations) % 4;
1839 cells[n_cells - 1].vertices[local_vertex_numbering[5]] =
1840 (3 + n_rotations) % 4;
1841 cells[n_cells - 1].vertices[local_vertex_numbering[6]] =
1842 (2 + n_rotations) % 4;
1843 cells[n_cells - 1].vertices[local_vertex_numbering[7]] =
1844 (1 + n_rotations) % 4;
1845
1847 tria.create_triangulation(vertices, cells, SubCellData());
1848 }
1849
1850
1851
1852 template <>
1853 void
1854 torus<2, 3>(Triangulation<2, 3> &tria,
1855 const double centerline_radius,
1856 const double inner_radius,
1857 const unsigned int,
1858 const double)
1859 {
1860 Assert(centerline_radius > inner_radius,
1861 ExcMessage("The centerline radius must be greater than the "
1862 "inner radius."));
1863 Assert(inner_radius > 0.0,
1864 ExcMessage("The inner radius must be positive."));
1865
1866 const unsigned int dim = 2;
1867 const unsigned int spacedim = 3;
1868 std::vector<Point<spacedim>> vertices(16);
1869
1870 vertices[0] = Point<spacedim>(centerline_radius - inner_radius, 0, 0);
1871 vertices[1] = Point<spacedim>(centerline_radius, -inner_radius, 0);
1872 vertices[2] = Point<spacedim>(centerline_radius + inner_radius, 0, 0);
1873 vertices[3] = Point<spacedim>(centerline_radius, inner_radius, 0);
1874 vertices[4] = Point<spacedim>(0, 0, centerline_radius - inner_radius);
1875 vertices[5] = Point<spacedim>(0, -inner_radius, centerline_radius);
1876 vertices[6] = Point<spacedim>(0, 0, centerline_radius + inner_radius);
1877 vertices[7] = Point<spacedim>(0, inner_radius, centerline_radius);
1878 vertices[8] = Point<spacedim>(-(centerline_radius - inner_radius), 0, 0);
1879 vertices[9] = Point<spacedim>(-centerline_radius, -inner_radius, 0);
1880 vertices[10] = Point<spacedim>(-(centerline_radius + inner_radius), 0, 0);
1881 vertices[11] = Point<spacedim>(-centerline_radius, inner_radius, 0);
1882 vertices[12] = Point<spacedim>(0, 0, -(centerline_radius - inner_radius));
1883 vertices[13] = Point<spacedim>(0, -inner_radius, -centerline_radius);
1884 vertices[14] = Point<spacedim>(0, 0, -(centerline_radius + inner_radius));
1885 vertices[15] = Point<spacedim>(0, inner_radius, -centerline_radius);
1886
1887 std::vector<CellData<dim>> cells(16);
1888 // Right Hand Orientation
1889 cells[0].vertices[0] = 0;
1890 cells[0].vertices[1] = 4;
1891 cells[0].vertices[2] = 3;
1892 cells[0].vertices[3] = 7;
1893 cells[0].material_id = 0;
1894
1895 cells[1].vertices[0] = 1;
1896 cells[1].vertices[1] = 5;
1897 cells[1].vertices[2] = 0;
1898 cells[1].vertices[3] = 4;
1899 cells[1].material_id = 0;
1900
1901 cells[2].vertices[0] = 2;
1902 cells[2].vertices[1] = 6;
1903 cells[2].vertices[2] = 1;
1904 cells[2].vertices[3] = 5;
1905 cells[2].material_id = 0;
1906
1907 cells[3].vertices[0] = 3;
1908 cells[3].vertices[1] = 7;
1909 cells[3].vertices[2] = 2;
1910 cells[3].vertices[3] = 6;
1911 cells[3].material_id = 0;
1912
1913 cells[4].vertices[0] = 4;
1914 cells[4].vertices[1] = 8;
1915 cells[4].vertices[2] = 7;
1916 cells[4].vertices[3] = 11;
1917 cells[4].material_id = 0;
1918
1919 cells[5].vertices[0] = 5;
1920 cells[5].vertices[1] = 9;
1921 cells[5].vertices[2] = 4;
1922 cells[5].vertices[3] = 8;
1923 cells[5].material_id = 0;
1924
1925 cells[6].vertices[0] = 6;
1926 cells[6].vertices[1] = 10;
1927 cells[6].vertices[2] = 5;
1928 cells[6].vertices[3] = 9;
1929 cells[6].material_id = 0;
1930
1931 cells[7].vertices[0] = 7;
1932 cells[7].vertices[1] = 11;
1933 cells[7].vertices[2] = 6;
1934 cells[7].vertices[3] = 10;
1935 cells[7].material_id = 0;
1936
1937 cells[8].vertices[0] = 8;
1938 cells[8].vertices[1] = 12;
1939 cells[8].vertices[2] = 11;
1940 cells[8].vertices[3] = 15;
1941 cells[8].material_id = 0;
1942
1943 cells[9].vertices[0] = 9;
1944 cells[9].vertices[1] = 13;
1945 cells[9].vertices[2] = 8;
1946 cells[9].vertices[3] = 12;
1947 cells[9].material_id = 0;
1948
1949 cells[10].vertices[0] = 10;
1950 cells[10].vertices[1] = 14;
1951 cells[10].vertices[2] = 9;
1952 cells[10].vertices[3] = 13;
1953 cells[10].material_id = 0;
1954
1955 cells[11].vertices[0] = 11;
1956 cells[11].vertices[1] = 15;
1957 cells[11].vertices[2] = 10;
1958 cells[11].vertices[3] = 14;
1959 cells[11].material_id = 0;
1960
1961 cells[12].vertices[0] = 12;
1962 cells[12].vertices[1] = 0;
1963 cells[12].vertices[2] = 15;
1964 cells[12].vertices[3] = 3;
1965 cells[12].material_id = 0;
1966
1967 cells[13].vertices[0] = 13;
1968 cells[13].vertices[1] = 1;
1969 cells[13].vertices[2] = 12;
1970 cells[13].vertices[3] = 0;
1971 cells[13].material_id = 0;
1972
1973 cells[14].vertices[0] = 14;
1974 cells[14].vertices[1] = 2;
1975 cells[14].vertices[2] = 13;
1976 cells[14].vertices[3] = 1;
1977 cells[14].material_id = 0;
1978
1979 cells[15].vertices[0] = 15;
1980 cells[15].vertices[1] = 3;
1981 cells[15].vertices[2] = 14;
1982 cells[15].vertices[3] = 2;
1983 cells[15].material_id = 0;
1984
1986 tria.create_triangulation(vertices, cells, SubCellData());
1987
1988 tria.set_all_manifold_ids(0);
1989 tria.set_manifold(0, TorusManifold<2>(centerline_radius, inner_radius));
1990 }
1991
1992
1993
1994 namespace
1995 {
1996 static constexpr int circle_cell_vertices[5][4] = {{0, 1, 2, 3},
1997 {0, 2, 6, 4},
1998 {2, 3, 4, 5},
1999 {1, 7, 3, 5},
2000 {6, 4, 7, 5}};
2001 }
2002
2003
2004
2005 template <>
2006 void
2007 torus<3, 3>(Triangulation<3, 3> &tria,
2008 const double centerline_radius,
2009 const double inner_radius,
2010 const unsigned int n_cells_toroidal,
2011 const double phi)
2012 {
2013 Assert(centerline_radius > inner_radius,
2014 ExcMessage("The centerline radius must be greater than the "
2015 "inner radius."));
2016 Assert(inner_radius > 0.0,
2017 ExcMessage("The inner radius must be positive."));
2018 Assert(n_cells_toroidal > static_cast<unsigned int>(phi / numbers::PI),
2019 ExcMessage("Number of cells in toroidal direction has "
2020 "to be at least 3 for a torus of polar extent 2*pi."));
2021 AssertThrow(phi > 0.0 && phi < 2.0 * numbers::PI + 1.0e-15,
2022 ExcMessage("Invalid angle phi specified."));
2023
2024 // the first 8 vertices are in the x-y-plane
2025 const Point<3> p = Point<3>(centerline_radius, 0.0, 0.0);
2026 const double a = 1. / (1 + std::sqrt(2.0));
2027 // A value of 1 indicates "open" torus with angle < 2*pi, which
2028 // means that we need an additional layer of vertices
2029 const unsigned int additional_layer =
2030 (phi < 2.0 * numbers::PI - 1.0e-15) ?
2031 1 :
2032 0; // torus is closed (angle of 2*pi)
2033 const unsigned int n_point_layers_toroidal =
2034 n_cells_toroidal + additional_layer;
2035 std::vector<Point<3>> vertices(8 * n_point_layers_toroidal);
2036 vertices[0] = p + Point<3>(-1, -1, 0) * (inner_radius / std::sqrt(2.0)),
2037 vertices[1] = p + Point<3>(+1, -1, 0) * (inner_radius / std::sqrt(2.0)),
2038 vertices[2] = p + Point<3>(-1, -1, 0) * (inner_radius / std::sqrt(2.0) * a),
2039 vertices[3] = p + Point<3>(+1, -1, 0) * (inner_radius / std::sqrt(2.0) * a),
2040 vertices[4] = p + Point<3>(-1, +1, 0) * (inner_radius / std::sqrt(2.0) * a),
2041 vertices[5] = p + Point<3>(+1, +1, 0) * (inner_radius / std::sqrt(2.0) * a),
2042 vertices[6] = p + Point<3>(-1, +1, 0) * (inner_radius / std::sqrt(2.0)),
2043 vertices[7] = p + Point<3>(+1, +1, 0) * (inner_radius / std::sqrt(2.0));
2044
2045 // create remaining vertices by rotating around negative y-axis (the
2046 // direction is to ensure positive cell measures)
2047 const double phi_cell = phi / n_cells_toroidal;
2048 for (unsigned int c = 1; c < n_point_layers_toroidal; ++c)
2049 {
2050 for (unsigned int v = 0; v < 8; ++v)
2051 {
2052 const double inner_radius_2d = vertices[v][0];
2053 vertices[8 * c + v][0] = inner_radius_2d * std::cos(phi_cell * c);
2054 vertices[8 * c + v][1] = vertices[v][1];
2055 vertices[8 * c + v][2] = inner_radius_2d * std::sin(phi_cell * c);
2056 }
2057 }
2058
2059 // cell connectivity
2060 std::vector<CellData<3>> cells(5 * n_cells_toroidal);
2061 for (unsigned int c = 0; c < n_cells_toroidal; ++c)
2062 {
2063 for (unsigned int j = 0; j < 2; ++j)
2064 {
2065 const unsigned int offset =
2066 (8 * (c + j)) % (8 * n_point_layers_toroidal);
2067
2068 // cells in x-y-plane
2069 for (unsigned int c2 = 0; c2 < 5; ++c2)
2070 for (unsigned int i = 0; i < 4; ++i)
2071 cells[5 * c + c2].vertices[i + j * 4] =
2072 offset + circle_cell_vertices[c2][i];
2073 }
2074
2075 cells[5 * c].material_id = 0;
2076 // mark cell on torus centerline
2077 cells[5 * c + 1].material_id = 0;
2078 cells[5 * c + 2].material_id = 1;
2079 cells[5 * c + 3].material_id = 0;
2080 cells[5 * c + 4].material_id = 0;
2081 }
2082
2083 tria.create_triangulation(vertices, cells, SubCellData());
2084
2085 tria.reset_all_manifolds();
2086 tria.set_all_manifold_ids(0);
2087
2088 for (const auto &cell : tria.cell_iterators())
2089 {
2090 // identify faces on torus surface and set manifold to 1
2091 for (const unsigned int f : GeometryInfo<3>::face_indices())
2092 {
2093 // faces 4 and 5 are those with normal vector aligned with torus
2094 // centerline
2095 if (cell->face(f)->at_boundary() && f != 4 && f != 5)
2096 {
2097 cell->face(f)->set_all_manifold_ids(1);
2098 }
2099 }
2100
2101 // set manifold id to 2 for those cells that are on the torus centerline
2102 if (cell->material_id() == 1)
2103 {
2104 cell->set_all_manifold_ids(2);
2105 // reset to 0
2106 cell->set_material_id(0);
2107 }
2108 }
2109
2110 tria.set_manifold(1, TorusManifold<3>(centerline_radius, inner_radius));
2111 tria.set_manifold(2,
2113 Point<3>()));
2114
2115 tria.set_manifold(0, FlatManifold<3>());
2117 transfinite.initialize(tria);
2118 tria.set_manifold(0, transfinite);
2119 }
2120
2121
2122
2123 template <int dim, int spacedim>
2124 void
2126 const std::vector<Point<spacedim>> &vertices,
2127 const bool colorize)
2128 {
2130 ExcMessage("Wrong number of vertices."));
2131
2132 // First create a hyper_rectangle and then deform it.
2133 hyper_cube(tria, 0, 1, colorize);
2134
2136 tria.begin_active();
2137 for (const unsigned int i : GeometryInfo<dim>::vertex_indices())
2138 cell->vertex(i) = vertices[i];
2139
2140 // Check that the order of the vertices makes sense, i.e., the volume of the
2141 // cell is positive.
2143 ExcMessage(
2144 "The volume of the cell is not greater than zero. "
2145 "This could be due to the wrong ordering of the vertices."));
2146 }
2147
2148
2149
2150 template <>
2151 void
2153 const Point<3> (& /*corners*/)[3],
2154 const bool /*colorize*/)
2155 {
2157 }
2158
2159 template <>
2160 void
2162 const Point<1> (& /*corners*/)[1],
2163 const bool /*colorize*/)
2164 {
2166 }
2167
2168 // Implementation for 2d only
2169 template <>
2170 void
2172 const Point<2> (&corners)[2],
2173 const bool colorize)
2174 {
2175 Point<2> origin;
2176 std::array<Tensor<1, 2>, 2> edges;
2177 edges[0] = corners[0];
2178 edges[1] = corners[1];
2179 std::vector<unsigned int> subdivisions;
2180 subdivided_parallelepiped<2, 2>(
2181 tria, origin, edges, subdivisions, colorize);
2182 }
2183
2184
2185
2186 template <int dim>
2187 void
2189 const Point<dim> (&corners)[dim],
2190 const bool colorize)
2191 {
2192 unsigned int n_subdivisions[dim];
2193 for (unsigned int i = 0; i < dim; ++i)
2194 n_subdivisions[i] = 1;
2195
2196 // and call the function below
2197 subdivided_parallelepiped(tria, n_subdivisions, corners, colorize);
2198 }
2199
2200 template <int dim>
2201 void
2203 const unsigned int n_subdivisions,
2204 const Point<dim> (&corners)[dim],
2205 const bool colorize)
2206 {
2207 // Equalize number of subdivisions in each dim-direction, their
2208 // validity will be checked later
2209 unsigned int n_subdivisions_[dim];
2210 for (unsigned int i = 0; i < dim; ++i)
2211 n_subdivisions_[i] = n_subdivisions;
2212
2213 // and call the function below
2214 subdivided_parallelepiped(tria, n_subdivisions_, corners, colorize);
2215 }
2216
2217 template <int dim>
2218 void
2220# ifndef _MSC_VER
2221 const unsigned int (&n_subdivisions)[dim],
2222# else
2223 const unsigned int *n_subdivisions,
2224# endif
2225 const Point<dim> (&corners)[dim],
2226 const bool colorize)
2227 {
2228 Point<dim> origin;
2229 std::vector<unsigned int> subdivisions;
2230 std::array<Tensor<1, dim>, dim> edges;
2231 for (unsigned int i = 0; i < dim; ++i)
2232 {
2233 subdivisions.push_back(n_subdivisions[i]);
2234 edges[i] = corners[i];
2235 }
2236
2237 subdivided_parallelepiped<dim, dim>(
2238 tria, origin, edges, subdivisions, colorize);
2239 }
2240
2241 // Parallelepiped implementation in 1d, 2d, and 3d. @note The
2242 // implementation in 1d is similar to hyper_rectangle(), and in 2d is
2243 // similar to parallelogram().
2244 template <int dim, int spacedim>
2245 void
2247 const Point<spacedim> &origin,
2248 const std::array<Tensor<1, spacedim>, dim> &edges,
2249 const std::vector<unsigned int> &subdivisions,
2250 const bool colorize)
2251 {
2252 std::vector<unsigned int> compute_subdivisions = subdivisions;
2253 if (compute_subdivisions.empty())
2254 {
2255 compute_subdivisions.resize(dim, 1);
2256 }
2257
2258 Assert(compute_subdivisions.size() == dim,
2259 ExcMessage("One subdivision must be provided for each dimension."));
2260 // check subdivisions
2261 for (unsigned int i = 0; i < dim; ++i)
2262 {
2263 Assert(compute_subdivisions[i] > 0,
2264 ExcInvalidRepetitions(subdivisions[i]));
2265 Assert(
2266 edges[i].norm() > 0,
2267 ExcMessage(
2268 "Edges in subdivided_parallelepiped() must not be degenerate."));
2269 }
2270
2271 /*
2272 * Verify that the edge points to the right in 1d, vectors are oriented in
2273 * a counter clockwise direction in 2d, or form a right handed system in
2274 * 3d.
2275 */
2276 bool twisted_data = false;
2277 switch (dim)
2278 {
2279 case 1:
2280 {
2281 twisted_data = (edges[0][0] < 0);
2282 break;
2283 }
2284 case 2:
2285 {
2286 if (spacedim == 2) // this check does not make sense otherwise
2287 {
2288 const double plane_normal =
2289 edges[0][0] * edges[1][1] - edges[0][1] * edges[1][0];
2290 twisted_data = (plane_normal < 0.0);
2291 }
2292 break;
2293 }
2294 case 3:
2295 {
2296 // Check that the first two vectors are not linear combinations to
2297 // avoid zero division later on.
2298 Assert(std::abs(edges[0] * edges[1] /
2299 (edges[0].norm() * edges[1].norm()) -
2300 1.0) > 1.0e-15,
2301 ExcMessage(
2302 "Edges in subdivided_parallelepiped() must point in"
2303 " different directions."));
2304 const Tensor<1, spacedim> plane_normal =
2305 cross_product_3d(edges[0], edges[1]);
2306
2307 /*
2308 * Ensure that edges 1, 2, and 3 form a right-handed set of
2309 * vectors. This works by applying the definition of the dot product
2310 *
2311 * cos(theta) = dot(x, y)/(norm(x)*norm(y))
2312 *
2313 * and then, since the normal vector and third edge should both
2314 * point away from the plane formed by the first two edges, the
2315 * angle between them must be between 0 and pi/2; hence we just need
2316 *
2317 * 0 < dot(x, y).
2318 */
2319 twisted_data = (plane_normal * edges[2] < 0.0);
2320 break;
2321 }
2322 default:
2324 }
2325 (void)twisted_data; // make the static analyzer happy
2326 Assert(
2327 !twisted_data,
2328 ExcInvalidInputOrientation(
2329 "The triangulation you are trying to create will consist of cells"
2330 " with negative measures. This is usually the result of input data"
2331 " that does not define a right-handed coordinate system. The usual"
2332 " fix for this is to ensure that in 1d the given point is to the"
2333 " right of the origin (or the given edge tensor is positive), in 2d"
2334 " that the two edges (and their cross product) obey the right-hand"
2335 " rule (which may usually be done by switching the order of the"
2336 " points or edge tensors), or in 3d that the edges form a"
2337 " right-handed coordinate system (which may also be accomplished by"
2338 " switching the order of the first two points or edge tensors)."));
2339
2340 // Check corners do not overlap (unique)
2341 for (unsigned int i = 0; i < dim; ++i)
2342 for (unsigned int j = i + 1; j < dim; ++j)
2343 Assert((edges[i] != edges[j]),
2344 ExcMessage(
2345 "Degenerate edges of subdivided_parallelepiped encountered."));
2346
2347 // Create a list of points
2348 std::vector<Point<spacedim>> points;
2349
2350 switch (dim)
2351 {
2352 case 1:
2353 for (unsigned int x = 0; x <= compute_subdivisions[0]; ++x)
2354 points.push_back(origin + edges[0] / compute_subdivisions[0] * x);
2355 break;
2356
2357 case 2:
2358 for (unsigned int y = 0; y <= compute_subdivisions[1]; ++y)
2359 for (unsigned int x = 0; x <= compute_subdivisions[0]; ++x)
2360 points.push_back(origin + edges[0] / compute_subdivisions[0] * x +
2361 edges[1] / compute_subdivisions[1] * y);
2362 break;
2363
2364 case 3:
2365 for (unsigned int z = 0; z <= compute_subdivisions[2]; ++z)
2366 for (unsigned int y = 0; y <= compute_subdivisions[1]; ++y)
2367 for (unsigned int x = 0; x <= compute_subdivisions[0]; ++x)
2368 points.push_back(origin +
2369 edges[0] / compute_subdivisions[0] * x +
2370 edges[1] / compute_subdivisions[1] * y +
2371 edges[2] / compute_subdivisions[2] * z);
2372 break;
2373
2374 default:
2376 }
2377
2378 // Prepare cell data
2379 unsigned int n_cells = 1;
2380 for (unsigned int i = 0; i < dim; ++i)
2381 n_cells *= compute_subdivisions[i];
2382 std::vector<CellData<dim>> cells(n_cells);
2383
2384 // Create fixed ordering of
2385 switch (dim)
2386 {
2387 case 1:
2388 for (unsigned int x = 0; x < compute_subdivisions[0]; ++x)
2389 {
2390 cells[x].vertices[0] = x;
2391 cells[x].vertices[1] = x + 1;
2392
2393 // wipe material id
2394 cells[x].material_id = 0;
2395 }
2396 break;
2397
2398 case 2:
2399 {
2400 // Shorthand
2401 const unsigned int n_dy = compute_subdivisions[1];
2402 const unsigned int n_dx = compute_subdivisions[0];
2403
2404 for (unsigned int y = 0; y < n_dy; ++y)
2405 for (unsigned int x = 0; x < n_dx; ++x)
2406 {
2407 const unsigned int c = y * n_dx + x;
2408 cells[c].vertices[0] = y * (n_dx + 1) + x;
2409 cells[c].vertices[1] = y * (n_dx + 1) + x + 1;
2410 cells[c].vertices[2] = (y + 1) * (n_dx + 1) + x;
2411 cells[c].vertices[3] = (y + 1) * (n_dx + 1) + x + 1;
2412
2413 // wipe material id
2414 cells[c].material_id = 0;
2415 }
2416 }
2417 break;
2418
2419 case 3:
2420 {
2421 // Shorthand
2422 const unsigned int n_dz = compute_subdivisions[2];
2423 const unsigned int n_dy = compute_subdivisions[1];
2424 const unsigned int n_dx = compute_subdivisions[0];
2425
2426 for (unsigned int z = 0; z < n_dz; ++z)
2427 for (unsigned int y = 0; y < n_dy; ++y)
2428 for (unsigned int x = 0; x < n_dx; ++x)
2429 {
2430 const unsigned int c = z * n_dy * n_dx + y * n_dx + x;
2431
2432 cells[c].vertices[0] =
2433 z * (n_dy + 1) * (n_dx + 1) + y * (n_dx + 1) + x;
2434 cells[c].vertices[1] =
2435 z * (n_dy + 1) * (n_dx + 1) + y * (n_dx + 1) + x + 1;
2436 cells[c].vertices[2] =
2437 z * (n_dy + 1) * (n_dx + 1) + (y + 1) * (n_dx + 1) + x;
2438 cells[c].vertices[3] = z * (n_dy + 1) * (n_dx + 1) +
2439 (y + 1) * (n_dx + 1) + x + 1;
2440 cells[c].vertices[4] =
2441 (z + 1) * (n_dy + 1) * (n_dx + 1) + y * (n_dx + 1) + x;
2442 cells[c].vertices[5] = (z + 1) * (n_dy + 1) * (n_dx + 1) +
2443 y * (n_dx + 1) + x + 1;
2444 cells[c].vertices[6] = (z + 1) * (n_dy + 1) * (n_dx + 1) +
2445 (y + 1) * (n_dx + 1) + x;
2446 cells[c].vertices[7] = (z + 1) * (n_dy + 1) * (n_dx + 1) +
2447 (y + 1) * (n_dx + 1) + x + 1;
2448
2449 // wipe material id
2450 cells[c].material_id = 0;
2451 }
2452 break;
2453 }
2454
2455 default:
2457 }
2458
2459 // Create triangulation
2460 // reorder the cells to ensure that they satisfy the convention for
2461 // edge and face directions
2463 tria.create_triangulation(points, cells, SubCellData());
2464
2465 // Finally assign boundary indicators according to hyper_rectangle
2466 if (colorize)
2467 {
2469 tria.begin_active(),
2470 endc = tria.end();
2471 for (; cell != endc; ++cell)
2472 {
2473 for (const unsigned int face : GeometryInfo<dim>::face_indices())
2474 {
2475 if (cell->face(face)->at_boundary())
2476 cell->face(face)->set_boundary_id(face);
2477 }
2478 }
2479 }
2480 }
2481
2482
2483 template <int dim, int spacedim>
2484 void
2486 const unsigned int repetitions,
2487 const double left,
2488 const double right,
2489 const bool colorize)
2490 {
2491 Assert(repetitions >= 1, ExcInvalidRepetitions(repetitions));
2492 Assert(left < right,
2493 ExcMessage("Invalid left-to-right bounds of hypercube"));
2494
2495 Point<dim> p0, p1;
2496 for (unsigned int i = 0; i < dim; ++i)
2497 {
2498 p0[i] = left;
2499 p1[i] = right;
2500 }
2501
2502 std::vector<unsigned int> reps(dim, repetitions);
2503 subdivided_hyper_rectangle(tria, reps, p0, p1, colorize);
2504 }
2505
2506
2507
2508 template <int dim, int spacedim>
2509 void
2511 const std::vector<unsigned int> &repetitions,
2512 const Point<dim> &p_1,
2513 const Point<dim> &p_2,
2514 const bool colorize)
2515 {
2516 Assert(repetitions.size() == dim, ExcInvalidRepetitionsDimension(dim));
2517
2518 // First, extend dimensions from dim to spacedim and
2519 // normalize such that p1 is lower in all coordinate
2520 // directions. Additional entries will be 0.
2521 Point<spacedim> p1, p2;
2522 for (unsigned int i = 0; i < dim; ++i)
2523 {
2524 p1[i] = std::min(p_1[i], p_2[i]);
2525 p2[i] = std::max(p_1[i], p_2[i]);
2526 }
2527
2528 // calculate deltas and validate input
2529 std::array<Point<spacedim>, dim> delta;
2530 for (unsigned int i = 0; i < dim; ++i)
2531 {
2532 Assert(repetitions[i] >= 1, ExcInvalidRepetitions(repetitions[i]));
2533
2534 delta[i][i] = (p2[i] - p1[i]) / repetitions[i];
2535 Assert(
2536 delta[i][i] > 0.0,
2537 ExcMessage(
2538 "The first dim entries of coordinates of p1 and p2 need to be different."));
2539 }
2540
2541 // then generate the points
2542 std::vector<Point<spacedim>> points;
2543 switch (dim)
2544 {
2545 case 1:
2546 for (unsigned int x = 0; x <= repetitions[0]; ++x)
2547 points.push_back(p1 + x * delta[0]);
2548 break;
2549
2550 case 2:
2551 for (unsigned int y = 0; y <= repetitions[1]; ++y)
2552 for (unsigned int x = 0; x <= repetitions[0]; ++x)
2553 points.push_back(p1 + x * delta[0] + y * delta[1]);
2554 break;
2555
2556 case 3:
2557 for (unsigned int z = 0; z <= repetitions[2]; ++z)
2558 for (unsigned int y = 0; y <= repetitions[1]; ++y)
2559 for (unsigned int x = 0; x <= repetitions[0]; ++x)
2560 points.push_back(p1 + x * delta[0] + y * delta[1] +
2561 z * delta[2]);
2562 break;
2563
2564 default:
2566 }
2567
2568 // next create the cells
2569 std::vector<CellData<dim>> cells;
2570 switch (dim)
2571 {
2572 case 1:
2573 {
2574 cells.resize(repetitions[0]);
2575 for (unsigned int x = 0; x < repetitions[0]; ++x)
2576 {
2577 cells[x].vertices[0] = x;
2578 cells[x].vertices[1] = x + 1;
2579 cells[x].material_id = 0;
2580 }
2581 break;
2582 }
2583
2584 case 2:
2585 {
2586 cells.resize(repetitions[1] * repetitions[0]);
2587 for (unsigned int y = 0; y < repetitions[1]; ++y)
2588 for (unsigned int x = 0; x < repetitions[0]; ++x)
2589 {
2590 const unsigned int c = x + y * repetitions[0];
2591 cells[c].vertices[0] = y * (repetitions[0] + 1) + x;
2592 cells[c].vertices[1] = y * (repetitions[0] + 1) + x + 1;
2593 cells[c].vertices[2] = (y + 1) * (repetitions[0] + 1) + x;
2594 cells[c].vertices[3] = (y + 1) * (repetitions[0] + 1) + x + 1;
2595 cells[c].material_id = 0;
2596 }
2597 break;
2598 }
2599
2600 case 3:
2601 {
2602 const unsigned int n_x = (repetitions[0] + 1);
2603 const unsigned int n_xy =
2604 (repetitions[0] + 1) * (repetitions[1] + 1);
2605
2606 cells.resize(repetitions[2] * repetitions[1] * repetitions[0]);
2607 for (unsigned int z = 0; z < repetitions[2]; ++z)
2608 for (unsigned int y = 0; y < repetitions[1]; ++y)
2609 for (unsigned int x = 0; x < repetitions[0]; ++x)
2610 {
2611 const unsigned int c = x + y * repetitions[0] +
2612 z * repetitions[0] * repetitions[1];
2613 cells[c].vertices[0] = z * n_xy + y * n_x + x;
2614 cells[c].vertices[1] = z * n_xy + y * n_x + x + 1;
2615 cells[c].vertices[2] = z * n_xy + (y + 1) * n_x + x;
2616 cells[c].vertices[3] = z * n_xy + (y + 1) * n_x + x + 1;
2617 cells[c].vertices[4] = (z + 1) * n_xy + y * n_x + x;
2618 cells[c].vertices[5] = (z + 1) * n_xy + y * n_x + x + 1;
2619 cells[c].vertices[6] = (z + 1) * n_xy + (y + 1) * n_x + x;
2620 cells[c].vertices[7] =
2621 (z + 1) * n_xy + (y + 1) * n_x + x + 1;
2622 cells[c].material_id = 0;
2623 }
2624 break;
2625 }
2626
2627 default:
2629 }
2630
2631 tria.create_triangulation(points, cells, SubCellData());
2632
2633 if (colorize)
2634 {
2635 // to colorize, run through all
2636 // faces of all cells and set
2637 // boundary indicator to the
2638 // correct value if it was 0.
2639
2640 // use a large epsilon to
2641 // compare numbers to avoid
2642 // roundoff problems.
2643 double epsilon = std::numeric_limits<double>::max();
2644 for (unsigned int i = 0; i < dim; ++i)
2645 epsilon = std::min(epsilon, 0.01 * delta[i][i]);
2646 Assert(epsilon > 0,
2647 ExcMessage(
2648 "The distance between corner points must be positive."));
2649
2650 // actual code is external since
2651 // 1-D is different from 2/3d.
2652 colorize_subdivided_hyper_rectangle(tria, p1, p2, epsilon);
2653 }
2654 }
2655
2656
2657
2658 template <int dim>
2659 void
2661 const std::vector<std::vector<double>> &step_sz,
2662 const Point<dim> &p_1,
2663 const Point<dim> &p_2,
2664 const bool colorize)
2665 {
2666 Assert(step_sz.size() == dim, ExcInvalidRepetitionsDimension(dim));
2667
2668 // First, normalize input such that
2669 // p1 is lower in all coordinate
2670 // directions and check the consistency of
2671 // step sizes, i.e. that they all
2672 // add up to the sizes specified by
2673 // p_1 and p_2
2674 Point<dim> p1(p_1);
2675 Point<dim> p2(p_2);
2676 std::vector<std::vector<double>> step_sizes(step_sz);
2677
2678 for (unsigned int i = 0; i < dim; ++i)
2679 {
2680 if (p1[i] > p2[i])
2681 {
2682 std::swap(p1[i], p2[i]);
2683 std::reverse(step_sizes[i].begin(), step_sizes[i].end());
2684 }
2685
2686 if constexpr (running_in_debug_mode())
2687 {
2688 double x = 0;
2689 for (unsigned int j = 0; j < step_sizes.at(i).size(); ++j)
2690 x += step_sizes[i][j];
2691 Assert(std::fabs(x - (p2[i] - p1[i])) <= 1e-12 * std::fabs(x),
2692 ExcMessage(
2693 "The sequence of step sizes in coordinate direction " +
2695 " must be equal to the distance of the two given "
2696 "points in this coordinate direction."));
2697 }
2698 }
2699
2700
2701 // then generate the necessary
2702 // points
2703 std::vector<Point<dim>> points;
2704 switch (dim)
2705 {
2706 case 1:
2707 {
2708 double x = 0;
2709 for (unsigned int i = 0;; ++i)
2710 {
2711 points.push_back(Point<dim>(p1[0] + x));
2712
2713 // form partial sums. in
2714 // the last run through
2715 // avoid accessing
2716 // non-existent values
2717 // and exit early instead
2718 if (i == step_sizes[0].size())
2719 break;
2720
2721 x += step_sizes[0][i];
2722 }
2723 break;
2724 }
2725
2726 case 2:
2727 {
2728 double y = 0;
2729 for (unsigned int j = 0;; ++j)
2730 {
2731 double x = 0;
2732 for (unsigned int i = 0;; ++i)
2733 {
2734 points.push_back(Point<dim>(p1[0] + x, p1[1] + y));
2735 if (i == step_sizes[0].size())
2736 break;
2737
2738 x += step_sizes[0][i];
2739 }
2740
2741 if (j == step_sizes[1].size())
2742 break;
2743
2744 y += step_sizes[1][j];
2745 }
2746 break;
2747 }
2748 case 3:
2749 {
2750 double z = 0;
2751 for (unsigned int k = 0;; ++k)
2752 {
2753 double y = 0;
2754 for (unsigned int j = 0;; ++j)
2755 {
2756 double x = 0;
2757 for (unsigned int i = 0;; ++i)
2758 {
2759 points.push_back(
2760 Point<dim>(p1[0] + x, p1[1] + y, p1[2] + z));
2761 if (i == step_sizes[0].size())
2762 break;
2763
2764 x += step_sizes[0][i];
2765 }
2766
2767 if (j == step_sizes[1].size())
2768 break;
2769
2770 y += step_sizes[1][j];
2771 }
2772
2773 if (k == step_sizes[2].size())
2774 break;
2775
2776 z += step_sizes[2][k];
2777 }
2778 break;
2779 }
2780
2781 default:
2783 }
2784
2785 // next create the cells
2786 // Prepare cell data
2787 std::vector<CellData<dim>> cells;
2788 switch (dim)
2789 {
2790 case 1:
2791 {
2792 cells.resize(step_sizes[0].size());
2793 for (unsigned int x = 0; x < step_sizes[0].size(); ++x)
2794 {
2795 cells[x].vertices[0] = x;
2796 cells[x].vertices[1] = x + 1;
2797 cells[x].material_id = 0;
2798 }
2799 break;
2800 }
2801
2802 case 2:
2803 {
2804 cells.resize(step_sizes[1].size() * step_sizes[0].size());
2805 for (unsigned int y = 0; y < step_sizes[1].size(); ++y)
2806 for (unsigned int x = 0; x < step_sizes[0].size(); ++x)
2807 {
2808 const unsigned int c = x + y * step_sizes[0].size();
2809 cells[c].vertices[0] = y * (step_sizes[0].size() + 1) + x;
2810 cells[c].vertices[1] = y * (step_sizes[0].size() + 1) + x + 1;
2811 cells[c].vertices[2] =
2812 (y + 1) * (step_sizes[0].size() + 1) + x;
2813 cells[c].vertices[3] =
2814 (y + 1) * (step_sizes[0].size() + 1) + x + 1;
2815 cells[c].material_id = 0;
2816 }
2817 break;
2818 }
2819
2820 case 3:
2821 {
2822 const unsigned int n_x = (step_sizes[0].size() + 1);
2823 const unsigned int n_xy =
2824 (step_sizes[0].size() + 1) * (step_sizes[1].size() + 1);
2825
2826 cells.resize(step_sizes[2].size() * step_sizes[1].size() *
2827 step_sizes[0].size());
2828 for (unsigned int z = 0; z < step_sizes[2].size(); ++z)
2829 for (unsigned int y = 0; y < step_sizes[1].size(); ++y)
2830 for (unsigned int x = 0; x < step_sizes[0].size(); ++x)
2831 {
2832 const unsigned int c =
2833 x + y * step_sizes[0].size() +
2834 z * step_sizes[0].size() * step_sizes[1].size();
2835 cells[c].vertices[0] = z * n_xy + y * n_x + x;
2836 cells[c].vertices[1] = z * n_xy + y * n_x + x + 1;
2837 cells[c].vertices[2] = z * n_xy + (y + 1) * n_x + x;
2838 cells[c].vertices[3] = z * n_xy + (y + 1) * n_x + x + 1;
2839 cells[c].vertices[4] = (z + 1) * n_xy + y * n_x + x;
2840 cells[c].vertices[5] = (z + 1) * n_xy + y * n_x + x + 1;
2841 cells[c].vertices[6] = (z + 1) * n_xy + (y + 1) * n_x + x;
2842 cells[c].vertices[7] =
2843 (z + 1) * n_xy + (y + 1) * n_x + x + 1;
2844 cells[c].material_id = 0;
2845 }
2846 break;
2847 }
2848
2849 default:
2851 }
2852
2853 tria.create_triangulation(points, cells, SubCellData());
2854
2855 if (colorize)
2856 {
2857 // to colorize, run through all
2858 // faces of all cells and set
2859 // boundary indicator to the
2860 // correct value if it was 0.
2861
2862 // use a large epsilon to
2863 // compare numbers to avoid
2864 // roundoff problems.
2865 double min_size =
2866 *std::min_element(step_sizes[0].begin(), step_sizes[0].end());
2867 for (unsigned int i = 1; i < dim; ++i)
2868 min_size = std::min(min_size,
2869 *std::min_element(step_sizes[i].begin(),
2870 step_sizes[i].end()));
2871 const double epsilon = 0.01 * min_size;
2872
2873 // actual code is external since
2874 // 1-D is different from 2/3d.
2875 colorize_subdivided_hyper_rectangle(tria, p1, p2, epsilon);
2876 }
2877 }
2878
2879
2880
2881 template <>
2882 void
2884 const std::vector<std::vector<double>> &spacing,
2885 const Point<1> &p,
2886 const Table<1, types::material_id> &material_id,
2887 const bool colorize)
2888 {
2889 Assert(spacing.size() == 1, ExcInvalidRepetitionsDimension(1));
2890
2891 const unsigned int n_cells = material_id.size(0);
2892
2893 Assert(spacing[0].size() == n_cells, ExcInvalidRepetitionsDimension(1));
2894
2895 double delta = std::numeric_limits<double>::max();
2896 for (unsigned int i = 0; i < n_cells; ++i)
2897 {
2898 Assert(spacing[0][i] >= 0, ExcInvalidRepetitions(-1));
2899 delta = std::min(delta, spacing[0][i]);
2900 }
2901
2902 // generate the necessary points
2903 std::vector<Point<1>> points;
2904 double ax = p[0];
2905 for (unsigned int x = 0; x <= n_cells; ++x)
2906 {
2907 points.emplace_back(ax);
2908 if (x < n_cells)
2909 ax += spacing[0][x];
2910 }
2911 // create the cells
2912 unsigned int n_val_cells = 0;
2913 for (unsigned int i = 0; i < n_cells; ++i)
2914 if (material_id[i] != numbers::invalid_material_id)
2915 ++n_val_cells;
2916
2917 std::vector<CellData<1>> cells(n_val_cells);
2918 unsigned int id = 0;
2919 for (unsigned int x = 0; x < n_cells; ++x)
2920 if (material_id[x] != numbers::invalid_material_id)
2921 {
2922 cells[id].vertices[0] = x;
2923 cells[id].vertices[1] = x + 1;
2924 cells[id].material_id = material_id[x];
2925 ++id;
2926 }
2927 // create triangulation
2928 SubCellData t;
2929 GridTools::delete_unused_vertices(points, cells, t);
2930
2931 tria.create_triangulation(points, cells, t);
2932
2933 // set boundary indicator
2934 if (colorize)
2936 }
2937
2938
2939 template <>
2940 void
2942 const std::vector<std::vector<double>> &spacing,
2943 const Point<2> &p,
2944 const Table<2, types::material_id> &material_id,
2945 const bool colorize)
2946 {
2947 Assert(spacing.size() == 2, ExcInvalidRepetitionsDimension(2));
2948
2949 std::vector<unsigned int> repetitions(2);
2950 double delta = std::numeric_limits<double>::max();
2951 for (unsigned int i = 0; i < 2; ++i)
2952 {
2953 repetitions[i] = spacing[i].size();
2954 for (unsigned int j = 0; j < repetitions[i]; ++j)
2955 {
2956 Assert(spacing[i][j] >= 0, ExcInvalidRepetitions(-1));
2957 delta = std::min(delta, spacing[i][j]);
2958 }
2959 Assert(material_id.size(i) == repetitions[i],
2960 ExcInvalidRepetitionsDimension(i));
2961 }
2962
2963 // generate the necessary points
2964 std::vector<Point<2>> points;
2965 double ay = p[1];
2966 for (unsigned int y = 0; y <= repetitions[1]; ++y)
2967 {
2968 double ax = p[0];
2969 for (unsigned int x = 0; x <= repetitions[0]; ++x)
2970 {
2971 points.emplace_back(ax, ay);
2972 if (x < repetitions[0])
2973 ax += spacing[0][x];
2974 }
2975 if (y < repetitions[1])
2976 ay += spacing[1][y];
2977 }
2978
2979 // create the cells
2980 unsigned int n_val_cells = 0;
2981 for (unsigned int i = 0; i < material_id.size(0); ++i)
2982 for (unsigned int j = 0; j < material_id.size(1); ++j)
2983 if (material_id[i][j] != numbers::invalid_material_id)
2984 ++n_val_cells;
2985
2986 std::vector<CellData<2>> cells(n_val_cells);
2987 unsigned int id = 0;
2988 for (unsigned int y = 0; y < repetitions[1]; ++y)
2989 for (unsigned int x = 0; x < repetitions[0]; ++x)
2990 if (material_id[x][y] != numbers::invalid_material_id)
2991 {
2992 cells[id].vertices[0] = y * (repetitions[0] + 1) + x;
2993 cells[id].vertices[1] = y * (repetitions[0] + 1) + x + 1;
2994 cells[id].vertices[2] = (y + 1) * (repetitions[0] + 1) + x;
2995 cells[id].vertices[3] = (y + 1) * (repetitions[0] + 1) + x + 1;
2996 cells[id].material_id = material_id[x][y];
2997 ++id;
2998 }
2999
3000 // create triangulation
3001 SubCellData t;
3002 GridTools::delete_unused_vertices(points, cells, t);
3003
3004 tria.create_triangulation(points, cells, t);
3005
3006 // set boundary indicator
3007 if (colorize)
3008 {
3009 double eps = 0.01 * delta;
3010 Triangulation<2>::cell_iterator cell = tria.begin(), endc = tria.end();
3011 for (; cell != endc; ++cell)
3012 {
3013 Point<2> cell_center = cell->center();
3014 for (const unsigned int f : GeometryInfo<2>::face_indices())
3015 if (cell->face(f)->boundary_id() == 0)
3016 {
3017 Point<2> face_center = cell->face(f)->center();
3018 for (unsigned int i = 0; i < 2; ++i)
3019 {
3020 if (face_center[i] < cell_center[i] - eps)
3021 cell->face(f)->set_boundary_id(i * 2);
3022 if (face_center[i] > cell_center[i] + eps)
3023 cell->face(f)->set_boundary_id(i * 2 + 1);
3024 }
3025 }
3026 }
3027 }
3028 }
3029
3030
3031 template <>
3032 void
3034 const std::vector<std::vector<double>> &spacing,
3035 const Point<3> &p,
3036 const Table<3, types::material_id> &material_id,
3037 const bool colorize)
3038 {
3039 const unsigned int dim = 3;
3040
3041 Assert(spacing.size() == dim, ExcInvalidRepetitionsDimension(dim));
3042
3043 std::array<unsigned int, dim> repetitions;
3044 double delta = std::numeric_limits<double>::max();
3045 for (unsigned int i = 0; i < dim; ++i)
3046 {
3047 repetitions[i] = spacing[i].size();
3048 for (unsigned int j = 0; j < repetitions[i]; ++j)
3049 {
3050 Assert(spacing[i][j] >= 0, ExcInvalidRepetitions(-1));
3051 delta = std::min(delta, spacing[i][j]);
3052 }
3053 Assert(material_id.size(i) == repetitions[i],
3054 ExcInvalidRepetitionsDimension(i));
3055 }
3056
3057 // generate the necessary points
3058 std::vector<Point<dim>> points;
3059 double az = p[2];
3060 for (unsigned int z = 0; z <= repetitions[2]; ++z)
3061 {
3062 double ay = p[1];
3063 for (unsigned int y = 0; y <= repetitions[1]; ++y)
3064 {
3065 double ax = p[0];
3066 for (unsigned int x = 0; x <= repetitions[0]; ++x)
3067 {
3068 points.emplace_back(ax, ay, az);
3069 if (x < repetitions[0])
3070 ax += spacing[0][x];
3071 }
3072 if (y < repetitions[1])
3073 ay += spacing[1][y];
3074 }
3075 if (z < repetitions[2])
3076 az += spacing[2][z];
3077 }
3078
3079 // create the cells
3080 unsigned int n_val_cells = 0;
3081 for (unsigned int i = 0; i < material_id.size(0); ++i)
3082 for (unsigned int j = 0; j < material_id.size(1); ++j)
3083 for (unsigned int k = 0; k < material_id.size(2); ++k)
3084 if (material_id[i][j][k] != numbers::invalid_material_id)
3085 ++n_val_cells;
3086
3087 std::vector<CellData<dim>> cells(n_val_cells);
3088 unsigned int id = 0;
3089 const unsigned int n_x = (repetitions[0] + 1);
3090 const unsigned int n_xy = (repetitions[0] + 1) * (repetitions[1] + 1);
3091 for (unsigned int z = 0; z < repetitions[2]; ++z)
3092 for (unsigned int y = 0; y < repetitions[1]; ++y)
3093 for (unsigned int x = 0; x < repetitions[0]; ++x)
3094 if (material_id[x][y][z] != numbers::invalid_material_id)
3095 {
3096 cells[id].vertices[0] = z * n_xy + y * n_x + x;
3097 cells[id].vertices[1] = z * n_xy + y * n_x + x + 1;
3098 cells[id].vertices[2] = z * n_xy + (y + 1) * n_x + x;
3099 cells[id].vertices[3] = z * n_xy + (y + 1) * n_x + x + 1;
3100 cells[id].vertices[4] = (z + 1) * n_xy + y * n_x + x;
3101 cells[id].vertices[5] = (z + 1) * n_xy + y * n_x + x + 1;
3102 cells[id].vertices[6] = (z + 1) * n_xy + (y + 1) * n_x + x;
3103 cells[id].vertices[7] = (z + 1) * n_xy + (y + 1) * n_x + x + 1;
3104 cells[id].material_id = material_id[x][y][z];
3105 ++id;
3106 }
3107
3108 // create triangulation
3109 SubCellData t;
3110 GridTools::delete_unused_vertices(points, cells, t);
3111
3112 tria.create_triangulation(points, cells, t);
3113
3114 // set boundary indicator
3115 if (colorize)
3116 {
3117 double eps = 0.01 * delta;
3119 endc = tria.end();
3120 for (; cell != endc; ++cell)
3121 {
3122 Point<dim> cell_center = cell->center();
3123 for (auto f : GeometryInfo<dim>::face_indices())
3124 if (cell->face(f)->boundary_id() == 0)
3125 {
3126 Point<dim> face_center = cell->face(f)->center();
3127 for (unsigned int i = 0; i < dim; ++i)
3128 {
3129 if (face_center[i] < cell_center[i] - eps)
3130 cell->face(f)->set_boundary_id(i * 2);
3131 if (face_center[i] > cell_center[i] + eps)
3132 cell->face(f)->set_boundary_id(i * 2 + 1);
3133 }
3134 }
3135 }
3136 }
3137 }
3138
3139 template <int dim, int spacedim>
3140 void
3142 const std::vector<unsigned int> &holes)
3143 {
3144 AssertDimension(holes.size(), dim);
3145 // The corner points of the first cell. If there is a desire at
3146 // some point to change the geometry of the cells, they can be
3147 // made an argument to the function.
3148
3149 Point<spacedim> p1;
3150 Point<spacedim> p2;
3151 for (unsigned int d = 0; d < dim; ++d)
3152 p2[d] = 1.;
3153
3154 // then check that all repetitions
3155 // are >= 1, and calculate deltas
3156 // convert repetitions from double
3157 // to int by taking the ceiling.
3158 std::array<Point<spacedim>, dim> delta;
3159 std::array<unsigned int, dim> repetitions;
3160 for (unsigned int i = 0; i < dim; ++i)
3161 {
3162 Assert(holes[i] >= 1,
3163 ExcMessage("At least one hole needed in each direction"));
3164 repetitions[i] = 2 * holes[i] + 1;
3165 delta[i][i] = (p2[i] - p1[i]);
3166 }
3167
3168 // then generate the necessary
3169 // points
3170 std::vector<Point<spacedim>> points;
3171 switch (dim)
3172 {
3173 case 1:
3174 for (unsigned int x = 0; x <= repetitions[0]; ++x)
3175 points.push_back(p1 + x * delta[0]);
3176 break;
3177
3178 case 2:
3179 for (unsigned int y = 0; y <= repetitions[1]; ++y)
3180 for (unsigned int x = 0; x <= repetitions[0]; ++x)
3181 points.push_back(p1 + x * delta[0] + y * delta[1]);
3182 break;
3183
3184 case 3:
3185 for (unsigned int z = 0; z <= repetitions[2]; ++z)
3186 for (unsigned int y = 0; y <= repetitions[1]; ++y)
3187 for (unsigned int x = 0; x <= repetitions[0]; ++x)
3188 points.push_back(p1 + x * delta[0] + y * delta[1] +
3189 z * delta[2]);
3190 break;
3191
3192 default:
3194 }
3195
3196 // next create the cells
3197 // Prepare cell data
3198 std::vector<CellData<dim>> cells;
3199 switch (dim)
3200 {
3201 case 2:
3202 {
3203 cells.resize(repetitions[1] * repetitions[0] - holes[1] * holes[0]);
3204 unsigned int c = 0;
3205 for (unsigned int y = 0; y < repetitions[1]; ++y)
3206 for (unsigned int x = 0; x < repetitions[0]; ++x)
3207 {
3208 if ((x % 2 == 1) && (y % 2 == 1))
3209 continue;
3210 Assert(c < cells.size(), ExcInternalError());
3211 cells[c].vertices[0] = y * (repetitions[0] + 1) + x;
3212 cells[c].vertices[1] = y * (repetitions[0] + 1) + x + 1;
3213 cells[c].vertices[2] = (y + 1) * (repetitions[0] + 1) + x;
3214 cells[c].vertices[3] = (y + 1) * (repetitions[0] + 1) + x + 1;
3215 cells[c].material_id = 0;
3216 ++c;
3217 }
3218 break;
3219 }
3220
3221 case 3:
3222 {
3223 const unsigned int n_x = (repetitions[0] + 1);
3224 const unsigned int n_xy =
3225 (repetitions[0] + 1) * (repetitions[1] + 1);
3226
3227 cells.resize(repetitions[2] * repetitions[1] * repetitions[0]);
3228
3229 unsigned int c = 0;
3230 for (unsigned int z = 0; z < repetitions[2]; ++z)
3231 for (unsigned int y = 0; y < repetitions[1]; ++y)
3232 for (unsigned int x = 0; x < repetitions[0]; ++x)
3233 {
3234 Assert(c < cells.size(), ExcInternalError());
3235 cells[c].vertices[0] = z * n_xy + y * n_x + x;
3236 cells[c].vertices[1] = z * n_xy + y * n_x + x + 1;
3237 cells[c].vertices[2] = z * n_xy + (y + 1) * n_x + x;
3238 cells[c].vertices[3] = z * n_xy + (y + 1) * n_x + x + 1;
3239 cells[c].vertices[4] = (z + 1) * n_xy + y * n_x + x;
3240 cells[c].vertices[5] = (z + 1) * n_xy + y * n_x + x + 1;
3241 cells[c].vertices[6] = (z + 1) * n_xy + (y + 1) * n_x + x;
3242 cells[c].vertices[7] =
3243 (z + 1) * n_xy + (y + 1) * n_x + x + 1;
3244 cells[c].material_id = 0;
3245 ++c;
3246 }
3247 break;
3248 }
3249
3250 default:
3252 }
3253
3254 tria.create_triangulation(points, cells, SubCellData());
3255 }
3256
3257
3258
3259 template <>
3260 void
3262 const double /*inner_radius*/,
3263 const double /*outer_radius*/,
3264 const double /*pad_bottom*/,
3265 const double /*pad_top*/,
3266 const double /*pad_left*/,
3267 const double /*pad_right*/,
3268 const Point<1> & /*center*/,
3269 const types::manifold_id /*polar_manifold_id*/,
3270 const types::manifold_id /*tfi_manifold_id*/,
3271 const double /*L*/,
3272 const unsigned int /*n_slices*/,
3273 const bool /*colorize*/)
3274 {
3276 }
3277
3278
3279
3280 template <>
3281 void
3283 const double /*shell_region_width*/,
3284 const unsigned int /*n_shells*/,
3285 const double /*skewness*/,
3286 const bool /*colorize*/)
3287 {
3289 }
3290
3291
3292
3293 namespace internal
3294 {
3295 // helper function to check if point is in 2d box
3296 bool inline point_in_2d_box(const Point<2> &p,
3297 const Point<2> &c,
3298 const double radius)
3299 {
3300 return (std::abs(p[0] - c[0]) < radius) &&
3301 (std::abs(p[1] - c[1]) < radius);
3302 }
3303
3304
3305
3306 // Find the minimal distance between two vertices. This is useful for
3307 // computing a tolerance for merging vertices in
3308 // GridTools::merge_triangulations.
3309 template <int dim, int spacedim>
3310 double
3311 minimal_vertex_distance(const Triangulation<dim, spacedim> &triangulation)
3312 {
3313 double length = std::numeric_limits<double>::max();
3314 for (const auto &cell : triangulation.active_cell_iterators())
3315 for (unsigned int n = 0; n < GeometryInfo<dim>::lines_per_cell; ++n)
3316 length = std::min(length, cell->line(n)->diameter());
3317 return length;
3318 }
3319 } // namespace internal
3320
3321
3322
3323 template <>
3324 void
3326 const double inner_radius,
3327 const double outer_radius,
3328 const double pad_bottom,
3329 const double pad_top,
3330 const double pad_left,
3331 const double pad_right,
3332 const Point<2> &new_center,
3333 const types::manifold_id polar_manifold_id,
3334 const types::manifold_id tfi_manifold_id,
3335 const double L,
3336 const unsigned int /*n_slices*/,
3337 const bool colorize)
3338 {
3339 const bool with_padding =
3340 pad_bottom > 0 || pad_top > 0 || pad_left > 0 || pad_right > 0;
3341
3342 Assert(pad_bottom >= 0., ExcMessage("Negative bottom padding."));
3343 Assert(pad_top >= 0., ExcMessage("Negative top padding."));
3344 Assert(pad_left >= 0., ExcMessage("Negative left padding."));
3345 Assert(pad_right >= 0., ExcMessage("Negative right padding."));
3346
3347 const Point<2> center;
3348
3349 auto min_line_length = [](const Triangulation<2> &tria) -> double {
3350 double length = std::numeric_limits<double>::max();
3351 for (const auto &cell : tria.active_cell_iterators())
3352 for (unsigned int n = 0; n < cell->n_lines(); ++n)
3353 length = std::min(length, cell->line(n)->diameter());
3354 return length;
3355 };
3356
3357 // start by setting up the cylinder triangulation
3358 Triangulation<2> cylinder_tria_maybe;
3359 Triangulation<2> &cylinder_tria = with_padding ? cylinder_tria_maybe : tria;
3361 inner_radius,
3362 outer_radius,
3363 L,
3364 /*repetitions*/ 1,
3365 colorize);
3366
3367 // we will deal with face manifold ids after we merge triangulations
3368 for (const auto &cell : cylinder_tria.active_cell_iterators())
3369 cell->set_manifold_id(tfi_manifold_id);
3370
3371 const Point<2> bl(-outer_radius - pad_left, -outer_radius - pad_bottom);
3372 const Point<2> tr(outer_radius + pad_right, outer_radius + pad_top);
3373 if (with_padding)
3374 {
3375 // hyper_cube_with_cylindrical_hole will have 2 cells along
3376 // each face, so the element size is outer_radius
3377
3378 auto add_sizes = [](std::vector<double> &step_sizes,
3379 const double padding,
3380 const double h) -> void {
3381 // use std::round instead of std::ceil to improve aspect ratio
3382 // in case padding is only slightly larger than h.
3383 const auto rounded =
3384 static_cast<unsigned int>(std::round(padding / h));
3385 // in case padding is much smaller than h, make sure we
3386 // have at least 1 element
3387 const unsigned int num = (padding > 0. && rounded == 0) ? 1 : rounded;
3388 for (unsigned int i = 0; i < num; ++i)
3389 step_sizes.push_back(padding / num);
3390 };
3391
3392 std::vector<std::vector<double>> step_sizes(2);
3393 // x-coord
3394 // left:
3395 add_sizes(step_sizes[0], pad_left, outer_radius);
3396 // center
3397 step_sizes[0].push_back(outer_radius);
3398 step_sizes[0].push_back(outer_radius);
3399 // right
3400 add_sizes(step_sizes[0], pad_right, outer_radius);
3401 // y-coord
3402 // bottom
3403 add_sizes(step_sizes[1], pad_bottom, outer_radius);
3404 // center
3405 step_sizes[1].push_back(outer_radius);
3406 step_sizes[1].push_back(outer_radius);
3407 // top
3408 add_sizes(step_sizes[1], pad_top, outer_radius);
3409
3410 // now create bulk
3411 Triangulation<2> bulk_tria;
3413 bulk_tria, step_sizes, bl, tr, colorize);
3414
3415 // now remove cells reserved from the cylindrical hole
3416 std::set<Triangulation<2>::active_cell_iterator> cells_to_remove;
3417 for (const auto &cell : bulk_tria.active_cell_iterators())
3418 if (internal::point_in_2d_box(cell->center(), center, outer_radius))
3419 cells_to_remove.insert(cell);
3420
3421 Triangulation<2> tria_without_cylinder;
3423 bulk_tria, cells_to_remove, tria_without_cylinder);
3424
3425 const double tolerance =
3426 std::min(min_line_length(tria_without_cylinder),
3427 min_line_length(cylinder_tria)) /
3428 2.0;
3429
3431 tria_without_cylinder, cylinder_tria, tria, tolerance, true);
3432 }
3433
3434 // now set manifold ids:
3435 for (const auto &cell : tria.active_cell_iterators())
3436 {
3437 // set all non-boundary manifold ids on the cells that came from the
3438 // grid around the cylinder to the new TFI manifold id.
3439 if (cell->manifold_id() == tfi_manifold_id)
3440 {
3441 for (const unsigned int face_n : cell->face_indices())
3442 {
3443 const auto &face = cell->face(face_n);
3444 if (face->at_boundary() &&
3445 internal::point_in_2d_box(face->center(),
3446 center,
3447 outer_radius * (1. - 1e-12)))
3448 face->set_manifold_id(polar_manifold_id);
3449 else
3450 face->set_manifold_id(tfi_manifold_id);
3451 }
3452 }
3453 else
3454 {
3455 // ensure that all other manifold ids (including the faces
3456 // opposite the cylinder) are set to the flat id
3457 cell->set_all_manifold_ids(numbers::flat_manifold_id);
3458 }
3459 }
3460
3461 static constexpr double tol =
3462 std::numeric_limits<double>::epsilon() * 10000;
3463 if (colorize)
3464 for (const auto &cell : tria.active_cell_iterators())
3465 for (const unsigned int face_n : cell->face_indices())
3466 {
3467 const auto face = cell->face(face_n);
3468 if (face->at_boundary())
3469 {
3470 const Point<2> center = face->center();
3471 // left side
3472 if (std::abs(center[0] - bl[0]) < tol * std::abs(bl[0]))
3473 face->set_boundary_id(0);
3474 // right side
3475 else if (std::abs(center[0] - tr[0]) < tol * std::abs(tr[0]))
3476 face->set_boundary_id(1);
3477 // bottom
3478 else if (std::abs(center[1] - bl[1]) < tol * std::abs(bl[1]))
3479 face->set_boundary_id(2);
3480 // top
3481 else if (std::abs(center[1] - tr[1]) < tol * std::abs(tr[1]))
3482 face->set_boundary_id(3);
3483 // cylinder boundary
3484 else
3485 {
3486 Assert(cell->manifold_id() == tfi_manifold_id,
3488 face->set_boundary_id(4);
3489 }
3490 }
3491 }
3492
3493 // move to the new center
3494 GridTools::shift(new_center, tria);
3495
3496 PolarManifold<2> polar_manifold(new_center);
3497 tria.set_manifold(polar_manifold_id, polar_manifold);
3498 tria.set_manifold(tfi_manifold_id, FlatManifold<2>());
3500 inner_manifold.initialize(tria);
3501 tria.set_manifold(tfi_manifold_id, inner_manifold);
3502 }
3503
3504
3505
3506 template <>
3507 void
3509 const double inner_radius,
3510 const double outer_radius,
3511 const double pad_bottom,
3512 const double pad_top,
3513 const double pad_left,
3514 const double pad_right,
3515 const Point<3> &new_center,
3516 const types::manifold_id polar_manifold_id,
3517 const types::manifold_id tfi_manifold_id,
3518 const double L,
3519 const unsigned int n_slices,
3520 const bool colorize)
3521 {
3522 Triangulation<2> tria_2;
3523 plate_with_a_hole(tria_2,
3524 inner_radius,
3525 outer_radius,
3526 pad_bottom,
3527 pad_top,
3528 pad_left,
3529 pad_right,
3530 Point<2>(new_center[0], new_center[1]),
3531 polar_manifold_id,
3532 tfi_manifold_id,
3533 L,
3534 n_slices,
3535 colorize);
3536
3537 // extrude to 3d
3538 extrude_triangulation(tria_2, n_slices, L, tria, true);
3539
3540 // shift in Z direction to match specified center
3541 GridTools::shift(Point<3>(0, 0, new_center[2] - L / 2.), tria);
3542
3543 // set up the new manifolds
3544 const Tensor<1, 3> direction{{0.0, 0.0, 1.0}};
3545 const CylindricalManifold<3> cylindrical_manifold(
3546 direction,
3547 /*axial_point*/ new_center);
3548 tria.set_manifold(polar_manifold_id, FlatManifold<3>());
3549 tria.set_manifold(tfi_manifold_id, FlatManifold<3>());
3551 inner_manifold.initialize(tria);
3552
3553 tria.set_manifold(polar_manifold_id, cylindrical_manifold);
3554 tria.set_manifold(tfi_manifold_id, inner_manifold);
3555 }
3556
3557
3558
3559 template <>
3560 void
3562 const double shell_region_width,
3563 const unsigned int n_shells,
3564 const double skewness,
3565 const bool colorize)
3566 {
3567 Assert(0.0 <= shell_region_width && shell_region_width < 0.05,
3568 ExcMessage("The width of the shell region must be less than 0.05 "
3569 "(and preferably close to 0.03)"));
3570 const types::manifold_id polar_manifold_id = 0;
3571 const types::manifold_id tfi_manifold_id = 1;
3572
3573 // We begin by setting up a grid that is 4 by 22 cells. While not
3574 // squares, these have pretty good aspect ratios.
3575 Triangulation<2> bulk_tria;
3577 {22u, 4u},
3578 Point<2>(0.0, 0.0),
3579 Point<2>(2.2, 0.41));
3580 // bulk_tria now looks like this:
3581 //
3582 // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
3583 // | | | | | | | | | | | | | | | | | | | | | | |
3584 // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
3585 // | |XX|XX| | | | | | | | | | | | | | | | | | | |
3586 // +--+--O--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
3587 // | |XX|XX| | | | | | | | | | | | | | | | | | | |
3588 // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
3589 // | | | | | | | | | | | | | | | | | | | | | | |
3590 // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
3591 //
3592 // Note that these cells are not quite squares: they are all 0.1 by
3593 // 0.1025.
3594 //
3595 // The next step is to remove the cells marked with XXs: we will place
3596 // the grid around the cylinder there later. The next loop does two
3597 // things:
3598 // 1. Determines which cells need to be removed from the Triangulation
3599 // (i.e., find the cells marked with XX in the picture).
3600 // 2. Finds the location of the vertex marked with 'O' and uses that to
3601 // calculate the shift vector for aligning cylinder_tria with
3602 // tria_without_cylinder.
3603 std::set<Triangulation<2>::active_cell_iterator> cells_to_remove;
3604 Tensor<1, 2> cylinder_triangulation_offset;
3605 for (const auto &cell : bulk_tria.active_cell_iterators())
3606 {
3607 if ((cell->center() - Point<2>(0.2, 0.2)).norm() < 0.15)
3608 cells_to_remove.insert(cell);
3609
3610 if (cylinder_triangulation_offset == Tensor<1, 2>())
3611 {
3612 for (const unsigned int vertex_n :
3614 if (cell->vertex(vertex_n) == Point<2>())
3615 {
3616 // cylinder_tria is centered at zero, so we need to
3617 // shift it up and to the right by two cells:
3618 cylinder_triangulation_offset =
3619 2.0 * (cell->vertex(3) - Point<2>());
3620 break;
3621 }
3622 }
3623 }
3624 Triangulation<2> tria_without_cylinder;
3626 bulk_tria, cells_to_remove, tria_without_cylinder);
3627
3628 // set up the cylinder triangulation. Note that this function sets the
3629 // manifold ids of the interior boundary cells to 0
3630 // (polar_manifold_id).
3631 Triangulation<2> cylinder_tria;
3633 0.05 + shell_region_width,
3634 0.41 / 4.0);
3635 // The bulk cells are not quite squares, so we need to move the left
3636 // and right sides of cylinder_tria inwards so that it fits in
3637 // bulk_tria:
3638 for (const auto &cell : cylinder_tria.active_cell_iterators())
3639 for (const unsigned int vertex_n : GeometryInfo<2>::vertex_indices())
3640 {
3641 if (std::abs(cell->vertex(vertex_n)[0] - -0.41 / 4.0) < 1e-10)
3642 cell->vertex(vertex_n)[0] = -0.1;
3643 else if (std::abs(cell->vertex(vertex_n)[0] - 0.41 / 4.0) < 1e-10)
3644 cell->vertex(vertex_n)[0] = 0.1;
3645 }
3646
3647 // Assign interior manifold ids to be the TFI id.
3648 for (const auto &cell : cylinder_tria.active_cell_iterators())
3649 {
3650 cell->set_manifold_id(tfi_manifold_id);
3651 for (const unsigned int face_n : GeometryInfo<2>::face_indices())
3652 if (!cell->face(face_n)->at_boundary())
3653 cell->face(face_n)->set_manifold_id(tfi_manifold_id);
3654 }
3655 if (0.0 < shell_region_width)
3656 {
3657 Assert(0 < n_shells,
3658 ExcMessage("If the shell region has positive width then "
3659 "there must be at least one shell."));
3660 Triangulation<2> shell_tria;
3662 Point<2>(),
3663 0.05,
3664 0.05 + shell_region_width,
3665 n_shells,
3666 skewness,
3667 8);
3668
3669 // Make the tolerance as large as possible since these cells can
3670 // be quite close together
3671 const double vertex_tolerance =
3672 std::min(internal::minimal_vertex_distance(shell_tria),
3673 internal::minimal_vertex_distance(cylinder_tria)) *
3674 0.5;
3675
3676 shell_tria.set_all_manifold_ids(polar_manifold_id);
3677 Triangulation<2> temp;
3679 shell_tria, cylinder_tria, temp, vertex_tolerance, true);
3680 cylinder_tria = std::move(temp);
3681 }
3682 GridTools::shift(cylinder_triangulation_offset, cylinder_tria);
3683
3684 // Compute the tolerance again, since the shells may be very close to
3685 // each-other:
3686 const double vertex_tolerance =
3687 std::min(internal::minimal_vertex_distance(tria_without_cylinder),
3688 internal::minimal_vertex_distance(cylinder_tria)) /
3689 10;
3691 tria_without_cylinder, cylinder_tria, tria, vertex_tolerance, true);
3692
3693 // Move the vertices in the middle of the faces of cylinder_tria slightly
3694 // to give a better mesh quality. We have to balance the quality of these
3695 // cells with the quality of the outer cells (initially rectangles). For
3696 // constant radial distance, we would place them at the distance 0.1 *
3697 // sqrt(2.) from the center. In case the shell region width is more than
3698 // 0.1/6., we choose to place them at 0.1 * 4./3. from the center, which
3699 // ensures that the shortest edge of the outer cells is 2./3. of the
3700 // original length. If the shell region width is less, we make the edge
3701 // length of the inner part and outer part (in the shorter x direction)
3702 // the same.
3703 {
3704 const double shift =
3705 std::min(0.125 + shell_region_width * 0.5, 0.1 * 4. / 3.);
3706 for (const auto &cell : tria.active_cell_iterators())
3707 for (const unsigned int v : GeometryInfo<2>::vertex_indices())
3708 if (cell->vertex(v).distance(Point<2>(0.1, 0.205)) < 1e-10)
3709 cell->vertex(v) = Point<2>(0.2 - shift, 0.205);
3710 else if (cell->vertex(v).distance(Point<2>(0.3, 0.205)) < 1e-10)
3711 cell->vertex(v) = Point<2>(0.2 + shift, 0.205);
3712 else if (cell->vertex(v).distance(Point<2>(0.2, 0.1025)) < 1e-10)
3713 cell->vertex(v) = Point<2>(0.2, 0.2 - shift);
3714 else if (cell->vertex(v).distance(Point<2>(0.2, 0.3075)) < 1e-10)
3715 cell->vertex(v) = Point<2>(0.2, 0.2 + shift);
3716 }
3717
3718 // Ensure that all manifold ids on a polar cell really are set to the
3719 // polar manifold id:
3720 for (const auto &cell : tria.active_cell_iterators())
3721 if (cell->manifold_id() == polar_manifold_id)
3722 cell->set_all_manifold_ids(polar_manifold_id);
3723
3724 // Ensure that all other manifold ids (including the interior faces
3725 // opposite the cylinder) are set to the flat manifold id:
3726 for (const auto &cell : tria.active_cell_iterators())
3727 if (cell->manifold_id() != polar_manifold_id &&
3728 cell->manifold_id() != tfi_manifold_id)
3729 cell->set_all_manifold_ids(numbers::flat_manifold_id);
3730
3731 // We need to calculate the current center so that we can move it later:
3732 // to start get a unique list of (points to) vertices on the cylinder
3733 std::vector<Point<2> *> cylinder_pointers;
3734 for (const auto &face : tria.active_face_iterators())
3735 if (face->manifold_id() == polar_manifold_id)
3736 {
3737 cylinder_pointers.push_back(&face->vertex(0));
3738 cylinder_pointers.push_back(&face->vertex(1));
3739 }
3740 // de-duplicate
3741 std::sort(cylinder_pointers.begin(), cylinder_pointers.end());
3742 cylinder_pointers.erase(std::unique(cylinder_pointers.begin(),
3743 cylinder_pointers.end()),
3744 cylinder_pointers.end());
3745
3746 // find the current center...
3747 Point<2> center;
3748 for (const Point<2> *const ptr : cylinder_pointers)
3749 center += *ptr / double(cylinder_pointers.size());
3750
3751 // and recenter at (0.2, 0.2)
3752 for (Point<2> *const ptr : cylinder_pointers)
3753 *ptr += Point<2>(0.2, 0.2) - center;
3754
3755 // attach manifolds
3756 PolarManifold<2> polar_manifold(Point<2>(0.2, 0.2));
3757 tria.set_manifold(polar_manifold_id, polar_manifold);
3758
3759 tria.set_manifold(tfi_manifold_id, FlatManifold<2>());
3761 inner_manifold.initialize(tria);
3762 tria.set_manifold(tfi_manifold_id, inner_manifold);
3763
3764 if (colorize)
3765 for (const auto &face : tria.active_face_iterators())
3766 if (face->at_boundary())
3767 {
3768 const Point<2> center = face->center();
3769 // left side
3770 if (std::abs(center[0] - 0.0) < 1e-10)
3771 face->set_boundary_id(0);
3772 // right side
3773 else if (std::abs(center[0] - 2.2) < 1e-10)
3774 face->set_boundary_id(1);
3775 // cylinder boundary
3776 else if (face->manifold_id() == polar_manifold_id)
3777 face->set_boundary_id(2);
3778 // sides of channel
3779 else
3780 {
3781 Assert(std::abs(center[1] - 0.00) < 1.0e-10 ||
3782 std::abs(center[1] - 0.41) < 1.0e-10,
3784 face->set_boundary_id(3);
3785 }
3786 }
3787 }
3788
3789
3790
3791 template <>
3792 void
3794 const double shell_region_width,
3795 const unsigned int n_shells,
3796 const double skewness,
3797 const bool colorize)
3798 {
3799 Triangulation<2> tria_2;
3801 tria_2, shell_region_width, n_shells, skewness, colorize);
3802 extrude_triangulation(tria_2, 5, 0.41, tria, true);
3803
3804 // set up the new 3d manifolds
3805 const types::manifold_id cylindrical_manifold_id = 0;
3806 const types::manifold_id tfi_manifold_id = 1;
3807 const PolarManifold<2> *const m_ptr =
3808 dynamic_cast<const PolarManifold<2> *>(
3809 &tria_2.get_manifold(cylindrical_manifold_id));
3810 Assert(m_ptr != nullptr, ExcInternalError());
3811 const Point<3> axial_point(m_ptr->center[0], m_ptr->center[1], 0.0);
3812 const Tensor<1, 3> direction{{0.0, 0.0, 1.0}};
3813
3814 tria.set_manifold(cylindrical_manifold_id, FlatManifold<3>());
3815 tria.set_manifold(tfi_manifold_id, FlatManifold<3>());
3816 const CylindricalManifold<3> cylindrical_manifold(direction, axial_point);
3818 inner_manifold.initialize(tria);
3819 tria.set_manifold(cylindrical_manifold_id, cylindrical_manifold);
3820 tria.set_manifold(tfi_manifold_id, inner_manifold);
3821
3822 // From extrude_triangulation: since the maximum boundary id of tria_2 was
3823 // 3, the bottom boundary id is 4 and the top is 5: both are walls, so set
3824 // them to 3
3825 if (colorize)
3826 for (const auto &face : tria.active_face_iterators())
3827 if (face->boundary_id() == 4 || face->boundary_id() == 5)
3828 face->set_boundary_id(3);
3829 }
3830
3831
3832
3833 template <int dim, int spacedim>
3834 void
3836 const std::vector<unsigned int> &sizes,
3837 const bool colorize)
3838 {
3840 Assert(dim > 1, ExcNotImplemented());
3841 Assert(dim < 4, ExcNotImplemented());
3842
3843 // If there is a desire at some point to change the geometry of
3844 // the cells, this tensor can be made an argument to the function.
3845 Tensor<1, dim> dimensions;
3846 for (unsigned int d = 0; d < dim; ++d)
3847 dimensions[d] = 1.;
3848
3849 std::vector<Point<spacedim>> points;
3850 unsigned int n_cells = 1;
3851 for (const unsigned int i : GeometryInfo<dim>::face_indices())
3852 n_cells += sizes[i];
3853
3854 std::vector<CellData<dim>> cells(n_cells);
3855 // Vertices of the center cell
3856 for (const unsigned int i : GeometryInfo<dim>::vertex_indices())
3857 {
3859 for (unsigned int d = 0; d < dim; ++d)
3860 p[d] = 0.5 * dimensions[d] *
3863 points.push_back(p);
3864 cells[0].vertices[i] = i;
3865 }
3866 cells[0].material_id = 0;
3867
3868 // The index of the first cell of the leg.
3869 unsigned int cell_index = 1;
3870 // The legs of the cross
3871 for (const unsigned int face : GeometryInfo<dim>::face_indices())
3872 {
3873 const unsigned int oface = GeometryInfo<dim>::opposite_face[face];
3874 const unsigned int dir = GeometryInfo<dim>::unit_normal_direction[face];
3875
3876 // We are moving in the direction of face
3877 for (unsigned int j = 0; j < sizes[face]; ++j, ++cell_index)
3878 {
3879 const unsigned int last_cell = (j == 0) ? 0U : (cell_index - 1);
3880
3881 for (unsigned int v = 0; v < GeometryInfo<dim>::vertices_per_face;
3882 ++v)
3883 {
3884 const unsigned int cellv =
3886 const unsigned int ocellv =
3888 // First the vertices which already exist
3889 cells[cell_index].vertices[ocellv] =
3890 cells[last_cell].vertices[cellv];
3891
3892 // Now the new vertices
3893 cells[cell_index].vertices[cellv] = points.size();
3894
3895 Point<spacedim> p = points[cells[cell_index].vertices[ocellv]];
3897 dimensions[dir];
3898 points.push_back(p);
3899 }
3900 cells[cell_index].material_id = (colorize) ? (face + 1U) : 0U;
3901 }
3902 }
3903 tria.create_triangulation(points, cells, SubCellData());
3904 }
3905
3906
3907 template <>
3908 void
3909 hyper_cube_slit(Triangulation<1> &, const double, const double, const bool)
3910 {
3912 }
3913
3914
3915
3916 template <>
3917 void
3919 const double,
3920 const double,
3921 const double,
3922 const bool)
3923 {
3925 }
3926
3927
3928
3929 template <>
3930 void
3931 hyper_L(Triangulation<1> &, const double, const double, const bool)
3932 {
3934 }
3935
3936
3937
3938 template <>
3939 void
3940 hyper_ball_balanced(Triangulation<1> &, const Point<1> &, const double)
3941 {
3943 }
3944
3945
3946
3947 template <>
3948 void
3949 cylinder(Triangulation<1> &, const double, const double)
3950 {
3952 }
3953
3954
3955 template <>
3956 void
3958 const unsigned int,
3959 const double,
3960 const double)
3961 {
3963 }
3964
3965
3966
3967 template <>
3968 void
3969 truncated_cone(Triangulation<1> &, const double, const double, const double)
3970 {
3972 }
3973
3974
3975
3976 template <>
3977 void
3979 const Point<1> &,
3980 const double,
3981 const double,
3982 const unsigned int,
3983 const bool)
3984 {
3986 }
3987
3988 template <>
3989 void
3991 const double,
3992 const double,
3993 const double,
3994 const unsigned int,
3995 const unsigned int,
3996 const bool)
3997 {
3999 }
4000
4001
4002 template <>
4003 void
4004 quarter_hyper_ball(Triangulation<1> &, const Point<1> &, const double)
4005 {
4007 }
4008
4009
4010 template <>
4011 void
4012 half_hyper_ball(Triangulation<1> &, const Point<1> &, const double)
4013 {
4015 }
4016
4017
4018 template <>
4019 void
4021 const Point<1> &,
4022 const double,
4023 const double,
4024 const unsigned int,
4025 const bool)
4026 {
4028 }
4029
4030 template <>
4031 void
4033 const Point<1> &,
4034 const double,
4035 const double,
4036 const unsigned int,
4037 const bool)
4038 {
4040 }
4041
4042 template <>
4043 void
4045 const double left,
4046 const double right,
4047 const double thickness,
4048 const bool colorize)
4049 {
4050 Assert(left < right,
4051 ExcMessage("Invalid left-to-right bounds of enclosed hypercube"));
4052
4053 std::vector<Point<2>> vertices(16);
4054 double coords[4];
4055 coords[0] = left - thickness;
4056 coords[1] = left;
4057 coords[2] = right;
4058 coords[3] = right + thickness;
4059
4060 unsigned int k = 0;
4061 for (const double y : coords)
4062 for (const double x : coords)
4063 vertices[k++] = Point<2>(x, y);
4064
4065 const types::material_id materials[9] = {5, 4, 6, 1, 0, 2, 9, 8, 10};
4066
4067 std::vector<CellData<2>> cells(9);
4068 k = 0;
4069 for (unsigned int i0 = 0; i0 < 3; ++i0)
4070 for (unsigned int i1 = 0; i1 < 3; ++i1)
4071 {
4072 cells[k].vertices[0] = i1 + 4 * i0;
4073 cells[k].vertices[1] = i1 + 4 * i0 + 1;
4074 cells[k].vertices[2] = i1 + 4 * i0 + 4;
4075 cells[k].vertices[3] = i1 + 4 * i0 + 5;
4076 if (colorize)
4077 cells[k].material_id = materials[k];
4078 ++k;
4079 }
4080 tria.create_triangulation(vertices,
4081 cells,
4082 SubCellData()); // no boundary information
4083 }
4084
4085
4086
4087 // Implementation for 2d only
4088 template <>
4089 void
4091 const double left,
4092 const double right,
4093 const bool colorize)
4094 {
4095 const double rl2 = (right + left) / 2;
4096 const Point<2> vertices[10] = {Point<2>(left, left),
4097 Point<2>(rl2, left),
4098 Point<2>(rl2, rl2),
4099 Point<2>(left, rl2),
4100 Point<2>(right, left),
4101 Point<2>(right, rl2),
4102 Point<2>(rl2, right),
4103 Point<2>(left, right),
4104 Point<2>(right, right),
4105 Point<2>(rl2, left)};
4106 const int cell_vertices[4][4] = {{0, 1, 3, 2},
4107 {9, 4, 2, 5},
4108 {3, 2, 7, 6},
4109 {2, 5, 6, 8}};
4110 std::vector<CellData<2>> cells(4, CellData<2>());
4111 for (unsigned int i = 0; i < 4; ++i)
4112 {
4113 for (unsigned int j = 0; j < 4; ++j)
4114 cells[i].vertices[j] = cell_vertices[i][j];
4115 cells[i].material_id = 0;
4116 }
4117 tria.create_triangulation(std::vector<Point<2>>(std::begin(vertices),
4118 std::end(vertices)),
4119 cells,
4120 SubCellData()); // no boundary information
4121
4122 if (colorize)
4123 {
4125 cell->face(1)->set_boundary_id(1);
4126 ++cell;
4127 cell->face(0)->set_boundary_id(2);
4128 }
4129 }
4130
4131
4132
4133 template <>
4134 void
4136 const double radius_0,
4137 const double radius_1,
4138 const double half_length)
4139 {
4140 Point<2> vertices_tmp[4];
4141
4142 vertices_tmp[0] = Point<2>(-half_length, -radius_0);
4143 vertices_tmp[1] = Point<2>(half_length, -radius_1);
4144 vertices_tmp[2] = Point<2>(-half_length, radius_0);
4145 vertices_tmp[3] = Point<2>(half_length, radius_1);
4146
4147 const std::vector<Point<2>> vertices(std::begin(vertices_tmp),
4148 std::end(vertices_tmp));
4149 unsigned int cell_vertices[1][GeometryInfo<2>::vertices_per_cell];
4150
4151 for (const unsigned int i : GeometryInfo<2>::vertex_indices())
4152 cell_vertices[0][i] = i;
4153
4154 std::vector<CellData<2>> cells(1, CellData<2>());
4155
4156 for (const unsigned int i : GeometryInfo<2>::vertex_indices())
4157 cells[0].vertices[i] = cell_vertices[0][i];
4158
4159 cells[0].material_id = 0;
4160 triangulation.create_triangulation(vertices, cells, SubCellData());
4161
4163
4164 cell->face(0)->set_boundary_id(1);
4165 cell->face(1)->set_boundary_id(2);
4166
4167 for (unsigned int i = 2; i < 4; ++i)
4168 cell->face(i)->set_boundary_id(0);
4169 }
4170
4171
4172
4173 // Implementation for 2d only
4174 template <>
4175 void
4177 const double a,
4178 const double b,
4179 const bool colorize)
4180 {
4181 const Point<2> vertices[8] = {Point<2>(a, a),
4182 Point<2>((a + b) / 2, a),
4183 Point<2>(b, a),
4184 Point<2>(a, (a + b) / 2),
4185 Point<2>((a + b) / 2, (a + b) / 2),
4186 Point<2>(b, (a + b) / 2),
4187 Point<2>(a, b),
4188 Point<2>((a + b) / 2, b)};
4189 const int cell_vertices[3][4] = {{0, 1, 3, 4}, {1, 2, 4, 5}, {3, 4, 6, 7}};
4190
4191 std::vector<CellData<2>> cells(3, CellData<2>());
4192
4193 for (unsigned int i = 0; i < 3; ++i)
4194 {
4195 for (unsigned int j = 0; j < 4; ++j)
4196 cells[i].vertices[j] = cell_vertices[i][j];
4197 cells[i].material_id = 0;
4198 }
4199
4200 tria.create_triangulation(std::vector<Point<2>>(std::begin(vertices),
4201 std::end(vertices)),
4202 cells,
4203 SubCellData());
4204
4205 if (colorize)
4206 {
4208
4209 cell->face(0)->set_boundary_id(0);
4210 cell->face(2)->set_boundary_id(1);
4211 ++cell;
4212
4213 cell->face(1)->set_boundary_id(2);
4214 cell->face(2)->set_boundary_id(1);
4215 cell->face(3)->set_boundary_id(3);
4216 ++cell;
4217
4218 cell->face(0)->set_boundary_id(0);
4219 cell->face(1)->set_boundary_id(4);
4220 cell->face(3)->set_boundary_id(5);
4221 }
4222 }
4223
4224
4225
4226 template <int dim, int spacedim>
4227 void
4229 const std::vector<unsigned int> &repetitions,
4230 const Point<dim> &bottom_left,
4231 const Point<dim> &top_right,
4232 const std::vector<int> &n_cells_to_remove)
4233 {
4234 Assert(dim > 1, ExcNotImplemented());
4235 // Check the consistency of the dimensions provided.
4236 AssertDimension(repetitions.size(), dim);
4237 AssertDimension(n_cells_to_remove.size(), dim);
4238 for (unsigned int d = 0; d < dim; ++d)
4239 {
4240 Assert(std::fabs(n_cells_to_remove[d]) <= repetitions[d],
4241 ExcMessage("Attempting to cut away too many cells."));
4242 }
4243 // Create the domain to be cut
4246 repetitions,
4247 bottom_left,
4248 top_right);
4249 // compute the vertex of the cut step, we will cut according to the
4250 // location of the cartesian coordinates of the cell centers
4251 std::array<double, dim> h;
4252 Point<dim> cut_step;
4253 for (unsigned int d = 0; d < dim; ++d)
4254 {
4255 // mesh spacing in each direction in cartesian coordinates
4256 h[d] = (top_right[d] - bottom_left[d]) / repetitions[d];
4257 // left to right, bottom to top, front to back
4258 if (n_cells_to_remove[d] >= 0)
4259 {
4260 // cartesian coordinates of vertex location
4261 cut_step[d] =
4262 h[d] * std::fabs(n_cells_to_remove[d]) + bottom_left[d];
4263 }
4264 // right to left, top to bottom, back to front
4265 else
4266 {
4267 cut_step[d] = top_right[d] - h[d] * std::fabs(n_cells_to_remove[d]);
4268 }
4269 }
4270
4271
4272 // compute cells to remove
4273 std::set<typename Triangulation<dim, spacedim>::active_cell_iterator>
4274 cells_to_remove;
4275 for (const auto &cell : rectangle.active_cell_iterators())
4276 {
4277 bool remove_cell = true;
4278 for (unsigned int d = 0; d < dim && remove_cell; ++d)
4279 if ((n_cells_to_remove[d] > 0 && cell->center()[d] >= cut_step[d]) ||
4280 (n_cells_to_remove[d] < 0 && cell->center()[d] <= cut_step[d]))
4281 remove_cell = false;
4282 if (remove_cell)
4283 cells_to_remove.insert(cell);
4284 }
4285
4287 cells_to_remove,
4288 tria);
4289 }
4290
4291
4292
4293 template <int dim, int spacedim>
4294 void
4296 const Point<spacedim> &p,
4297 const double radius,
4298 const bool internal_manifolds)
4299 {
4300 if constexpr (dim == 2)
4301 {
4302 const auto embed_point = [](const double x,
4303 const double y) -> Point<spacedim> {
4304 if constexpr (spacedim == 2)
4305 return Point<spacedim>(x, y);
4306 else if constexpr (spacedim == 3)
4307 return Point<spacedim>(x, y, 0);
4308 else
4310 };
4311
4312
4313 // Equilibrate cell sizes at transition from the inner part
4314 // to the radial cells
4315 const double a = 1. / (1 + std::sqrt(2.0));
4316 const Point<spacedim> vertices[8] = {
4317 p + embed_point(-1., -1.) * (radius / std::sqrt(2.0)),
4318 p + embed_point(+1., -1.) * (radius / std::sqrt(2.0)),
4319 p + embed_point(-1., -1.) * (radius / std::sqrt(2.0) * a),
4320 p + embed_point(+1., -1.) * (radius / std::sqrt(2.0) * a),
4321 p + embed_point(-1., +1.) * (radius / std::sqrt(2.0) * a),
4322 p + embed_point(+1., +1.) * (radius / std::sqrt(2.0) * a),
4323 p + embed_point(-1., +1.) * (radius / std::sqrt(2.0)),
4324 p + embed_point(+1., +1.) * (radius / std::sqrt(2.0))};
4325
4326 std::vector<CellData<2>> cells(5, CellData<2>());
4327
4328 for (unsigned int i = 0; i < 5; ++i)
4329 {
4330 for (unsigned int j = 0; j < 4; ++j)
4331 cells[i].vertices[j] = circle_cell_vertices[i][j];
4332 cells[i].material_id = 0;
4333 cells[i].manifold_id = (i == 2 ? numbers::flat_manifold_id : 1);
4334 }
4335
4336 tria.create_triangulation(std::vector<Point<spacedim>>(
4337 std::begin(vertices), std::end(vertices)),
4338 cells,
4339 SubCellData()); // no boundary information
4342 if (internal_manifolds)
4344 else
4346 }
4347 else if constexpr (dim == 3)
4348 {
4349 const double a =
4350 1. / (1 + std::sqrt(3.0)); // equilibrate cell sizes at transition
4351 // from the inner part to the radial
4352 // cells
4353 const unsigned int n_vertices = 16;
4354 const Point<3> vertices[n_vertices] = {
4355 // first the vertices of the inner
4356 // cell
4357 p + Point<3>(-1, -1, -1) * (radius / std::sqrt(3.0) * a),
4358 p + Point<3>(+1, -1, -1) * (radius / std::sqrt(3.0) * a),
4359 p + Point<3>(+1, -1, +1) * (radius / std::sqrt(3.0) * a),
4360 p + Point<3>(-1, -1, +1) * (radius / std::sqrt(3.0) * a),
4361 p + Point<3>(-1, +1, -1) * (radius / std::sqrt(3.0) * a),
4362 p + Point<3>(+1, +1, -1) * (radius / std::sqrt(3.0) * a),
4363 p + Point<3>(+1, +1, +1) * (radius / std::sqrt(3.0) * a),
4364 p + Point<3>(-1, +1, +1) * (radius / std::sqrt(3.0) * a),
4365 // now the eight vertices at
4366 // the outer sphere
4367 p + Point<3>(-1, -1, -1) * (radius / std::sqrt(3.0)),
4368 p + Point<3>(+1, -1, -1) * (radius / std::sqrt(3.0)),
4369 p + Point<3>(+1, -1, +1) * (radius / std::sqrt(3.0)),
4370 p + Point<3>(-1, -1, +1) * (radius / std::sqrt(3.0)),
4371 p + Point<3>(-1, +1, -1) * (radius / std::sqrt(3.0)),
4372 p + Point<3>(+1, +1, -1) * (radius / std::sqrt(3.0)),
4373 p + Point<3>(+1, +1, +1) * (radius / std::sqrt(3.0)),
4374 p + Point<3>(-1, +1, +1) * (radius / std::sqrt(3.0)),
4375 };
4376
4377 // one needs to draw the seven cubes to
4378 // understand what's going on here
4379 const unsigned int n_cells = 7;
4380 const int cell_vertices[n_cells][8] = {
4381 {0, 1, 4, 5, 3, 2, 7, 6}, // center
4382 {8, 9, 12, 13, 0, 1, 4, 5}, // bottom
4383 {9, 13, 1, 5, 10, 14, 2, 6}, // right
4384 {11, 10, 3, 2, 15, 14, 7, 6}, // top
4385 {8, 0, 12, 4, 11, 3, 15, 7}, // left
4386 {8, 9, 0, 1, 11, 10, 3, 2}, // front
4387 {12, 4, 13, 5, 15, 7, 14, 6}}; // back
4388
4389 std::vector<CellData<3>> cells(n_cells, CellData<3>());
4390
4391 for (unsigned int i = 0; i < n_cells; ++i)
4392 {
4393 for (const unsigned int j : GeometryInfo<3>::vertex_indices())
4394 cells[i].vertices[j] = cell_vertices[i][j];
4395 cells[i].material_id = 0;
4396 cells[i].manifold_id = i == 0 ? numbers::flat_manifold_id : 1;
4397 }
4398
4399 tria.create_triangulation(std::vector<Point<3>>(std::begin(vertices),
4400 std::end(vertices)),
4401 cells,
4402 SubCellData()); // no boundary information
4405 if (internal_manifolds)
4407 else
4408 tria.set_manifold(1, FlatManifold<3>());
4409 }
4410 else
4412 }
4413
4414
4415
4416 template <int spacedim>
4417 void
4418 hyper_shell_2D(Triangulation<2, spacedim> &tria,
4419 const Point<spacedim> &center,
4420 const double inner_radius,
4421 const double outer_radius,
4422 const unsigned int n_cells,
4423 const bool colorize)
4424 {
4425 Assert((inner_radius > 0) && (inner_radius < outer_radius),
4426 ExcInvalidRadii());
4427
4428 const double pi = numbers::PI;
4429
4430 // determine the number of cells
4431 // for the grid. if not provided by
4432 // the user determine it such that
4433 // the length of each cell on the
4434 // median (in the middle between
4435 // the two circles) is equal to its
4436 // radial extent (which is the
4437 // difference between the two
4438 // radii)
4439 const unsigned int N =
4440 (n_cells == 0 ? static_cast<unsigned int>(
4441 std::ceil((2 * pi * (outer_radius + inner_radius) / 2) /
4442 (outer_radius - inner_radius))) :
4443 n_cells);
4444
4445 // set up N vertices on the
4446 // outer and N vertices on
4447 // the inner circle. the
4448 // first N ones are on the
4449 // outer one, and all are
4450 // numbered counter-clockwise
4451 std::vector<Point<spacedim>> vertices(2 * N);
4452 for (unsigned int i = 0; i < N; ++i)
4453 {
4455 point[0] = std::cos(2 * pi * i / N);
4456 point[1] = std::sin(2 * pi * i / N);
4457
4458 vertices[i] = point * outer_radius;
4459 vertices[i + N] = vertices[i] * (inner_radius / outer_radius);
4460
4461 vertices[i] += center;
4462 vertices[i + N] += center;
4463 }
4464
4465 std::vector<CellData<2>> cells(N, CellData<2>());
4466
4467 for (unsigned int i = 0; i < N; ++i)
4468 {
4469 cells[i].vertices[0] = i;
4470 cells[i].vertices[1] = (i + 1) % N;
4471 cells[i].vertices[2] = N + i;
4472 cells[i].vertices[3] = N + ((i + 1) % N);
4473
4474 cells[i].material_id = 0;
4475 }
4476
4477 tria.create_triangulation(vertices, cells, SubCellData());
4478
4479 if (colorize)
4480 colorize_hyper_shell(tria, center, inner_radius, outer_radius);
4481
4482 tria.set_all_manifold_ids(0);
4484 }
4485
4486
4487
4488 template <>
4489 void
4491 const Point<2> &center,
4492 const double inner_radius,
4493 const double outer_radius,
4494 const unsigned int n_cells,
4495 const bool colorize)
4496 {
4497 hyper_shell_2D(tria, center, inner_radius, outer_radius, n_cells, colorize);
4498 }
4499
4500
4501
4502 template <>
4503 void
4505 const Point<3> &center,
4506 const double inner_radius,
4507 const double outer_radius,
4508 const unsigned int n_cells,
4509 const bool colorize)
4510 {
4511 hyper_shell_2D(tria, center, inner_radius, outer_radius, n_cells, colorize);
4512 }
4513
4514
4515
4516 template <int dim>
4517 void
4519 const Point<dim> &inner_center,
4520 const Point<dim> &outer_center,
4521 const double inner_radius,
4522 const double outer_radius,
4523 const unsigned int n_cells)
4524 {
4526 tria, outer_center, inner_radius, outer_radius, n_cells, true);
4527
4528 // check the consistency of the dimensions provided
4529 Assert(
4530 outer_radius - inner_radius > outer_center.distance(inner_center),
4532 "The inner radius is greater than or equal to the outer radius plus eccentricity."));
4533
4534 // shift nodes along the inner boundary according to the position of
4535 // inner_circle
4536 std::set<Point<dim> *> vertices_to_move;
4537
4538 for (const auto &face : tria.active_face_iterators())
4539 if (face->boundary_id() == 0)
4540 for (unsigned int v = 0; v < GeometryInfo<dim>::vertices_per_face; ++v)
4541 vertices_to_move.insert(&face->vertex(v));
4542
4543 const auto shift = inner_center - outer_center;
4544 for (const auto &p : vertices_to_move)
4545 (*p) += shift;
4546
4547 // the original hyper_shell function assigns the same manifold id
4548 // to all cells and faces. Set all manifolds ids to a different
4549 // value (2), then use boundary ids to assign different manifolds to
4550 // the inner (0) and outer manifolds (1). Use a transfinite manifold
4551 // for all faces and cells aside from the boundaries.
4552 tria.set_all_manifold_ids(2);
4554
4555 SphericalManifold<dim> inner_manifold(inner_center);
4556 SphericalManifold<dim> outer_manifold(outer_center);
4557
4562 transfinite.initialize(tria);
4563
4564 tria.set_manifold(0, inner_manifold);
4565 tria.set_manifold(1, outer_manifold);
4566 tria.set_manifold(2, transfinite);
4567 }
4568
4569
4570
4571 // Implementation for 2d only
4572 template <>
4573 void
4575 const double radius,
4576 const double half_length)
4577 {
4578 Point<2> p1(-half_length, -radius);
4579 Point<2> p2(half_length, radius);
4580
4581 hyper_rectangle(tria, p1, p2, true);
4582
4585 while (f != end)
4586 {
4587 switch (f->boundary_id())
4588 {
4589 case 0:
4590 f->set_boundary_id(1);
4591 break;
4592 case 1:
4593 f->set_boundary_id(2);
4594 break;
4595 default:
4596 f->set_boundary_id(0);
4597 break;
4598 }
4599 ++f;
4600 }
4601 }
4602
4603 template <>
4604 void
4606 const unsigned int,
4607 const double,
4608 const double)
4609 {
4611 }
4612
4613
4614
4615 // Implementation for 2d only
4616 template <>
4617 void
4619 const double,
4620 const double,
4621 const double,
4622 const unsigned int,
4623 const unsigned int,
4624 const bool)
4625 {
4627 }
4628
4629
4630 template <>
4631 void
4633 const Point<2> &p,
4634 const double radius)
4635 {
4636 const unsigned int dim = 2;
4637
4638 // the numbers 0.55647 and 0.42883 have been found by a search for the
4639 // best aspect ratio (defined as the maximal between the minimal singular
4640 // value of the Jacobian)
4641 const Point<dim> vertices[7] = {p + Point<dim>(0, 0) * radius,
4642 p + Point<dim>(+1, 0) * radius,
4643 p + Point<dim>(+1, 0) * (radius * 0.55647),
4644 p + Point<dim>(0, +1) * (radius * 0.55647),
4645 p + Point<dim>(+1, +1) * (radius * 0.42883),
4646 p + Point<dim>(0, +1) * radius,
4647 p + Point<dim>(+1, +1) *
4648 (radius / std::sqrt(2.0))};
4649
4650 const int cell_vertices[3][4] = {{0, 2, 3, 4}, {1, 6, 2, 4}, {5, 3, 6, 4}};
4651
4652 std::vector<CellData<dim>> cells(3, CellData<dim>());
4653
4654 for (unsigned int i = 0; i < 3; ++i)
4655 {
4656 for (unsigned int j = 0; j < 4; ++j)
4657 cells[i].vertices[j] = cell_vertices[i][j];
4658 cells[i].material_id = 0;
4659 }
4660
4661 tria.create_triangulation(std::vector<Point<dim>>(std::begin(vertices),
4662 std::end(vertices)),
4663 cells,
4664 SubCellData()); // no boundary information
4665
4668
4670
4671 while (cell != end)
4672 {
4673 for (const unsigned int i : GeometryInfo<dim>::face_indices())
4674 {
4675 if (cell->face(i)->boundary_id() ==
4677 continue;
4678
4679 // If one the components is the same as the respective
4680 // component of the center, then this is part of the plane
4681 if (cell->face(i)->center()[0] < p[0] + 1.e-5 * radius ||
4682 cell->face(i)->center()[1] < p[1] + 1.e-5 * radius)
4683 {
4684 cell->face(i)->set_boundary_id(1);
4685 cell->face(i)->set_manifold_id(numbers::flat_manifold_id);
4686 }
4687 }
4688 ++cell;
4689 }
4691 }
4692
4693
4694 template <>
4695 void
4697 const Point<2> &p,
4698 const double radius)
4699 {
4700 // equilibrate cell sizes at
4701 // transition from the inner part
4702 // to the radial cells
4703 const double a = 1. / (1 + std::sqrt(2.0));
4704 const Point<2> vertices[8] = {
4705 p + Point<2>(0, -1) * radius,
4706 p + Point<2>(+1, -1) * (radius / std::sqrt(2.0)),
4707 p + Point<2>(0, -1) * (radius / std::sqrt(2.0) * a),
4708 p + Point<2>(+1, -1) * (radius / std::sqrt(2.0) * a),
4709 p + Point<2>(0, +1) * (radius / std::sqrt(2.0) * a),
4710 p + Point<2>(+1, +1) * (radius / std::sqrt(2.0) * a),
4711 p + Point<2>(0, +1) * radius,
4712 p + Point<2>(+1, +1) * (radius / std::sqrt(2.0))};
4713
4714 const int cell_vertices[5][4] = {{0, 1, 2, 3},
4715 {2, 3, 4, 5},
4716 {1, 7, 3, 5},
4717 {6, 4, 7, 5}};
4718
4719 std::vector<CellData<2>> cells(4, CellData<2>());
4720
4721 for (unsigned int i = 0; i < 4; ++i)
4722 {
4723 for (unsigned int j = 0; j < 4; ++j)
4724 cells[i].vertices[j] = cell_vertices[i][j];
4725 cells[i].material_id = 0;
4726 }
4727
4728 tria.create_triangulation(std::vector<Point<2>>(std::begin(vertices),
4729 std::end(vertices)),
4730 cells,
4731 SubCellData()); // no boundary information
4732
4735
4737
4738 while (cell != end)
4739 {
4740 for (const unsigned int i : GeometryInfo<2>::face_indices())
4741 {
4742 if (cell->face(i)->boundary_id() ==
4744 continue;
4745
4746 // If x is zero, then this is part of the plane
4747 if (cell->face(i)->center()[0] < p[0] + 1.e-5 * radius)
4748 {
4749 cell->face(i)->set_boundary_id(1);
4750 cell->face(i)->set_manifold_id(numbers::flat_manifold_id);
4751 }
4752 }
4753 ++cell;
4754 }
4756 }
4757
4758
4759
4760 // Implementation for 2d only
4761 template <>
4762 void
4764 const Point<2> &center,
4765 const double inner_radius,
4766 const double outer_radius,
4767 const unsigned int n_cells,
4768 const bool colorize)
4769 {
4770 Assert((inner_radius > 0) && (inner_radius < outer_radius),
4771 ExcInvalidRadii());
4772
4773 const double pi = numbers::PI;
4774 // determine the number of cells
4775 // for the grid. if not provided by
4776 // the user determine it such that
4777 // the length of each cell on the
4778 // median (in the middle between
4779 // the two circles) is equal to its
4780 // radial extent (which is the
4781 // difference between the two
4782 // radii)
4783 const unsigned int N =
4784 (n_cells == 0 ? static_cast<unsigned int>(
4785 std::ceil((pi * (outer_radius + inner_radius) / 2) /
4786 (outer_radius - inner_radius))) :
4787 n_cells);
4788
4789 // set up N+1 vertices on the
4790 // outer and N+1 vertices on
4791 // the inner circle. the
4792 // first N+1 ones are on the
4793 // outer one, and all are
4794 // numbered counter-clockwise
4795 std::vector<Point<2>> vertices(2 * (N + 1));
4796 for (unsigned int i = 0; i <= N; ++i)
4797 {
4798 // enforce that the x-coordinates
4799 // of the first and last point of
4800 // each half-circle are exactly
4801 // zero (contrary to what we may
4802 // compute using the imprecise
4803 // value of pi)
4804 vertices[i] =
4805 Point<2>(((i == 0) || (i == N) ? 0 : std::cos(pi * i / N - pi / 2)),
4806 std::sin(pi * i / N - pi / 2)) *
4807 outer_radius;
4808 vertices[i + N + 1] = vertices[i] * (inner_radius / outer_radius);
4809
4810 vertices[i] += center;
4811 vertices[i + N + 1] += center;
4812 }
4813
4814
4815 std::vector<CellData<2>> cells(N, CellData<2>());
4816
4817 for (unsigned int i = 0; i < N; ++i)
4818 {
4819 cells[i].vertices[0] = i;
4820 cells[i].vertices[1] = (i + 1) % (N + 1);
4821 cells[i].vertices[2] = N + 1 + i;
4822 cells[i].vertices[3] = N + 1 + ((i + 1) % (N + 1));
4823
4824 cells[i].material_id = 0;
4825 }
4826
4827 tria.create_triangulation(vertices, cells, SubCellData());
4828
4829 if (colorize)
4830 {
4832 for (; cell != tria.end(); ++cell)
4833 {
4834 cell->face(2)->set_boundary_id(1);
4835 }
4836 tria.begin()->face(0)->set_boundary_id(3);
4837
4838 tria.last()->face(1)->set_boundary_id(2);
4839 }
4840 tria.set_all_manifold_ids(0);
4841 tria.set_manifold(0, SphericalManifold<2>(center));
4842 }
4843
4844
4845 template <>
4846 void
4848 const Point<2> &center,
4849 const double inner_radius,
4850 const double outer_radius,
4851 const unsigned int n_cells,
4852 const bool colorize)
4853 {
4854 Assert((inner_radius > 0) && (inner_radius < outer_radius),
4855 ExcInvalidRadii());
4856
4857 const double pi = numbers::PI;
4858 // determine the number of cells
4859 // for the grid. if not provided by
4860 // the user determine it such that
4861 // the length of each cell on the
4862 // median (in the middle between
4863 // the two circles) is equal to its
4864 // radial extent (which is the
4865 // difference between the two
4866 // radii)
4867 const unsigned int N =
4868 (n_cells == 0 ? static_cast<unsigned int>(
4869 std::ceil((pi * (outer_radius + inner_radius) / 4) /
4870 (outer_radius - inner_radius))) :
4871 n_cells);
4872
4873 // set up N+1 vertices on the
4874 // outer and N+1 vertices on
4875 // the inner circle. the
4876 // first N+1 ones are on the
4877 // outer one, and all are
4878 // numbered counter-clockwise
4879 std::vector<Point<2>> vertices(2 * (N + 1));
4880 for (unsigned int i = 0; i <= N; ++i)
4881 {
4882 // enforce that the x-coordinates
4883 // of the last point is exactly
4884 // zero (contrary to what we may
4885 // compute using the imprecise
4886 // value of pi)
4887 vertices[i] = Point<2>(((i == N) ? 0 : std::cos(pi * i / N / 2)),
4888 std::sin(pi * i / N / 2)) *
4889 outer_radius;
4890 vertices[i + N + 1] = vertices[i] * (inner_radius / outer_radius);
4891
4892 vertices[i] += center;
4893 vertices[i + N + 1] += center;
4894 }
4895
4896
4897 std::vector<CellData<2>> cells(N, CellData<2>());
4898
4899 for (unsigned int i = 0; i < N; ++i)
4900 {
4901 cells[i].vertices[0] = i;
4902 cells[i].vertices[1] = (i + 1) % (N + 1);
4903 cells[i].vertices[2] = N + 1 + i;
4904 cells[i].vertices[3] = N + 1 + ((i + 1) % (N + 1));
4905
4906 cells[i].material_id = 0;
4907 }
4908
4909 tria.create_triangulation(vertices, cells, SubCellData());
4910
4911 if (colorize)
4912 {
4914 for (; cell != tria.end(); ++cell)
4915 {
4916 cell->face(2)->set_boundary_id(1);
4917 }
4918 tria.begin()->face(0)->set_boundary_id(3);
4919
4920 tria.last()->face(1)->set_boundary_id(2);
4921 }
4922
4923 tria.set_all_manifold_ids(0);
4924 tria.set_manifold(0, SphericalManifold<2>(center));
4925 }
4926
4927
4928
4929 // Implementation for 3d only
4930 template <>
4931 void
4933 const double left,
4934 const double right,
4935 const bool colorize)
4936 {
4937 const double rl2 = (right + left) / 2;
4938 const double len = (right - left) / 2.;
4939
4940 const Point<3> vertices[20] = {
4941 Point<3>(left, left, -len / 2.), Point<3>(rl2, left, -len / 2.),
4942 Point<3>(rl2, rl2, -len / 2.), Point<3>(left, rl2, -len / 2.),
4943 Point<3>(right, left, -len / 2.), Point<3>(right, rl2, -len / 2.),
4944 Point<3>(rl2, right, -len / 2.), Point<3>(left, right, -len / 2.),
4945 Point<3>(right, right, -len / 2.), Point<3>(rl2, left, -len / 2.),
4946 Point<3>(left, left, len / 2.), Point<3>(rl2, left, len / 2.),
4947 Point<3>(rl2, rl2, len / 2.), Point<3>(left, rl2, len / 2.),
4948 Point<3>(right, left, len / 2.), Point<3>(right, rl2, len / 2.),
4949 Point<3>(rl2, right, len / 2.), Point<3>(left, right, len / 2.),
4950 Point<3>(right, right, len / 2.), Point<3>(rl2, left, len / 2.)};
4951 const int cell_vertices[4][8] = {{0, 1, 3, 2, 10, 11, 13, 12},
4952 {9, 4, 2, 5, 19, 14, 12, 15},
4953 {3, 2, 7, 6, 13, 12, 17, 16},
4954 {2, 5, 6, 8, 12, 15, 16, 18}};
4955 std::vector<CellData<3>> cells(4, CellData<3>());
4956 for (unsigned int i = 0; i < 4; ++i)
4957 {
4958 for (unsigned int j = 0; j < 8; ++j)
4959 cells[i].vertices[j] = cell_vertices[i][j];
4960 cells[i].material_id = 0;
4961 }
4962 tria.create_triangulation(std::vector<Point<3>>(std::begin(vertices),
4963 std::end(vertices)),
4964 cells,
4965 SubCellData()); // no boundary information
4966
4967 if (colorize)
4968 {
4970 cell->face(1)->set_boundary_id(1);
4971 ++cell;
4972 cell->face(0)->set_boundary_id(2);
4973 }
4974 }
4975
4976
4977
4978 // Implementation for 3d only
4979 template <>
4980 void
4982 const double left,
4983 const double right,
4984 const double thickness,
4985 const bool colorize)
4986 {
4987 Assert(left < right,
4988 ExcMessage("Invalid left-to-right bounds of enclosed hypercube"));
4989
4990 std::vector<Point<3>> vertices(64);
4991 double coords[4];
4992 coords[0] = left - thickness;
4993 coords[1] = left;
4994 coords[2] = right;
4995 coords[3] = right + thickness;
4996
4997 unsigned int k = 0;
4998 for (const double z : coords)
4999 for (const double y : coords)
5000 for (const double x : coords)
5001 vertices[k++] = Point<3>(x, y, z);
5002
5003 const types::material_id materials[27] = {21, 20, 22, 17, 16, 18, 25,
5004 24, 26, 5, 4, 6, 1, 0,
5005 2, 9, 8, 10, 37, 36, 38,
5006 33, 32, 34, 41, 40, 42};
5007
5008 std::vector<CellData<3>> cells(27);
5009 k = 0;
5010 for (unsigned int z = 0; z < 3; ++z)
5011 for (unsigned int y = 0; y < 3; ++y)
5012 for (unsigned int x = 0; x < 3; ++x)
5013 {
5014 cells[k].vertices[0] = x + 4 * y + 16 * z;
5015 cells[k].vertices[1] = x + 4 * y + 16 * z + 1;
5016 cells[k].vertices[2] = x + 4 * y + 16 * z + 4;
5017 cells[k].vertices[3] = x + 4 * y + 16 * z + 5;
5018 cells[k].vertices[4] = x + 4 * y + 16 * z + 16;
5019 cells[k].vertices[5] = x + 4 * y + 16 * z + 17;
5020 cells[k].vertices[6] = x + 4 * y + 16 * z + 20;
5021 cells[k].vertices[7] = x + 4 * y + 16 * z + 21;
5022 if (colorize)
5023 cells[k].material_id = materials[k];
5024 ++k;
5025 }
5026 tria.create_triangulation(vertices,
5027 cells,
5028 SubCellData()); // no boundary information
5029 }
5030
5031
5032
5033 template <>
5034 void
5036 const double radius_0,
5037 const double radius_1,
5038 const double half_length)
5039 {
5040 Assert(triangulation.n_cells() == 0,
5041 ExcMessage("The output triangulation object needs to be empty."));
5042 Assert(0 < radius_0, ExcMessage("The radii must be positive."));
5043 Assert(0 < radius_1, ExcMessage("The radii must be positive."));
5044 Assert(0 < half_length, ExcMessage("The half length must be positive."));
5045
5046 const auto n_slices = 1 + static_cast<unsigned int>(std::ceil(
5047 half_length / std::max(radius_0, radius_1)));
5048
5049 Triangulation<2> triangulation_2;
5050 GridGenerator::hyper_ball(triangulation_2, Point<2>(), radius_0);
5052 n_slices,
5053 2 * half_length,
5056 GridTools::shift(Tensor<1, 3>({-half_length, 0.0, 0.0}), triangulation);
5057 // At this point we have a cylinder. Multiply the y and z coordinates by a
5058 // factor that scales (with x) linearly between radius_0 and radius_1 to fix
5059 // the circle radii and interior points:
5060 auto shift_radii = [=](const Point<3> &p) {
5061 const double slope = (radius_1 / radius_0 - 1.0) / (2.0 * half_length);
5062 const double factor = slope * (p[0] - -half_length) + 1.0;
5063 return Point<3>(p[0], factor * p[1], factor * p[2]);
5064 };
5065 GridTools::transform(shift_radii, triangulation);
5066
5067 // Set boundary ids at -half_length to 1 and at half_length to 2. Set the
5068 // manifold id on hull faces (i.e., faces not on either end) to 0.
5069 for (const auto &face : triangulation.active_face_iterators())
5070 if (face->at_boundary())
5071 {
5072 if (std::abs(face->center()[0] - -half_length) < 1e-8 * half_length)
5073 face->set_boundary_id(1);
5074 else if (std::abs(face->center()[0] - half_length) <
5075 1e-8 * half_length)
5076 face->set_boundary_id(2);
5077 else
5078 face->set_all_manifold_ids(0);
5079 }
5080
5081 triangulation.set_manifold(0, CylindricalManifold<3>());
5082 }
5083
5084
5085 // Implementation for 3d only
5086 template <>
5087 void
5089 const double a,
5090 const double b,
5091 const bool colorize)
5092 {
5093 // we slice out the top back right
5094 // part of the cube
5095 const Point<3> vertices[26] = {
5096 // front face of the big cube
5097 Point<3>(a, a, a),
5098 Point<3>((a + b) / 2, a, a),
5099 Point<3>(b, a, a),
5100 Point<3>(a, a, (a + b) / 2),
5101 Point<3>((a + b) / 2, a, (a + b) / 2),
5102 Point<3>(b, a, (a + b) / 2),
5103 Point<3>(a, a, b),
5104 Point<3>((a + b) / 2, a, b),
5105 Point<3>(b, a, b),
5106 // middle face of the big cube
5107 Point<3>(a, (a + b) / 2, a),
5108 Point<3>((a + b) / 2, (a + b) / 2, a),
5109 Point<3>(b, (a + b) / 2, a),
5110 Point<3>(a, (a + b) / 2, (a + b) / 2),
5111 Point<3>((a + b) / 2, (a + b) / 2, (a + b) / 2),
5112 Point<3>(b, (a + b) / 2, (a + b) / 2),
5113 Point<3>(a, (a + b) / 2, b),
5114 Point<3>((a + b) / 2, (a + b) / 2, b),
5115 Point<3>(b, (a + b) / 2, b),
5116 // back face of the big cube
5117 // last (top right) point is missing
5118 Point<3>(a, b, a),
5119 Point<3>((a + b) / 2, b, a),
5120 Point<3>(b, b, a),
5121 Point<3>(a, b, (a + b) / 2),
5122 Point<3>((a + b) / 2, b, (a + b) / 2),
5123 Point<3>(b, b, (a + b) / 2),
5124 Point<3>(a, b, b),
5125 Point<3>((a + b) / 2, b, b)};
5126 const int cell_vertices[7][8] = {{0, 1, 9, 10, 3, 4, 12, 13},
5127 {1, 2, 10, 11, 4, 5, 13, 14},
5128 {3, 4, 12, 13, 6, 7, 15, 16},
5129 {4, 5, 13, 14, 7, 8, 16, 17},
5130 {9, 10, 18, 19, 12, 13, 21, 22},
5131 {10, 11, 19, 20, 13, 14, 22, 23},
5132 {12, 13, 21, 22, 15, 16, 24, 25}};
5133
5134 std::vector<CellData<3>> cells(7, CellData<3>());
5135
5136 for (unsigned int i = 0; i < 7; ++i)
5137 {
5138 for (unsigned int j = 0; j < 8; ++j)
5139 cells[i].vertices[j] = cell_vertices[i][j];
5140 cells[i].material_id = 0;
5141 }
5142
5143 tria.create_triangulation(std::vector<Point<3>>(std::begin(vertices),
5144 std::end(vertices)),
5145 cells,
5146 SubCellData()); // no boundary information
5147
5148 if (colorize)
5149 {
5151 }
5152 }
5153
5154
5155
5156 void
5158 const unsigned int n_rotate_middle_square)
5159 {
5160 AssertThrow(n_rotate_middle_square < 4,
5161 ExcMessage("The number of rotation by pi/2 of the right square "
5162 "must be in the half-open range [0,4)."));
5163
5164 constexpr unsigned int dim = 2;
5165
5166 const unsigned int n_cells = 5;
5167 std::vector<CellData<dim>> cells(n_cells);
5168
5169 // Corner points of the cube [0,1]^2
5170 const std::vector<Point<dim>> vertices = {Point<dim>(0, 0), // 0
5171 Point<dim>(1, 0), // 1
5172 Point<dim>(0, 1), // 2
5173 Point<dim>(1, 1), // 3
5174 Point<dim>(2, 0), // 4
5175 Point<dim>(2, 1), // 5
5176 Point<dim>(3, 0), // 6
5177 Point<dim>(3, 1), // 7
5178 Point<dim>(1, -1), // 8
5179 Point<dim>(2, -1), // 9
5180 Point<dim>(1, 2), // 10
5181 Point<dim>(2, 2)}; // 11
5182
5183
5184 // consistent orientation
5185 unsigned int cell_vertices[n_cells][4] = {{0, 1, 2, 3},
5186 {1, 4, 3, 5}, // rotating cube
5187 {8, 9, 1, 4},
5188 {4, 6, 5, 7},
5189 {3, 5, 10, 11}};
5190
5191 switch (n_rotate_middle_square)
5192 {
5193 case /* rotate right square */ 1:
5194 {
5195 cell_vertices[1][0] = 4;
5196 cell_vertices[1][1] = 5;
5197 cell_vertices[1][2] = 1;
5198 cell_vertices[1][3] = 3;
5199 break;
5200 }
5201
5202 case /* rotate right square */ 2:
5203 {
5204 cell_vertices[1][0] = 5;
5205 cell_vertices[1][1] = 3;
5206 cell_vertices[1][2] = 4;
5207 cell_vertices[1][3] = 1;
5208 break;
5209 }
5210
5211 case /* rotate right square */ 3:
5212 {
5213 cell_vertices[1][0] = 3;
5214 cell_vertices[1][1] = 1;
5215 cell_vertices[1][2] = 5;
5216 cell_vertices[1][3] = 4;
5217 break;
5218 }
5219
5220 default /* 0 */:
5221 break;
5222 } // switch
5223
5224 cells.resize(n_cells, CellData<dim>());
5225
5226 for (unsigned int cell_index = 0; cell_index < n_cells; ++cell_index)
5227 {
5228 for (const unsigned int vertex_index :
5230 {
5231 cells[cell_index].vertices[vertex_index] =
5232 cell_vertices[cell_index][vertex_index];
5233 cells[cell_index].material_id = 0;
5234 }
5235 }
5236
5237 tria.create_triangulation(vertices, cells, SubCellData());
5238 }
5239
5240
5241 void
5243 const bool face_orientation,
5244 const bool face_flip,
5245 const bool face_rotation,
5246 const bool manipulate_left_cube)
5247 {
5248 constexpr unsigned int dim = 3;
5249
5250 const unsigned int n_cells = 2;
5251 std::vector<CellData<dim>> cells(n_cells);
5252
5253 // Corner points of the cube [0,1]^3
5254 const std::vector<Point<dim>> vertices = {Point<dim>(0, 0, 0), // 0
5255 Point<dim>(1, 0, 0), // 1
5256 Point<dim>(0, 1, 0), // 2
5257 Point<dim>(1, 1, 0), // 3
5258 Point<dim>(0, 0, 1), // 4
5259 Point<dim>(1, 0, 1), // 5
5260 Point<dim>(0, 1, 1), // 6
5261 Point<dim>(1, 1, 1), // 7
5262 Point<dim>(2, 0, 0), // 8
5263 Point<dim>(2, 1, 0), // 9
5264 Point<dim>(2, 0, 1), // 10
5265 Point<dim>(2, 1, 1)}; // 11
5266
5267 unsigned int cell_vertices[n_cells][8] = {
5268 {0, 1, 2, 3, 4, 5, 6, 7}, // unit cube
5269 {1, 8, 3, 9, 5, 10, 7, 11}}; // shifted cube
5270
5271 // binary to case number
5272 const unsigned int this_case = 4 * static_cast<int>(face_orientation) +
5273 2 * static_cast<int>(face_flip) +
5274 static_cast<int>(face_rotation);
5275
5276 if (manipulate_left_cube)
5277 {
5278 switch (this_case)
5279 {
5280 case 0:
5281 {
5282 cell_vertices[0][0] = 1;
5283 cell_vertices[0][1] = 0;
5284 cell_vertices[0][2] = 5;
5285 cell_vertices[0][3] = 4;
5286 cell_vertices[0][4] = 3;
5287 cell_vertices[0][5] = 2;
5288 cell_vertices[0][6] = 7;
5289 cell_vertices[0][7] = 6;
5290 break;
5291 }
5292
5293 case 1:
5294 {
5295 cell_vertices[0][0] = 5;
5296 cell_vertices[0][1] = 4;
5297 cell_vertices[0][2] = 7;
5298 cell_vertices[0][3] = 6;
5299 cell_vertices[0][4] = 1;
5300 cell_vertices[0][5] = 0;
5301 cell_vertices[0][6] = 3;
5302 cell_vertices[0][7] = 2;
5303 break;
5304 }
5305
5306 case 2:
5307 {
5308 cell_vertices[0][0] = 7;
5309 cell_vertices[0][1] = 6;
5310 cell_vertices[0][2] = 3;
5311 cell_vertices[0][3] = 2;
5312 cell_vertices[0][4] = 5;
5313 cell_vertices[0][5] = 4;
5314 cell_vertices[0][6] = 1;
5315 cell_vertices[0][7] = 0;
5316 break;
5317 }
5318 case 3:
5319 {
5320 cell_vertices[0][0] = 3;
5321 cell_vertices[0][1] = 2;
5322 cell_vertices[0][2] = 1;
5323 cell_vertices[0][3] = 0;
5324 cell_vertices[0][4] = 7;
5325 cell_vertices[0][5] = 6;
5326 cell_vertices[0][6] = 5;
5327 cell_vertices[0][7] = 4;
5328 break;
5329 }
5330
5331 case 4:
5332 {
5333 cell_vertices[0][0] = 0;
5334 cell_vertices[0][1] = 1;
5335 cell_vertices[0][2] = 2;
5336 cell_vertices[0][3] = 3;
5337 cell_vertices[0][4] = 4;
5338 cell_vertices[0][5] = 5;
5339 cell_vertices[0][6] = 6;
5340 cell_vertices[0][7] = 7;
5341 break;
5342 }
5343
5344 case 5:
5345 {
5346 cell_vertices[0][0] = 2;
5347 cell_vertices[0][1] = 3;
5348 cell_vertices[0][2] = 6;
5349 cell_vertices[0][3] = 7;
5350 cell_vertices[0][4] = 0;
5351 cell_vertices[0][5] = 1;
5352 cell_vertices[0][6] = 4;
5353 cell_vertices[0][7] = 5;
5354 break;
5355 }
5356
5357 case 6:
5358 {
5359 cell_vertices[0][0] = 6;
5360 cell_vertices[0][1] = 7;
5361 cell_vertices[0][2] = 4;
5362 cell_vertices[0][3] = 5;
5363 cell_vertices[0][4] = 2;
5364 cell_vertices[0][5] = 3;
5365 cell_vertices[0][6] = 0;
5366 cell_vertices[0][7] = 1;
5367 break;
5368 }
5369
5370 case 7:
5371 {
5372 cell_vertices[0][0] = 4;
5373 cell_vertices[0][1] = 5;
5374 cell_vertices[0][2] = 0;
5375 cell_vertices[0][3] = 1;
5376 cell_vertices[0][4] = 6;
5377 cell_vertices[0][5] = 7;
5378 cell_vertices[0][6] = 2;
5379 cell_vertices[0][7] = 3;
5380 break;
5381 }
5382 } // switch
5383 }
5384 else
5385 {
5386 switch (this_case)
5387 {
5388 case 0:
5389 {
5390 cell_vertices[1][0] = 8;
5391 cell_vertices[1][1] = 1;
5392 cell_vertices[1][2] = 10;
5393 cell_vertices[1][3] = 5;
5394 cell_vertices[1][4] = 9;
5395 cell_vertices[1][5] = 3;
5396 cell_vertices[1][6] = 11;
5397 cell_vertices[1][7] = 7;
5398 break;
5399 }
5400
5401 case 1:
5402 {
5403 cell_vertices[1][0] = 10;
5404 cell_vertices[1][1] = 5;
5405 cell_vertices[1][2] = 11;
5406 cell_vertices[1][3] = 7;
5407 cell_vertices[1][4] = 8;
5408 cell_vertices[1][5] = 1;
5409 cell_vertices[1][6] = 9;
5410 cell_vertices[1][7] = 3;
5411 break;
5412 }
5413
5414 case 2:
5415 {
5416 cell_vertices[1][0] = 11;
5417 cell_vertices[1][1] = 7;
5418 cell_vertices[1][2] = 9;
5419 cell_vertices[1][3] = 3;
5420 cell_vertices[1][4] = 10;
5421 cell_vertices[1][5] = 5;
5422 cell_vertices[1][6] = 8;
5423 cell_vertices[1][7] = 1;
5424 break;
5425 }
5426
5427 case 3:
5428 {
5429 cell_vertices[1][0] = 9;
5430 cell_vertices[1][1] = 3;
5431 cell_vertices[1][2] = 8;
5432 cell_vertices[1][3] = 1;
5433 cell_vertices[1][4] = 11;
5434 cell_vertices[1][5] = 7;
5435 cell_vertices[1][6] = 10;
5436 cell_vertices[1][7] = 5;
5437 break;
5438 }
5439
5440 case 4:
5441 {
5442 cell_vertices[1][0] = 1;
5443 cell_vertices[1][1] = 8;
5444 cell_vertices[1][2] = 3;
5445 cell_vertices[1][3] = 9;
5446 cell_vertices[1][4] = 5;
5447 cell_vertices[1][5] = 10;
5448 cell_vertices[1][6] = 7;
5449 cell_vertices[1][7] = 11;
5450 break;
5451 }
5452
5453 case 5:
5454 {
5455 cell_vertices[1][0] = 5;
5456 cell_vertices[1][1] = 10;
5457 cell_vertices[1][2] = 1;
5458 cell_vertices[1][3] = 8;
5459 cell_vertices[1][4] = 7;
5460 cell_vertices[1][5] = 11;
5461 cell_vertices[1][6] = 3;
5462 cell_vertices[1][7] = 9;
5463 break;
5464 }
5465
5466 case 6:
5467 {
5468 cell_vertices[1][0] = 7;
5469 cell_vertices[1][1] = 11;
5470 cell_vertices[1][2] = 5;
5471 cell_vertices[1][3] = 10;
5472 cell_vertices[1][4] = 3;
5473 cell_vertices[1][5] = 9;
5474 cell_vertices[1][6] = 1;
5475 cell_vertices[1][7] = 8;
5476 break;
5477 }
5478
5479 case 7:
5480 {
5481 cell_vertices[1][0] = 3;
5482 cell_vertices[1][1] = 9;
5483 cell_vertices[1][2] = 7;
5484 cell_vertices[1][3] = 11;
5485 cell_vertices[1][4] = 1;
5486 cell_vertices[1][5] = 8;
5487 cell_vertices[1][6] = 5;
5488 cell_vertices[1][7] = 10;
5489 break;
5490 }
5491 } // switch
5492 }
5493
5494 cells.resize(n_cells, CellData<dim>());
5495
5496 for (unsigned int cell_index = 0; cell_index < n_cells; ++cell_index)
5497 {
5498 for (const unsigned int vertex_index :
5500 {
5501 cells[cell_index].vertices[vertex_index] =
5502 cell_vertices[cell_index][vertex_index];
5503 cells[cell_index].material_id = 0;
5504 }
5505 }
5506
5507 tria.create_triangulation(vertices, cells, SubCellData());
5508 }
5509
5510
5511
5512 template <int spacedim>
5513 void
5515 const Point<spacedim> &p,
5516 const double radius)
5517 {
5518 Triangulation<spacedim> volume_mesh;
5519 GridGenerator::hyper_ball(volume_mesh, p, radius);
5520 const std::set<types::boundary_id> boundary_ids = {0};
5521 GridGenerator::extract_boundary_mesh(volume_mesh, tria, boundary_ids);
5522 tria.set_all_manifold_ids(0);
5524 }
5525
5526
5527
5528 // Implementation for 3d only
5529 template <>
5530 void
5532 const unsigned int x_subdivisions,
5533 const double radius,
5534 const double half_length)
5535 {
5536 // Copy the base from hyper_ball<3>
5537 // and transform it to yz
5538 const double d = radius / std::sqrt(2.0);
5539 const double a = d / (1 + std::sqrt(2.0));
5540
5541 std::vector<Point<3>> vertices;
5542 const double initial_height = -half_length;
5543 const double height_increment = 2. * half_length / x_subdivisions;
5544
5545 for (unsigned int rep = 0; rep < (x_subdivisions + 1); ++rep)
5546 {
5547 const double height = initial_height + height_increment * rep;
5548
5549 vertices.emplace_back(-d, height, -d);
5550 vertices.emplace_back(d, height, -d);
5551 vertices.emplace_back(-a, height, -a);
5552 vertices.emplace_back(a, height, -a);
5553 vertices.emplace_back(-a, height, a);
5554 vertices.emplace_back(a, height, a);
5555 vertices.emplace_back(-d, height, d);
5556 vertices.emplace_back(d, height, d);
5557 }
5558
5559 // Turn cylinder such that y->x
5560 for (auto &vertex : vertices)
5561 {
5562 const double h = vertex[1];
5563 vertex[1] = -vertex[0];
5564 vertex[0] = h;
5565 }
5566
5567 std::vector<std::vector<int>> cell_vertices;
5568 cell_vertices.push_back({0, 1, 8, 9, 2, 3, 10, 11});
5569 cell_vertices.push_back({0, 2, 8, 10, 6, 4, 14, 12});
5570 cell_vertices.push_back({2, 3, 10, 11, 4, 5, 12, 13});
5571 cell_vertices.push_back({1, 7, 9, 15, 3, 5, 11, 13});
5572 cell_vertices.push_back({6, 4, 14, 12, 7, 5, 15, 13});
5573
5574 for (unsigned int rep = 1; rep < x_subdivisions; ++rep)
5575 {
5576 for (unsigned int i = 0; i < 5; ++i)
5577 {
5578 std::vector<int> new_cell_vertices(8);
5579 for (unsigned int j = 0; j < 8; ++j)
5580 new_cell_vertices[j] = cell_vertices[i][j] + 8 * rep;
5581 cell_vertices.push_back(new_cell_vertices);
5582 }
5583 }
5584
5585 unsigned int n_cells = x_subdivisions * 5;
5586
5587 std::vector<CellData<3>> cells(n_cells, CellData<3>());
5588
5589 for (unsigned int i = 0; i < n_cells; ++i)
5590 {
5591 for (unsigned int j = 0; j < 8; ++j)
5592 cells[i].vertices[j] = cell_vertices[i][j];
5593 cells[i].material_id = 0;
5594 }
5595
5596 tria.create_triangulation(std::vector<Point<3>>(std::begin(vertices),
5597 std::end(vertices)),
5598 cells,
5599 SubCellData()); // no boundary information
5600
5601 // set boundary indicators for the
5602 // faces at the ends to 1 and 2,
5603 // respectively. note that we also
5604 // have to deal with those lines
5605 // that are purely in the interior
5606 // of the ends. we determine whether
5607 // an edge is purely in the
5608 // interior if one of its vertices
5609 // is at coordinates '+-a' as set
5610 // above
5612
5613 // Tolerance is calculated using the minimal length defining
5614 // the cylinder
5615 const double tolerance = 1e-5 * std::min(radius, half_length);
5616
5617 for (const auto &cell : tria.cell_iterators())
5618 for (const unsigned int i : GeometryInfo<3>::face_indices())
5619 if (cell->at_boundary(i))
5620 {
5621 if (cell->face(i)->center()[0] > half_length - tolerance)
5622 {
5623 cell->face(i)->set_boundary_id(2);
5624 cell->face(i)->set_manifold_id(numbers::flat_manifold_id);
5625
5626 for (unsigned int e = 0; e < GeometryInfo<3>::lines_per_face;
5627 ++e)
5628 if ((std::fabs(cell->face(i)->line(e)->vertex(0)[1]) == a) ||
5629 (std::fabs(cell->face(i)->line(e)->vertex(0)[2]) == a) ||
5630 (std::fabs(cell->face(i)->line(e)->vertex(1)[1]) == a) ||
5631 (std::fabs(cell->face(i)->line(e)->vertex(1)[2]) == a))
5632 {
5633 cell->face(i)->line(e)->set_boundary_id(2);
5634 cell->face(i)->line(e)->set_manifold_id(
5636 }
5637 }
5638 else if (cell->face(i)->center()[0] < -half_length + tolerance)
5639 {
5640 cell->face(i)->set_boundary_id(1);
5641 cell->face(i)->set_manifold_id(numbers::flat_manifold_id);
5642
5643 for (unsigned int e = 0; e < GeometryInfo<3>::lines_per_face;
5644 ++e)
5645 if ((std::fabs(cell->face(i)->line(e)->vertex(0)[1]) == a) ||
5646 (std::fabs(cell->face(i)->line(e)->vertex(0)[2]) == a) ||
5647 (std::fabs(cell->face(i)->line(e)->vertex(1)[1]) == a) ||
5648 (std::fabs(cell->face(i)->line(e)->vertex(1)[2]) == a))
5649 {
5650 cell->face(i)->line(e)->set_boundary_id(1);
5651 cell->face(i)->line(e)->set_manifold_id(
5653 }
5654 }
5655 }
5657 }
5658
5659 // Implementation for 3d only
5660 template <>
5661 void
5663 const double radius,
5664 const double half_length)
5665 {
5666 subdivided_cylinder(tria, 2, radius, half_length);
5667 }
5668
5669 template <>
5670 void
5672 const Point<3> &center,
5673 const double radius)
5674 {
5675 const unsigned int dim = 3;
5676
5677 // the parameters a (intersection on the octant lines from center), b
5678 // (intersection within the octant faces) and c (position inside the
5679 // octant) have been derived by equilibrating the minimal singular value
5680 // of the Jacobian of the four cells around the center point c and, as a
5681 // secondary measure, to minimize the aspect ratios defined as the maximal
5682 // divided by the minimal singular values throughout cells
5683 const double a = 0.528;
5684 const double b = 0.4533;
5685 const double c = 0.3752;
5686 const Point<dim> vertices[15] = {
5687 center + Point<dim>(0, 0, 0) * radius,
5688 center + Point<dim>(+1, 0, 0) * radius,
5689 center + Point<dim>(+1, 0, 0) * (radius * a),
5690 center + Point<dim>(0, +1, 0) * (radius * a),
5691 center + Point<dim>(+1, +1, 0) * (radius * b),
5692 center + Point<dim>(0, +1, 0) * radius,
5693 center + Point<dim>(+1, +1, 0) * radius / std::sqrt(2.0),
5694 center + Point<dim>(0, 0, 1) * radius * a,
5695 center + Point<dim>(+1, 0, 1) * radius / std::sqrt(2.0),
5696 center + Point<dim>(+1, 0, 1) * (radius * b),
5697 center + Point<dim>(0, +1, 1) * (radius * b),
5698 center + Point<dim>(+1, +1, 1) * (radius * c),
5699 center + Point<dim>(0, +1, 1) * radius / std::sqrt(2.0),
5700 center + Point<dim>(+1, +1, 1) * (radius / (std::sqrt(3.0))),
5701 center + Point<dim>(0, 0, 1) * radius};
5702 const int cell_vertices[4][8] = {{0, 2, 3, 4, 7, 9, 10, 11},
5703 {1, 6, 2, 4, 8, 13, 9, 11},
5704 {5, 3, 6, 4, 12, 10, 13, 11},
5705 {7, 9, 10, 11, 14, 8, 12, 13}};
5706
5707 std::vector<CellData<dim>> cells(4, CellData<dim>());
5708
5709 for (unsigned int i = 0; i < 4; ++i)
5710 {
5711 for (unsigned int j = 0; j < 8; ++j)
5712 cells[i].vertices[j] = cell_vertices[i][j];
5713 cells[i].material_id = 0;
5714 }
5715
5716 tria.create_triangulation(std::vector<Point<dim>>(std::begin(vertices),
5717 std::end(vertices)),
5718 cells,
5719 SubCellData()); // no boundary information
5720
5723
5725 while (cell != end)
5726 {
5727 for (const unsigned int i : GeometryInfo<dim>::face_indices())
5728 {
5729 if (cell->face(i)->boundary_id() ==
5731 continue;
5732
5733 // If x,y or z is zero, then this is part of the plane
5734 if (cell->face(i)->center()[0] < center[0] + 1.e-5 * radius ||
5735 cell->face(i)->center()[1] < center[1] + 1.e-5 * radius ||
5736 cell->face(i)->center()[2] < center[2] + 1.e-5 * radius)
5737 {
5738 cell->face(i)->set_boundary_id(1);
5739 cell->face(i)->set_manifold_id(numbers::flat_manifold_id);
5740 // also set the boundary indicators of the bounding lines,
5741 // unless both vertices are on the perimeter
5742 for (unsigned int j = 0; j < GeometryInfo<3>::lines_per_face;
5743 ++j)
5744 {
5745 const Point<3> line_vertices[2] = {
5746 cell->face(i)->line(j)->vertex(0),
5747 cell->face(i)->line(j)->vertex(1)};
5748 if ((std::fabs(line_vertices[0].distance(center) - radius) >
5749 1e-5 * radius) ||
5750 (std::fabs(line_vertices[1].distance(center) - radius) >
5751 1e-5 * radius))
5752 {
5753 cell->face(i)->line(j)->set_boundary_id(1);
5754 cell->face(i)->line(j)->set_manifold_id(
5756 }
5757 }
5758 }
5759 }
5760 ++cell;
5761 }
5762 tria.set_manifold(0, SphericalManifold<3>(center));
5763 }
5764
5765
5766
5767 // Implementation for 3d only
5768 template <>
5769 void
5771 const Point<3> &center,
5772 const double radius)
5773 {
5774 // These are for the two lower squares
5775 const double d = radius / std::sqrt(2.0);
5776 const double a = d / (1 + std::sqrt(2.0));
5777 // These are for the two upper square
5778 const double b = a / 2.0;
5779 const double c = d / 2.0;
5780 // And so are these
5781 const double hb = radius * std::sqrt(3.0) / 4.0;
5782 const double hc = radius * std::sqrt(3.0) / 2.0;
5783
5784 Point<3> vertices[16] = {
5785 center + Point<3>(0, d, -d),
5786 center + Point<3>(0, -d, -d),
5787 center + Point<3>(0, a, -a),
5788 center + Point<3>(0, -a, -a),
5789 center + Point<3>(0, a, a),
5790 center + Point<3>(0, -a, a),
5791 center + Point<3>(0, d, d),
5792 center + Point<3>(0, -d, d),
5793
5794 center + Point<3>(hc, c, -c),
5795 center + Point<3>(hc, -c, -c),
5796 center + Point<3>(hb, b, -b),
5797 center + Point<3>(hb, -b, -b),
5798 center + Point<3>(hb, b, b),
5799 center + Point<3>(hb, -b, b),
5800 center + Point<3>(hc, c, c),
5801 center + Point<3>(hc, -c, c),
5802 };
5803
5804 int cell_vertices[6][8] = {{0, 1, 8, 9, 2, 3, 10, 11},
5805 {0, 2, 8, 10, 6, 4, 14, 12},
5806 {2, 3, 10, 11, 4, 5, 12, 13},
5807 {1, 7, 9, 15, 3, 5, 11, 13},
5808 {6, 4, 14, 12, 7, 5, 15, 13},
5809 {8, 10, 9, 11, 14, 12, 15, 13}};
5810
5811 std::vector<CellData<3>> cells(6, CellData<3>());
5812
5813 for (unsigned int i = 0; i < 6; ++i)
5814 {
5815 for (unsigned int j = 0; j < 8; ++j)
5816 cells[i].vertices[j] = cell_vertices[i][j];
5817 cells[i].material_id = 0;
5818 }
5819
5820 tria.create_triangulation(std::vector<Point<3>>(std::begin(vertices),
5821 std::end(vertices)),
5822 cells,
5823 SubCellData()); // no boundary information
5824
5827
5829
5830 // go over all faces. for the ones on the flat face, set boundary
5831 // indicator for face and edges to one; the rest will remain at
5832 // zero but we have to pay attention to those edges that are
5833 // at the perimeter of the flat face since they should not be
5834 // set to one
5835 while (cell != end)
5836 {
5837 for (const unsigned int i : GeometryInfo<3>::face_indices())
5838 {
5839 if (!cell->at_boundary(i))
5840 continue;
5841
5842 // If the center is on the plane x=0, this is a planar element. set
5843 // its boundary indicator. also set the boundary indicators of the
5844 // bounding faces unless both vertices are on the perimeter
5845 if (cell->face(i)->center()[0] < center[0] + 1.e-5 * radius)
5846 {
5847 cell->face(i)->set_boundary_id(1);
5848 cell->face(i)->set_manifold_id(numbers::flat_manifold_id);
5849 for (unsigned int j = 0; j < GeometryInfo<3>::lines_per_face;
5850 ++j)
5851 {
5852 const Point<3> line_vertices[2] = {
5853 cell->face(i)->line(j)->vertex(0),
5854 cell->face(i)->line(j)->vertex(1)};
5855 if ((std::fabs(line_vertices[0].distance(center) - radius) >
5856 1e-5 * radius) ||
5857 (std::fabs(line_vertices[1].distance(center) - radius) >
5858 1e-5 * radius))
5859 {
5860 cell->face(i)->line(j)->set_boundary_id(1);
5861 cell->face(i)->line(j)->set_manifold_id(
5863 }
5864 }
5865 }
5866 }
5867 ++cell;
5868 }
5869 tria.set_manifold(0, SphericalManifold<3>(center));
5870 }
5871
5872
5873
5874 template <int dim>
5875 void
5877 const Point<dim> &p,
5878 const double radius)
5879 {
5880 // We create the ball by duplicating the information in each dimension at
5881 // a time by appropriate rotations, starting from the quarter ball. The
5882 // rotations make sure we do not generate inverted cells that would appear
5883 // if we tried the slightly simpler approach to simply mirror the cells.
5884 //
5885 // Make the rotations easy by centering at the origin now and shifting by p
5886 // later.
5887
5888 Triangulation<dim> tria_piece;
5889 GridGenerator::quarter_hyper_ball(tria_piece, Point<dim>(), radius);
5890
5891 for (unsigned int round = 0; round < dim; ++round)
5892 {
5893 Triangulation<dim> tria_copy;
5894 tria_copy.copy_triangulation(tria_piece);
5895 tria_piece.clear();
5896 std::vector<Point<dim>> new_points(tria_copy.n_vertices());
5897 if (round == 0)
5898 for (unsigned int v = 0; v < tria_copy.n_vertices(); ++v)
5899 {
5900 // rotate by 90 degrees counterclockwise
5901 new_points[v][0] = -tria_copy.get_vertices()[v][1];
5902 new_points[v][1] = tria_copy.get_vertices()[v][0];
5903 if (dim == 3)
5904 new_points[v][2] = tria_copy.get_vertices()[v][2];
5905 }
5906 else if (round == 1)
5907 {
5908 for (unsigned int v = 0; v < tria_copy.n_vertices(); ++v)
5909 {
5910 // rotate by 180 degrees along the xy plane
5911 new_points[v][0] = -tria_copy.get_vertices()[v][0];
5912 new_points[v][1] = -tria_copy.get_vertices()[v][1];
5913 if (dim == 3)
5914 new_points[v][2] = tria_copy.get_vertices()[v][2];
5915 }
5916 }
5917 else if (round == 2)
5918 for (unsigned int v = 0; v < tria_copy.n_vertices(); ++v)
5919 {
5920 // rotate by 180 degrees along the xz plane
5921 Assert(dim == 3, ExcInternalError());
5922 new_points[v][0] = -tria_copy.get_vertices()[v][0];
5923 new_points[v][1] = tria_copy.get_vertices()[v][1];
5924 new_points[v][2] = -tria_copy.get_vertices()[v][2];
5925 }
5926 else
5928
5929
5930 // the cell data is exactly the same as before
5931 std::vector<CellData<dim>> cells;
5932 cells.reserve(tria_copy.n_cells());
5933 for (const auto &cell : tria_copy.cell_iterators())
5934 {
5936 for (const unsigned int v : GeometryInfo<dim>::vertex_indices())
5937 data.vertices[v] = cell->vertex_index(v);
5938 data.material_id = cell->material_id();
5939 data.manifold_id = cell->manifold_id();
5940 cells.push_back(data);
5941 }
5942
5943 Triangulation<dim> rotated_tria;
5944 rotated_tria.create_triangulation(new_points, cells, SubCellData());
5945
5946 // merge the triangulations - this will make sure that the duplicate
5947 // vertices in the interior are absorbed
5948 if (round == dim - 1)
5949 merge_triangulations(tria_copy, rotated_tria, tria, 1e-12 * radius);
5950 else
5951 merge_triangulations(tria_copy,
5952 rotated_tria,
5953 tria_piece,
5954 1e-12 * radius);
5955 }
5956
5957 for (const auto &cell : tria.cell_iterators())
5958 if (cell->center().norm_square() > 0.4 * radius)
5959 cell->set_manifold_id(1);
5960 else
5961 cell->set_all_manifold_ids(numbers::flat_manifold_id);
5962 GridTools::shift(p, tria);
5963
5965
5968 }
5969
5970 // To work around an internal clang-13 error we need to split up the
5971 // individual hyper shell functions. This has the added bonus of making the
5972 // control flow easier to follow - some hyper shell functions call others.
5973 namespace internal
5974 {
5975 namespace
5976 {
5977 void
5978 hyper_shell_6(Triangulation<3> &tria,
5979 const Point<3> &p,
5980 const double inner_radius,
5981 const double outer_radius)
5982 {
5983 std::vector<Point<3>> vertices;
5984 std::vector<CellData<3>> cells;
5985
5986 const double irad = inner_radius / std::sqrt(3.0);
5987 const double orad = outer_radius / std::sqrt(3.0);
5988
5989 // Corner points of the cube [-1,1]^3
5990 static const std::array<Point<3>, 8> hexahedron = {{{-1, -1, -1}, //
5991 {+1, -1, -1}, //
5992 {-1, +1, -1}, //
5993 {+1, +1, -1}, //
5994 {-1, -1, +1}, //
5995 {+1, -1, +1}, //
5996 {-1, +1, +1}, //
5997 {+1, +1, +1}}};
5998
5999 // Start with the shell bounded by two nested cubes
6000 vertices.reserve(8);
6001 for (unsigned int i = 0; i < 8; ++i)
6002 vertices.push_back(p + hexahedron[i] * irad);
6003 for (unsigned int i = 0; i < 8; ++i)
6004 vertices.push_back(p + hexahedron[i] * orad);
6005
6006 const unsigned int n_cells = 6;
6007 const int cell_vertices[n_cells][8] = {
6008 {8, 9, 10, 11, 0, 1, 2, 3}, // bottom
6009 {9, 11, 1, 3, 13, 15, 5, 7}, // right
6010 {12, 13, 4, 5, 14, 15, 6, 7}, // top
6011 {8, 0, 10, 2, 12, 4, 14, 6}, // left
6012 {8, 9, 0, 1, 12, 13, 4, 5}, // front
6013 {10, 2, 11, 3, 14, 6, 15, 7}}; // back
6014
6015 cells.resize(n_cells, CellData<3>());
6016
6017 for (unsigned int i = 0; i < n_cells; ++i)
6018 {
6019 for (const unsigned int j : GeometryInfo<3>::vertex_indices())
6020 cells[i].vertices[j] = cell_vertices[i][j];
6021 cells[i].material_id = 0;
6022 }
6023
6024 tria.create_triangulation(vertices, cells, SubCellData());
6025 tria.set_all_manifold_ids(0);
6027 }
6028
6029 void
6030 hyper_shell_12(Triangulation<3> &tria,
6031 const Point<3> &p,
6032 const double inner_radius,
6033 const double outer_radius)
6034 {
6035 std::vector<Point<3>> vertices;
6036 std::vector<CellData<3>> cells;
6037
6038 const double irad = inner_radius / std::sqrt(3.0);
6039 const double orad = outer_radius / std::sqrt(3.0);
6040
6041 // A more regular subdivision can be obtained by two nested rhombic
6042 // dodecahedra
6043 //
6044 // Octahedron inscribed in the cube [-1,1]^3
6045 static const std::array<Point<3>, 6> octahedron = {{{-1, 0, 0}, //
6046 {1, 0, 0}, //
6047 {0, -1, 0}, //
6048 {0, 1, 0}, //
6049 {0, 0, -1}, //
6050 {0, 0, 1}}};
6051
6052 // Corner points of the cube [-1,1]^3
6053 static const std::array<Point<3>, 8> hexahedron = {{{-1, -1, -1}, //
6054 {+1, -1, -1}, //
6055 {-1, +1, -1}, //
6056 {+1, +1, -1}, //
6057 {-1, -1, +1}, //
6058 {+1, -1, +1}, //
6059 {-1, +1, +1}, //
6060 {+1, +1, +1}}};
6061
6062 vertices.reserve(8);
6063 for (unsigned int i = 0; i < 8; ++i)
6064 vertices.push_back(p + hexahedron[i] * irad);
6065 for (unsigned int i = 0; i < 6; ++i)
6066 vertices.push_back(p + octahedron[i] * inner_radius);
6067 for (unsigned int i = 0; i < 8; ++i)
6068 vertices.push_back(p + hexahedron[i] * orad);
6069 for (unsigned int i = 0; i < 6; ++i)
6070 vertices.push_back(p + octahedron[i] * outer_radius);
6071
6072 const unsigned int n_cells = 12;
6073 const unsigned int rhombi[n_cells][4] = {{10, 4, 0, 8},
6074 {4, 13, 8, 6},
6075 {10, 5, 4, 13},
6076 {1, 9, 10, 5},
6077 {9, 7, 5, 13},
6078 {7, 11, 13, 6},
6079 {9, 3, 7, 11},
6080 {1, 12, 9, 3},
6081 {12, 2, 3, 11},
6082 {2, 8, 11, 6},
6083 {12, 0, 2, 8},
6084 {1, 10, 12, 0}};
6085
6086 cells.resize(n_cells, CellData<3>());
6087
6088 for (unsigned int i = 0; i < n_cells; ++i)
6089 {
6090 for (unsigned int j = 0; j < 4; ++j)
6091 {
6092 cells[i].vertices[j] = rhombi[i][j];
6093 cells[i].vertices[j + 4] = rhombi[i][j] + 14;
6094 }
6095 cells[i].material_id = 0;
6096 }
6097
6098 tria.create_triangulation(vertices, cells, SubCellData());
6099 tria.set_all_manifold_ids(0);
6101 }
6102
6103 void
6104 hyper_shell_24_48(Triangulation<3> &tria,
6105 const unsigned int n,
6106 const unsigned int n_refinement_steps,
6107 const Point<3> &p,
6108 const double inner_radius,
6109 const double outer_radius)
6110 {
6111 // These two meshes are created by first creating a mesh of the
6112 // 6-cell/12-cell version, refining globally, and removing the outer
6113 // half of the cells. For 192 and more cells, we do this iteratively
6114 // several times, always refining and removing the outer half. Thus, the
6115 // outer radius for the start is larger and set as 2^n_refinement_steps
6116 // such that it exactly gives the desired radius in the end. It would
6117 // have been slightly less code to treat refinement steps recursively
6118 // for 192 cells or beyond, but unfortunately we could end up with the
6119 // 96 cell case which is not what we want. Thus, we need to implement a
6120 // loop manually here.
6121 Triangulation<3> tmp;
6122 const unsigned int outer_radius_factor = 1 << n_refinement_steps;
6123 if (n == 24)
6124 hyper_shell_6(tmp,
6125 p,
6126 inner_radius,
6127 outer_radius_factor * outer_radius -
6128 (outer_radius_factor - 1) * inner_radius);
6129 else if (n == 48)
6130 hyper_shell_12(tmp,
6131 p,
6132 inner_radius,
6133 outer_radius_factor * outer_radius -
6134 (outer_radius_factor - 1) * inner_radius);
6135 else
6136 Assert(n == 24 || n == 48, ExcInternalError());
6137 for (unsigned int r = 0; r < n_refinement_steps; ++r)
6138 {
6139 tmp.refine_global(1);
6140 std::set<Triangulation<3>::active_cell_iterator> cells_to_remove;
6141
6142 // We remove all cells which do not have exactly four vertices
6143 // at the inner radius (plus some tolerance).
6144 for (const auto &cell : tmp.active_cell_iterators())
6145 {
6146 unsigned int n_vertices_inside = 0;
6147 for (const auto v : GeometryInfo<3>::vertex_indices())
6148 if ((cell->vertex(v) - p).norm_square() <
6149 inner_radius * inner_radius * (1 + 1e-12))
6150 ++n_vertices_inside;
6151 if (n_vertices_inside < 4)
6152 cells_to_remove.insert(cell);
6153 }
6154
6155 AssertDimension(cells_to_remove.size(), tmp.n_active_cells() / 2);
6156 if (r == n_refinement_steps - 1)
6158 cells_to_remove,
6159 tria);
6160 else
6161 {
6164 cells_to_remove,
6165 copy);
6166 tmp = std::move(copy);
6167 tmp.set_all_manifold_ids(0);
6169 }
6170 }
6171 tria.set_all_manifold_ids(0);
6173 }
6174
6175 } // namespace
6176 } // namespace internal
6177
6178
6179
6180 template <>
6181 void
6183 const Point<3> &p,
6184 const double inner_radius,
6185 const double outer_radius,
6186 const unsigned int n_cells,
6187 const bool colorize)
6188 {
6189 Assert((inner_radius > 0) && (inner_radius < outer_radius),
6190 ExcInvalidRadii());
6191
6192 unsigned int n_refinement_steps = 0;
6193 unsigned int n_cells_coarsened = n_cells;
6194 if (n_cells != 96 && n_cells > 12)
6195 while (n_cells_coarsened > 12 && n_cells_coarsened % 4 == 0)
6196 {
6197 ++n_refinement_steps;
6198 n_cells_coarsened /= 4;
6199 }
6200 Assert(n_cells == 0 || n_cells == 6 || n_cells == 12 || n_cells == 96 ||
6201 (n_refinement_steps > 0 &&
6202 (n_cells_coarsened == 6 || n_cells_coarsened == 12)),
6203 ExcMessage("Invalid number of coarse mesh cells"));
6204
6205 const unsigned int n = n_refinement_steps > 0 ?
6206 4 * n_cells_coarsened :
6207 ((n_cells == 0) ? 6 : n_cells);
6208
6209 switch (n)
6210 {
6211 case 6:
6212 internal::hyper_shell_6(tria, p, inner_radius, outer_radius);
6213 break;
6214 case 12:
6215 internal::hyper_shell_12(tria, p, inner_radius, outer_radius);
6216 break;
6217 case 24:
6218 case 48:
6219 internal::hyper_shell_24_48(
6220 tria, n, n_refinement_steps, p, inner_radius, outer_radius);
6221 break;
6222 case 96:
6223 {
6224 // create a triangulation based on the 12-cell version. This
6225 // function was needed before SphericalManifold was written: it
6226 // manually adjusted the interior vertices to lie along concentric
6227 // spheres. Nowadays we can just refine globally:
6228 Triangulation<3> tmp;
6229 internal::hyper_shell_12(tmp, p, inner_radius, outer_radius);
6230 tmp.refine_global(1);
6231 flatten_triangulation(tmp, tria);
6232 tria.set_all_manifold_ids(0);
6234 break;
6235 }
6236 default:
6237 {
6238 Assert(false, ExcMessage("Invalid number of coarse mesh cells."));
6239 }
6240 }
6241
6242 if (n_cells > 0)
6243 AssertDimension(tria.n_global_active_cells(), n_cells);
6244
6245 if (colorize)
6246 colorize_hyper_shell(tria, p, inner_radius, outer_radius);
6247 }
6248
6249
6250
6251 // Implementation for 3d only
6252 template <>
6253 void
6255 const Point<3> &center,
6256 const double inner_radius,
6257 const double outer_radius,
6258 const unsigned int /*n_cells*/,
6259 const bool colorize)
6260 {
6261 Assert((inner_radius > 0) && (inner_radius < outer_radius),
6262 ExcInvalidRadii());
6263
6264 // These are for the two lower squares
6265 const double d = outer_radius / std::sqrt(2.0);
6266 const double a = inner_radius / std::sqrt(2.0);
6267 // These are for the two upper square
6268 const double b = a / 2.0;
6269 const double c = d / 2.0;
6270 // And so are these
6271 const double hb = inner_radius * std::sqrt(3.0) / 2.0;
6272 const double hc = outer_radius * std::sqrt(3.0) / 2.0;
6273
6274 Point<3> vertices[16] = {
6275 center + Point<3>(0, d, -d),
6276 center + Point<3>(0, -d, -d),
6277 center + Point<3>(0, a, -a),
6278 center + Point<3>(0, -a, -a),
6279 center + Point<3>(0, a, a),
6280 center + Point<3>(0, -a, a),
6281 center + Point<3>(0, d, d),
6282 center + Point<3>(0, -d, d),
6283
6284 center + Point<3>(hc, c, -c),
6285 center + Point<3>(hc, -c, -c),
6286 center + Point<3>(hb, b, -b),
6287 center + Point<3>(hb, -b, -b),
6288 center + Point<3>(hb, b, b),
6289 center + Point<3>(hb, -b, b),
6290 center + Point<3>(hc, c, c),
6291 center + Point<3>(hc, -c, c),
6292 };
6293
6294 int cell_vertices[5][8] = {{0, 1, 8, 9, 2, 3, 10, 11},
6295 {0, 2, 8, 10, 6, 4, 14, 12},
6296 {1, 7, 9, 15, 3, 5, 11, 13},
6297 {6, 4, 14, 12, 7, 5, 15, 13},
6298 {8, 10, 9, 11, 14, 12, 15, 13}};
6299
6300 std::vector<CellData<3>> cells(5, CellData<3>());
6301
6302 for (unsigned int i = 0; i < 5; ++i)
6303 {
6304 for (unsigned int j = 0; j < 8; ++j)
6305 cells[i].vertices[j] = cell_vertices[i][j];
6306 cells[i].material_id = 0;
6307 }
6308
6309 tria.create_triangulation(std::vector<Point<3>>(std::begin(vertices),
6310 std::end(vertices)),
6311 cells,
6312 SubCellData()); // no boundary information
6313
6314 if (colorize)
6315 {
6316 // We want to use a standard boundary description where
6317 // the boundary is not curved. Hence set boundary id 2 to
6318 // to all faces in a first step.
6320 for (; cell != tria.end(); ++cell)
6321 for (const unsigned int i : GeometryInfo<3>::face_indices())
6322 if (cell->at_boundary(i))
6323 cell->face(i)->set_all_boundary_ids(2);
6324
6325 // Next look for the curved boundaries. If the x value of the
6326 // center of the face is not equal to center(0), we're on a curved
6327 // boundary. Then decide whether the center is nearer to the inner
6328 // or outer boundary to set the correct boundary id.
6329 for (cell = tria.begin(); cell != tria.end(); ++cell)
6330 for (const unsigned int i : GeometryInfo<3>::face_indices())
6331 if (cell->at_boundary(i))
6332 {
6333 const Triangulation<3>::face_iterator face = cell->face(i);
6334
6335 const Point<3> face_center(face->center());
6336 if (std::abs(face_center[0] - center[0]) >
6337 1.e-6 * face_center.norm())
6338 {
6339 if (std::abs((face_center - center).norm() - inner_radius) <
6340 std::abs((face_center - center).norm() - outer_radius))
6341 face->set_all_boundary_ids(0);
6342 else
6343 face->set_all_boundary_ids(1);
6344 }
6345 }
6346 }
6347 tria.set_all_manifold_ids(0);
6348 tria.set_manifold(0, SphericalManifold<3>(center));
6349 }
6350
6351
6352 // Implementation for 3d only
6353 template <>
6354 void
6356 const Point<3> &center,
6357 const double inner_radius,
6358 const double outer_radius,
6359 const unsigned int n,
6360 const bool colorize)
6361 {
6362 Assert((inner_radius > 0) && (inner_radius < outer_radius),
6363 ExcInvalidRadii());
6364 if (n == 0 || n == 3)
6365 {
6366 const double a = inner_radius * std::sqrt(2.0) / 2e0;
6367 const double b = outer_radius * std::sqrt(2.0) / 2e0;
6368 const double c = a * std::sqrt(3.0) / 2e0;
6369 const double d = b * std::sqrt(3.0) / 2e0;
6370 const double e = outer_radius / 2e0;
6371 const double h = inner_radius / 2e0;
6372
6373 std::vector<Point<3>> vertices;
6374
6375 vertices.push_back(center + Point<3>(0, inner_radius, 0)); // 0
6376 vertices.push_back(center + Point<3>(a, a, 0)); // 1
6377 vertices.push_back(center + Point<3>(b, b, 0)); // 2
6378 vertices.push_back(center + Point<3>(0, outer_radius, 0)); // 3
6379 vertices.push_back(center + Point<3>(0, a, a)); // 4
6380 vertices.push_back(center + Point<3>(c, c, h)); // 5
6381 vertices.push_back(center + Point<3>(d, d, e)); // 6
6382 vertices.push_back(center + Point<3>(0, b, b)); // 7
6383 vertices.push_back(center + Point<3>(inner_radius, 0, 0)); // 8
6384 vertices.push_back(center + Point<3>(outer_radius, 0, 0)); // 9
6385 vertices.push_back(center + Point<3>(a, 0, a)); // 10
6386 vertices.push_back(center + Point<3>(b, 0, b)); // 11
6387 vertices.push_back(center + Point<3>(0, 0, inner_radius)); // 12
6388 vertices.push_back(center + Point<3>(0, 0, outer_radius)); // 13
6389
6390 const int cell_vertices[3][8] = {
6391 {0, 1, 3, 2, 4, 5, 7, 6},
6392 {1, 8, 2, 9, 5, 10, 6, 11},
6393 {4, 5, 7, 6, 12, 10, 13, 11},
6394 };
6395 std::vector<CellData<3>> cells(3);
6396
6397 for (unsigned int i = 0; i < 3; ++i)
6398 {
6399 for (unsigned int j = 0; j < 8; ++j)
6400 cells[i].vertices[j] = cell_vertices[i][j];
6401 cells[i].material_id = 0;
6402 }
6403
6404 tria.create_triangulation(vertices,
6405 cells,
6406 SubCellData()); // no boundary information
6407 }
6408 else
6409 {
6411 }
6412
6413 if (colorize)
6414 colorize_quarter_hyper_shell(tria, center, inner_radius, outer_radius);
6415
6416 tria.set_all_manifold_ids(0);
6417 tria.set_manifold(0, SphericalManifold<3>(center));
6418 }
6419
6420
6421 // Implementation for 3d only
6422 template <>
6423 void
6425 const double length,
6426 const double inner_radius,
6427 const double outer_radius,
6428 const unsigned int n_radial_cells,
6429 const unsigned int n_axial_cells,
6430 const bool colorize)
6431 {
6432 Assert((inner_radius > 0) && (inner_radius < outer_radius),
6433 ExcInvalidRadii());
6434
6435 const double pi = numbers::PI;
6436
6437 // determine the number of cells
6438 // for the grid. if not provided by
6439 // the user determine it such that
6440 // the length of each cell on the
6441 // median (in the middle between
6442 // the two circles) is equal to its
6443 // radial extent (which is the
6444 // difference between the two
6445 // radii)
6446 const unsigned int N_r =
6447 (n_radial_cells == 0 ? static_cast<unsigned int>(std::ceil(
6448 (2 * pi * (outer_radius + inner_radius) / 2) /
6449 (outer_radius - inner_radius))) :
6450 n_radial_cells);
6451 const unsigned int N_z =
6452 (n_axial_cells == 0 ?
6453 static_cast<unsigned int>(std::ceil(
6454 length / (2 * pi * (outer_radius + inner_radius) / 2 / N_r))) :
6455 n_axial_cells);
6456
6457 // set up N vertices on the
6458 // outer and N vertices on
6459 // the inner circle. the
6460 // first N ones are on the
6461 // outer one, and all are
6462 // numbered counter-clockwise
6463 std::vector<Point<2>> vertices_2d(2 * N_r);
6464 for (unsigned int i = 0; i < N_r; ++i)
6465 {
6466 vertices_2d[i] =
6467 Point<2>(std::cos(2 * pi * i / N_r), std::sin(2 * pi * i / N_r)) *
6468 outer_radius;
6469 vertices_2d[i + N_r] = vertices_2d[i] * (inner_radius / outer_radius);
6470 }
6471
6472 std::vector<Point<3>> vertices_3d;
6473 vertices_3d.reserve(2 * N_r * (N_z + 1));
6474 for (unsigned int j = 0; j <= N_z; ++j)
6475 for (unsigned int i = 0; i < 2 * N_r; ++i)
6476 {
6477 const Point<3> v(vertices_2d[i][0],
6478 vertices_2d[i][1],
6479 j * length / N_z);
6480 vertices_3d.push_back(v);
6481 }
6482
6483 std::vector<CellData<3>> cells(N_r * N_z, CellData<3>());
6484
6485 for (unsigned int j = 0; j < N_z; ++j)
6486 for (unsigned int i = 0; i < N_r; ++i)
6487 {
6488 cells[i + j * N_r].vertices[0] = i + (j + 1) * 2 * N_r;
6489 cells[i + j * N_r].vertices[1] = (i + 1) % N_r + (j + 1) * 2 * N_r;
6490 cells[i + j * N_r].vertices[2] = i + j * 2 * N_r;
6491 cells[i + j * N_r].vertices[3] = (i + 1) % N_r + j * 2 * N_r;
6492
6493 cells[i + j * N_r].vertices[4] = N_r + i + (j + 1) * 2 * N_r;
6494 cells[i + j * N_r].vertices[5] =
6495 N_r + ((i + 1) % N_r) + (j + 1) * 2 * N_r;
6496 cells[i + j * N_r].vertices[6] = N_r + i + j * 2 * N_r;
6497 cells[i + j * N_r].vertices[7] = N_r + ((i + 1) % N_r) + j * 2 * N_r;
6498
6499 cells[i + j * N_r].material_id = 0;
6500 }
6501
6502 tria.create_triangulation(vertices_3d, cells, SubCellData());
6503 tria.set_all_manifold_ids(0);
6505
6506 if (!colorize)
6507 return;
6508
6509 // If colorize, set boundary id on the triangualtion.
6510 // Inner cylinder has boundary id 0
6511 // Outer cylinder has boundary id 1
6512 // Bottom (Z-) part of the cylinder has boundary id 2
6513 // Top (Z+) part of the cylinder has boundary id 3
6514
6515 // Define tolerance to help detect boundary conditions
6516 // First we define the tolerance along the z axis to identify
6517 // bottom and top cells.
6518 double eps_z = 1e-6 * length;
6519
6520 // Gather the inner radius from the faces instead of the argument, this is
6521 // more robust for some aspect ratios. First initialize the outer to 0 and
6522 // the inner to a large value
6523 double face_inner_radius = std::numeric_limits<double>::max();
6524 double face_outer_radius = 0.;
6525
6526 // Loop over the cells once to acquire the min and max radius at the face
6527 // centers. Otherwise, for some cell ratio, the center of the faces can be
6528 // at a radius which is significantly different from the one prescribed.
6529 for (const auto &cell : tria.active_cell_iterators())
6530 for (const unsigned int f : GeometryInfo<3>::face_indices())
6531 {
6532 if (!cell->face(f)->at_boundary())
6533 continue;
6534
6535 const auto face_center = cell->face(f)->center();
6536 const double z = face_center[2];
6537
6538 if ((std::fabs(z) > eps_z) &&
6539 (std::fabs(z - length) > eps_z)) // Not a zmin or zmax boundary
6540 {
6541 const double radius = std::sqrt(face_center[0] * face_center[0] +
6542 face_center[1] * face_center[1]);
6543 face_inner_radius = std::min(face_inner_radius, radius);
6544 face_outer_radius = std::max(face_outer_radius, radius);
6545 }
6546 }
6547
6548 double mid_radial_distance = 0.5 * (face_outer_radius - face_inner_radius);
6549
6550 for (const auto &cell : tria.active_cell_iterators())
6551 for (const unsigned int f : GeometryInfo<3>::face_indices())
6552 {
6553 if (cell->face(f)->at_boundary())
6554 {
6555 const auto face_center = cell->face(f)->center();
6556
6557 const double radius = std::sqrt(face_center[0] * face_center[0] +
6558 face_center[1] * face_center[1]);
6559
6560 const double z = face_center[2];
6561
6562 if (std::fabs(z) < eps_z) // z = 0 set boundary 2
6563 {
6564 cell->face(f)->set_boundary_id(2);
6565 }
6566 else if (std::fabs(z - length) <
6567 eps_z) // z = length set boundary 3
6568 {
6569 cell->face(f)->set_boundary_id(3);
6570 }
6571 else if (std::fabs(radius - face_inner_radius) >
6572 mid_radial_distance) // r = outer_radius set boundary 1
6573 {
6574 cell->face(f)->set_boundary_id(1);
6575 }
6576 else if (std::fabs(radius - face_inner_radius) <
6577 mid_radial_distance) // r = inner_radius set boundary 0
6578 {
6579 cell->face(f)->set_boundary_id(0);
6580 }
6581 else
6583 }
6584 }
6585 }
6586
6587
6588 template <int dim, int spacedim>
6589 void
6591 const std::vector<const Triangulation<dim, spacedim> *> &triangulations,
6593 const double duplicated_vertex_tolerance,
6594 const bool copy_manifold_ids,
6595 const bool copy_boundary_ids)
6596 {
6597 std::vector<Point<spacedim>> vertices;
6598 std::vector<CellData<dim>> cells;
6599 SubCellData subcell_data;
6600
6601 unsigned int n_accumulated_vertices = 0;
6602 for (const auto triangulation : triangulations)
6603 {
6604 Assert(triangulation->n_levels() == 1,
6605 ExcMessage("The input triangulations must be non-empty "
6606 "and must not be refined."));
6607
6608 auto [tria_vertices, tria_cells, tria_subcell_data] =
6610 Assert(tria_vertices.size() == triangulation->n_vertices(),
6612 Assert(tria_cells.size() == triangulation->n_cells(),
6614
6615 // Copy the vertices of the current triangulation into the merged list,
6616 // and then let the vertex indices of the cells refer to those in
6617 // the merged list:
6618 vertices.insert(vertices.end(),
6619 tria_vertices.begin(),
6620 tria_vertices.end());
6621 for (CellData<dim> &cell_data : tria_cells)
6622 {
6623 for (unsigned int &vertex_n : cell_data.vertices)
6624 vertex_n += n_accumulated_vertices;
6625 cells.push_back(cell_data);
6626 }
6627
6628 // Skip copying lines with no manifold information.
6629 if (copy_manifold_ids)
6630 {
6631 for (CellData<1> &line_data : tria_subcell_data.boundary_lines)
6632 {
6633 if (line_data.manifold_id == numbers::flat_manifold_id)
6634 continue;
6635 for (unsigned int &vertex_n : line_data.vertices)
6636 vertex_n += n_accumulated_vertices;
6637 line_data.boundary_id =
6639 subcell_data.boundary_lines.push_back(line_data);
6640 }
6641
6642 for (CellData<2> &quad_data : tria_subcell_data.boundary_quads)
6643 {
6644 if (quad_data.manifold_id == numbers::flat_manifold_id)
6645 continue;
6646 for (unsigned int &vertex_n : quad_data.vertices)
6647 vertex_n += n_accumulated_vertices;
6648 quad_data.boundary_id =
6650 subcell_data.boundary_quads.push_back(quad_data);
6651 }
6652 }
6653
6654 n_accumulated_vertices += triangulation->n_vertices();
6655 }
6656
6657 // throw out duplicated vertices
6658 std::vector<unsigned int> considered_vertices;
6660 cells,
6661 subcell_data,
6662 considered_vertices,
6663 duplicated_vertex_tolerance);
6664
6665 // reorder the cells to ensure that they satisfy the convention for
6666 // edge and face directions
6667 if (std::all_of(cells.begin(), cells.end(), [](const auto &cell) {
6668 return cell.vertices.size() ==
6669 ReferenceCells::get_hypercube<dim>().n_vertices();
6670 }))
6672 result.clear();
6673 result.create_triangulation(vertices, cells, subcell_data);
6674
6675 if (!copy_manifold_ids)
6677
6678 if (copy_boundary_ids)
6679 {
6680 auto result_cell = result.begin();
6681 for (const auto &tria : triangulations)
6682 {
6683 for (const auto &cell : tria->cell_iterators())
6684 {
6685 for (const auto &f : cell->face_indices())
6686 if (result_cell->face(f)->at_boundary())
6687 result_cell->face(f)->set_boundary_id(
6688 cell->face(f)->boundary_id());
6689 ++result_cell;
6690 }
6691 }
6692 }
6693
6694 Assert(duplicated_vertex_tolerance > 0.0 ||
6695 n_accumulated_vertices == result.n_vertices(),
6697 }
6698
6699
6700
6701 template <int dim, int spacedim>
6702 void
6704 const Triangulation<dim, spacedim> &triangulation_2,
6706 const double duplicated_vertex_tolerance,
6707 const bool copy_manifold_ids,
6708 const bool copy_boundary_ids)
6709 {
6710 // if either Triangulation is empty then merging is just a copy.
6711 if (triangulation_1.n_cells() == 0)
6712 {
6713 if (&result != &triangulation_2)
6714 result.copy_triangulation(triangulation_2);
6715 }
6716 else if (triangulation_2.n_cells() == 0)
6717 {
6718 if (&result != &triangulation_1)
6719 result.copy_triangulation(triangulation_1);
6720 }
6721 else
6722 merge_triangulations({&triangulation_1, &triangulation_2},
6723 result,
6724 duplicated_vertex_tolerance,
6725 copy_manifold_ids,
6726 copy_boundary_ids);
6727 }
6728
6729
6730
6731 namespace
6732 {
6754 template <int structdim>
6755 void
6756 delete_duplicated_objects(std::vector<CellData<structdim>> &subcell_data)
6757 {
6758 static_assert(structdim == 1 || structdim == 2,
6759 "This function is only implemented for lines and "
6760 "quadrilaterals.");
6761 // start by making sure that all objects representing the same vertices
6762 // are numbered in the same way by canonicalizing the numberings. This
6763 // makes it possible to detect duplicates.
6764 for (CellData<structdim> &cell_data : subcell_data)
6765 {
6766 if (structdim == 1)
6767 std::sort(std::begin(cell_data.vertices),
6768 std::end(cell_data.vertices));
6769 else if (structdim == 2)
6770 {
6771 // rotate the vertex numbers so that the lowest one is first
6772 std::array<unsigned int, 4> renumbering{};
6773 std::copy(std::begin(cell_data.vertices),
6774 std::end(cell_data.vertices),
6775 renumbering.begin());
6776
6777 // convert to old style vertex numbering. This makes the
6778 // permutations easy since the valid configurations are
6779 //
6780 // 3 2 2 1 1 0 0 3
6781 // 0 1 3 0 2 3 1 2
6782 // (0123) (3012) (2310) (1230)
6783 //
6784 // rather than the lexical ordering which is harder to permute
6785 // by rotation.
6786 std::swap(renumbering[2], renumbering[3]);
6787 std::rotate(renumbering.begin(),
6788 std::min_element(renumbering.begin(),
6789 renumbering.end()),
6790 renumbering.end());
6791 // convert to new style
6792 std::swap(renumbering[2], renumbering[3]);
6793 // deal with cases where we might have
6794 //
6795 // 3 2 1 2
6796 // 0 1 0 3
6797 //
6798 // by forcing the second vertex (in lexical ordering) to be
6799 // smaller than the third
6800 if (renumbering[1] > renumbering[2])
6801 std::swap(renumbering[1], renumbering[2]);
6802 std::copy(renumbering.begin(),
6803 renumbering.end(),
6804 std::begin(cell_data.vertices));
6805 }
6806 }
6807
6808 // Now that all cell objects have been canonicalized they can be sorted:
6809 auto compare = [](const CellData<structdim> &a,
6810 const CellData<structdim> &b) {
6811 return std::lexicographical_compare(std::begin(a.vertices),
6812 std::end(a.vertices),
6813 std::begin(b.vertices),
6814 std::end(b.vertices));
6815 };
6816 std::sort(subcell_data.begin(), subcell_data.end(), compare);
6817
6818 // Finally, determine which objects are duplicates. Duplicates are
6819 // assumed to be interior objects, so delete all but one and change the
6820 // boundary id:
6821 auto left = subcell_data.begin();
6822 while (left != subcell_data.end())
6823 {
6824 const auto right =
6825 std::upper_bound(left, subcell_data.end(), *left, compare);
6826 // if the range has more than one item, then there are duplicates -
6827 // set all boundary ids in the range to the internal boundary id
6828 if (left + 1 != right)
6829 for (auto it = left; it != right; ++it)
6830 {
6831 it->boundary_id = numbers::internal_face_boundary_id;
6832 Assert(it->manifold_id == left->manifold_id,
6833 ExcMessage(
6834 "In the process of grid generation a single "
6835 "line or quadrilateral has been assigned two "
6836 "different manifold ids. This can happen when "
6837 "a Triangulation is copied, e.g., via "
6838 "GridGenerator::replicate_triangulation() and "
6839 "not all external boundary faces have the same "
6840 "manifold id. Double check that all faces "
6841 "which you expect to be merged together have "
6842 "the same manifold id."));
6843 }
6844 left = right;
6845 }
6846
6847 subcell_data.erase(std::unique(subcell_data.begin(), subcell_data.end()),
6848 subcell_data.end());
6849 }
6850 } // namespace
6851
6852
6853
6854 template <int dim, int spacedim>
6855 void
6857 const std::vector<unsigned int> &extents,
6859 {
6860 AssertDimension(dim, extents.size());
6861 if constexpr (running_in_debug_mode())
6862 {
6863 for (const auto &extent : extents)
6864 Assert(0 < extent,
6865 ExcMessage(
6866 "The Triangulation must be copied at least one time in "
6867 "each coordinate dimension."));
6868 }
6869 const BoundingBox<spacedim> bbox(input.get_vertices());
6870 const auto &min = bbox.get_boundary_points().first;
6871 const auto &max = bbox.get_boundary_points().second;
6872
6873 std::array<Tensor<1, spacedim>, dim> offsets;
6874 for (unsigned int d = 0; d < dim; ++d)
6875 offsets[d][d] = max[d] - min[d];
6876
6877 Triangulation<dim, spacedim> tria_to_replicate;
6878 tria_to_replicate.copy_triangulation(input);
6879 for (unsigned int d = 0; d < dim; ++d)
6880 {
6881 auto [input_vertices, input_cell_data, input_subcell_data] =
6882 GridTools::get_coarse_mesh_description(tria_to_replicate);
6883
6884 std::vector<Point<spacedim>> output_vertices = input_vertices;
6885 std::vector<CellData<dim>> output_cell_data = input_cell_data;
6886 SubCellData output_subcell_data = input_subcell_data;
6887
6888 for (unsigned int k = 1; k < extents[d]; ++k)
6889 {
6890 const std::size_t vertex_offset = k * input_vertices.size();
6891 // vertices
6892 for (const Point<spacedim> &point : input_vertices)
6893 output_vertices.push_back(point + double(k) * offsets[d]);
6894 // cell data
6895 for (const CellData<dim> &cell_data : input_cell_data)
6896 {
6897 output_cell_data.push_back(cell_data);
6898 for (unsigned int &vertex : output_cell_data.back().vertices)
6899 vertex += vertex_offset;
6900 }
6901 // subcell data
6902 for (const CellData<1> &boundary_line :
6903 input_subcell_data.boundary_lines)
6904 {
6905 output_subcell_data.boundary_lines.push_back(boundary_line);
6906 for (unsigned int &vertex :
6907 output_subcell_data.boundary_lines.back().vertices)
6908 vertex += vertex_offset;
6909 }
6910 for (const CellData<2> &boundary_quad :
6911 input_subcell_data.boundary_quads)
6912 {
6913 output_subcell_data.boundary_quads.push_back(boundary_quad);
6914 for (unsigned int &vertex :
6915 output_subcell_data.boundary_quads.back().vertices)
6916 vertex += vertex_offset;
6917 }
6918 }
6919 // check all vertices: since the grid is coarse, most will be on the
6920 // boundary anyway
6921 std::vector<unsigned int> boundary_vertices;
6923 output_vertices,
6924 output_cell_data,
6925 output_subcell_data,
6926 boundary_vertices,
6927 1e-6 * input.begin_active()->diameter());
6928 // delete_duplicated_vertices also deletes any unused vertices
6929 // deal with any reordering issues created by delete_duplicated_vertices
6930 GridTools::consistently_order_cells(output_cell_data);
6931 // clean up the boundary ids of the boundary objects: note that we
6932 // have to do this after delete_duplicated_vertices so that boundary
6933 // objects are actually duplicated at this point
6934 if (dim == 2)
6935 delete_duplicated_objects(output_subcell_data.boundary_lines);
6936 else if (dim == 3)
6937 {
6938 delete_duplicated_objects(output_subcell_data.boundary_quads);
6939 for (CellData<1> &boundary_line :
6940 output_subcell_data.boundary_lines)
6941 // set boundary lines to the default value - let
6942 // create_triangulation figure out the rest.
6944 }
6945
6946 tria_to_replicate.clear();
6947 tria_to_replicate.create_triangulation(output_vertices,
6948 output_cell_data,
6949 output_subcell_data);
6950 }
6951
6952 result.copy_triangulation(tria_to_replicate);
6953 }
6954
6955
6956
6957 template <int dim, int spacedim>
6958 void
6960 const Triangulation<dim, spacedim> &triangulation_1,
6961 const Triangulation<dim, spacedim> &triangulation_2,
6963 {
6964 Assert(GridTools::have_same_coarse_mesh(triangulation_1, triangulation_2),
6965 ExcMessage("The two input triangulations are not derived from "
6966 "the same coarse mesh as required."));
6967 Assert((dynamic_cast<
6969 &triangulation_1) == nullptr) &&
6970 (dynamic_cast<
6972 &triangulation_2) == nullptr),
6973 ExcMessage("The source triangulations for this function must both "
6974 "be available entirely locally, and not be distributed "
6975 "triangulations."));
6976
6977 // first copy triangulation_1, and
6978 // then do as many iterations as
6979 // there are levels in
6980 // triangulation_2 to refine
6981 // additional cells. since this is
6982 // the maximum number of
6983 // refinements to get from the
6984 // coarse grid to triangulation_2,
6985 // it is clear that this is also
6986 // the maximum number of
6987 // refinements to get from any cell
6988 // on triangulation_1 to
6989 // triangulation_2
6990 result.clear();
6991 result.copy_triangulation(triangulation_1);
6992 for (unsigned int iteration = 0; iteration < triangulation_2.n_levels();
6993 ++iteration)
6994 {
6996 intergrid_map.make_mapping(result, triangulation_2);
6997
6998 bool any_cell_flagged = false;
6999 for (const auto &result_cell : result.active_cell_iterators())
7000 if (intergrid_map[result_cell]->has_children())
7001 {
7002 any_cell_flagged = true;
7003 result_cell->set_refine_flag();
7004 }
7005
7006 if (any_cell_flagged == false)
7007 break;
7008 else
7010 }
7011 }
7012
7013
7014
7015 template <int dim, int spacedim>
7016 void
7018 const Triangulation<dim, spacedim> &input_triangulation,
7020 &cells_to_remove,
7022 {
7023 // simply copy the vertices; we will later strip those
7024 // that turn out to be unused
7025 std::vector<Point<spacedim>> vertices = input_triangulation.get_vertices();
7026
7027 // the loop through the cells and copy stuff, excluding
7028 // the ones we are to remove
7029 std::vector<CellData<dim>> cells;
7030 for (const auto &cell : input_triangulation.active_cell_iterators())
7031 if (cells_to_remove.find(cell) == cells_to_remove.end())
7032 {
7033 Assert(static_cast<unsigned int>(cell->level()) ==
7034 input_triangulation.n_levels() - 1,
7035 ExcMessage(
7036 "Your input triangulation appears to have "
7037 "adaptively refined cells. This is not allowed. You can "
7038 "only call this function on a triangulation in which "
7039 "all cells are on the same refinement level."));
7040
7041 CellData<dim> this_cell;
7042 for (const unsigned int v : GeometryInfo<dim>::vertex_indices())
7043 this_cell.vertices[v] = cell->vertex_index(v);
7044 this_cell.material_id = cell->material_id();
7045 cells.push_back(this_cell);
7046 }
7047
7048 // throw out duplicated vertices from the two meshes, reorder vertices as
7049 // necessary and create the triangulation
7050 SubCellData subcell_data;
7051 std::vector<unsigned int> considered_vertices;
7053 cells,
7054 subcell_data,
7055 considered_vertices);
7056
7057 // then clear the old triangulation and create the new one
7058 result.clear();
7059 result.create_triangulation(vertices, cells, subcell_data);
7060 }
7061
7062
7063
7064 void
7066 const Triangulation<2, 2> &input,
7067 const unsigned int n_slices,
7068 const double height,
7069 Triangulation<3, 3> &result,
7070 const bool copy_manifold_ids,
7071 const std::vector<types::manifold_id> &manifold_priorities)
7072 {
7073 Assert(input.n_levels() == 1,
7074 ExcMessage(
7075 "The input triangulation must be a coarse mesh, i.e., it must "
7076 "not have been refined."));
7077 Assert(result.n_cells() == 0,
7078 ExcMessage("The output triangulation object needs to be empty."));
7079 Assert(height > 0,
7080 ExcMessage("The given height for extrusion must be positive."));
7081 Assert(n_slices >= 2,
7082 ExcMessage(
7083 "The number of slices for extrusion must be at least 2."));
7084
7085 const double delta_h = height / (n_slices - 1);
7086 std::vector<double> slices_z_values;
7087 slices_z_values.reserve(n_slices);
7088 for (unsigned int i = 0; i < n_slices; ++i)
7089 slices_z_values.push_back(i * delta_h);
7091 input, slices_z_values, result, copy_manifold_ids, manifold_priorities);
7092 }
7093
7094
7095
7096 void
7098 const Triangulation<2, 2> &input,
7099 const unsigned int n_slices,
7100 const double height,
7101 Triangulation<2, 2> &result,
7102 const bool copy_manifold_ids,
7103 const std::vector<types::manifold_id> &manifold_priorities)
7104 {
7105 (void)input;
7106 (void)n_slices;
7107 (void)height;
7108 (void)result;
7109 (void)copy_manifold_ids;
7110 (void)manifold_priorities;
7111
7112 AssertThrow(false,
7113 ExcMessage(
7114 "GridTools::extrude_triangulation() is only available "
7115 "for Triangulation<3, 3> as output triangulation."));
7116 }
7117
7118
7119
7120 void
7122 const Triangulation<2, 2> &input,
7123 const std::vector<double> &slice_coordinates,
7124 Triangulation<3, 3> &result,
7125 const bool copy_manifold_ids,
7126 const std::vector<types::manifold_id> &manifold_priorities)
7127 {
7128 Assert(input.n_levels() == 1,
7129 ExcMessage(
7130 "The input triangulation must be a coarse mesh, i.e., it must "
7131 "not have been refined."));
7132 Assert(result.n_cells() == 0,
7133 ExcMessage("The output triangulation object needs to be empty."));
7134 Assert(slice_coordinates.size() >= 2,
7135 ExcMessage(
7136 "The number of slices for extrusion must be at least 2."));
7137 Assert(std::is_sorted(slice_coordinates.begin(), slice_coordinates.end()),
7138 ExcMessage("Slice z-coordinates should be in ascending order"));
7140 ExcMessage(
7141 "This function is only implemented for quadrilateral meshes."));
7142
7143 const auto priorities = [&]() -> std::vector<types::manifold_id> {
7144 // if a non-empty (i.e., not the default) vector is given for
7145 // manifold_priorities then use it (but check its validity in debug
7146 // mode)
7147 if (0 < manifold_priorities.size())
7148 {
7149 if constexpr (running_in_debug_mode())
7150 {
7151 // check that the provided manifold_priorities is valid
7152 std::vector<types::manifold_id> sorted_manifold_priorities =
7153 manifold_priorities;
7154 std::sort(sorted_manifold_priorities.begin(),
7155 sorted_manifold_priorities.end());
7156 Assert(std::unique(sorted_manifold_priorities.begin(),
7157 sorted_manifold_priorities.end()) ==
7158 sorted_manifold_priorities.end(),
7159 ExcMessage(
7160 "The given vector of manifold ids may not contain any "
7161 "duplicated entries."));
7162 std::vector<types::manifold_id> sorted_manifold_ids =
7163 input.get_manifold_ids();
7164 std::sort(sorted_manifold_ids.begin(), sorted_manifold_ids.end());
7165 if (sorted_manifold_priorities != sorted_manifold_ids)
7166 {
7167 std::ostringstream message;
7168 message << "The given triangulation has manifold ids {";
7169 for (const types::manifold_id manifold_id :
7170 sorted_manifold_ids)
7171 if (manifold_id != sorted_manifold_ids.back())
7172 message << manifold_id << ", ";
7173 message << sorted_manifold_ids.back() << "}, but \n"
7174 << " the given vector of manifold ids is {";
7175 for (const types::manifold_id manifold_id :
7176 manifold_priorities)
7177 if (manifold_id != manifold_priorities.back())
7178 message << manifold_id << ", ";
7179 message
7180 << manifold_priorities.back() << "}.\n"
7181 << " These vectors should contain the same elements.\n";
7182 const std::string m = message.str();
7183 Assert(false, ExcMessage(m));
7184 }
7185 }
7186 return manifold_priorities;
7187 }
7188 // otherwise use the default ranking: ascending order, but TFI manifolds
7189 // are at the end.
7190 std::vector<types::manifold_id> default_priorities =
7191 input.get_manifold_ids();
7192 const auto first_tfi_it = std::partition(
7193 default_priorities.begin(),
7194 default_priorities.end(),
7195 [&input](const types::manifold_id &id) {
7196 return dynamic_cast<const TransfiniteInterpolationManifold<2, 2> *>(
7197 &input.get_manifold(id)) == nullptr;
7198 });
7199 std::sort(default_priorities.begin(), first_tfi_it);
7200 std::sort(first_tfi_it, default_priorities.end());
7201
7202 return default_priorities;
7203 }();
7204
7205 const std::size_t n_slices = slice_coordinates.size();
7206 std::vector<Point<3>> points(n_slices * input.n_vertices());
7207 std::vector<CellData<3>> cells;
7208 cells.reserve((n_slices - 1) * input.n_active_cells());
7209
7210 // copy the array of points as many times as there will be slices,
7211 // one slice at a time. The z-axis value are defined in slices_coordinates
7212 for (std::size_t slice_n = 0; slice_n < n_slices; ++slice_n)
7213 {
7214 for (std::size_t vertex_n = 0; vertex_n < input.n_vertices();
7215 ++vertex_n)
7216 {
7217 const Point<2> vertex = input.get_vertices()[vertex_n];
7218 points[slice_n * input.n_vertices() + vertex_n] =
7219 Point<3>(vertex[0], vertex[1], slice_coordinates[slice_n]);
7220 }
7221 }
7222
7223 // then create the cells of each of the slices, one stack at a
7224 // time
7225 for (const auto &cell : input.active_cell_iterators())
7226 {
7227 for (std::size_t slice_n = 0; slice_n < n_slices - 1; ++slice_n)
7228 {
7229 CellData<3> this_cell;
7230 for (const unsigned int vertex_n :
7232 {
7233 this_cell.vertices[vertex_n] =
7234 cell->vertex_index(vertex_n) + slice_n * input.n_vertices();
7235 this_cell
7237 cell->vertex_index(vertex_n) +
7238 (slice_n + 1) * input.n_vertices();
7239 }
7240
7241 this_cell.material_id = cell->material_id();
7242 if (copy_manifold_ids)
7243 this_cell.manifold_id = cell->manifold_id();
7244 cells.push_back(this_cell);
7245 }
7246 }
7247
7248 // Next, create face data for all faces that are orthogonal to the x-y
7249 // plane
7250 SubCellData subcell_data;
7251 std::vector<CellData<2>> &quads = subcell_data.boundary_quads;
7252 types::boundary_id max_boundary_id = 0;
7253 quads.reserve(input.n_active_lines() * (n_slices - 1) +
7254 input.n_active_cells() * 2);
7255 for (const auto &face : input.active_face_iterators())
7256 {
7257 CellData<2> quad;
7258 quad.boundary_id = face->boundary_id();
7259 if (face->at_boundary())
7260 max_boundary_id = std::max(max_boundary_id, quad.boundary_id);
7261 if (copy_manifold_ids)
7262 quad.manifold_id = face->manifold_id();
7263 for (std::size_t slice_n = 0; slice_n < n_slices - 1; ++slice_n)
7264 {
7265 quad.vertices[0] =
7266 face->vertex_index(0) + slice_n * input.n_vertices();
7267 quad.vertices[1] =
7268 face->vertex_index(1) + slice_n * input.n_vertices();
7269 quad.vertices[2] =
7270 face->vertex_index(0) + (slice_n + 1) * input.n_vertices();
7271 quad.vertices[3] =
7272 face->vertex_index(1) + (slice_n + 1) * input.n_vertices();
7273 quads.push_back(quad);
7274 }
7275 }
7276
7277 // if necessary, create face data for faces parallel to the x-y
7278 // plane. This is only necessary if we need to set manifolds.
7279 if (copy_manifold_ids)
7280 for (const auto &cell : input.active_cell_iterators())
7281 {
7282 CellData<2> quad;
7284 quad.manifold_id = cell->manifold_id(); // check is outside loop
7285 for (std::size_t slice_n = 1; slice_n < n_slices - 1; ++slice_n)
7286 {
7287 quad.vertices[0] =
7288 cell->vertex_index(0) + slice_n * input.n_vertices();
7289 quad.vertices[1] =
7290 cell->vertex_index(1) + slice_n * input.n_vertices();
7291 quad.vertices[2] =
7292 cell->vertex_index(2) + slice_n * input.n_vertices();
7293 quad.vertices[3] =
7294 cell->vertex_index(3) + slice_n * input.n_vertices();
7295 quads.push_back(quad);
7296 }
7297 }
7298
7299 // then mark the bottom and top boundaries of the extruded mesh
7300 // with max_boundary_id+1 and max_boundary_id+2. check that this
7301 // remains valid
7302 Assert((max_boundary_id != numbers::invalid_boundary_id) &&
7303 (max_boundary_id + 1 != numbers::invalid_boundary_id) &&
7304 (max_boundary_id + 2 != numbers::invalid_boundary_id),
7305 ExcMessage(
7306 "The input triangulation to this function is using boundary "
7307 "indicators in a range that do not allow using "
7308 "max_boundary_id+1 and max_boundary_id+2 as boundary "
7309 "indicators for the bottom and top faces of the "
7310 "extruded triangulation."));
7311 const types::boundary_id bottom_boundary_id = max_boundary_id + 1;
7312 const types::boundary_id top_boundary_id = max_boundary_id + 2;
7313 for (const auto &cell : input.active_cell_iterators())
7314 {
7315 CellData<2> quad;
7316 quad.boundary_id = bottom_boundary_id;
7317 quad.vertices[0] = cell->vertex_index(0);
7318 quad.vertices[1] = cell->vertex_index(1);
7319 quad.vertices[2] = cell->vertex_index(2);
7320 quad.vertices[3] = cell->vertex_index(3);
7321 if (copy_manifold_ids)
7322 quad.manifold_id = cell->manifold_id();
7323 quads.push_back(quad);
7324
7325 quad.boundary_id = top_boundary_id;
7326 for (unsigned int &vertex : quad.vertices)
7327 vertex += (n_slices - 1) * input.n_vertices();
7328 if (copy_manifold_ids)
7329 quad.manifold_id = cell->manifold_id();
7330 quads.push_back(quad);
7331 }
7332
7333 // use all of this to finally create the extruded 3d
7334 // triangulation. it is not necessary to call
7335 // GridTools::consistently_order_cells() because the cells we have
7336 // constructed above are automatically correctly oriented. this is
7337 // because the 2d base mesh is always correctly oriented, and
7338 // extruding it automatically yields a correctly oriented 3d mesh,
7339 // as discussed in the edge orientation paper mentioned in the
7340 // introduction to the @ref reordering "reordering module".
7341 result.create_triangulation(points, cells, subcell_data);
7342
7343 for (auto manifold_id_it = priorities.rbegin();
7344 manifold_id_it != priorities.rend();
7345 ++manifold_id_it)
7346 for (const auto &face : result.active_face_iterators())
7347 if (face->manifold_id() == *manifold_id_it)
7348 for (unsigned int line_n = 0;
7349 line_n < GeometryInfo<3>::lines_per_face;
7350 ++line_n)
7351 face->line(line_n)->set_manifold_id(*manifold_id_it);
7352 }
7353
7354
7355
7356 void
7358 const Triangulation<2, 2> &input,
7359 const std::vector<double> &slice_coordinates,
7360 Triangulation<2, 2> &result,
7361 const bool copy_manifold_ids,
7362 const std::vector<types::manifold_id> &manifold_priorities)
7363 {
7364 (void)input;
7365 (void)slice_coordinates;
7366 (void)result;
7367 (void)copy_manifold_ids;
7368 (void)manifold_priorities;
7369
7370 AssertThrow(false,
7371 ExcMessage(
7372 "GridTools::extrude_triangulation() is only available "
7373 "for Triangulation<3, 3> as output triangulation."));
7374 }
7375
7376
7377
7378 template <>
7379 void
7381 const double,
7382 const double,
7383 const double,
7384 const unsigned int,
7385 const bool)
7386 {
7388 }
7389
7390
7391
7392 template <int spacedim>
7393 void
7394 hyper_cube_with_cylindrical_hole_2D(Triangulation<2, spacedim> &triangulation,
7395 const double inner_radius,
7396 const double outer_radius,
7397 const double, // width,
7398 const unsigned int, // width_repetition,
7399 const bool colorize)
7400 {
7401 const int dim = 2;
7402
7403 Assert(inner_radius < outer_radius,
7404 ExcMessage("outer_radius has to be bigger than inner_radius."));
7405
7406 const Point<spacedim> center;
7407
7408 // We create a hyper_shell (i.e., an annulus) in two dimensions, and then we
7409 // modify it by pulling the vertices on the diagonals out to where the
7410 // corners of a square would be:
7411 hyper_shell(triangulation, center, inner_radius, outer_radius, 8);
7412 triangulation.set_all_manifold_ids(numbers::flat_manifold_id);
7413 std::vector<bool> treated_vertices(triangulation.n_vertices(), false);
7414 for (const auto &cell : triangulation.active_cell_iterators())
7415 {
7416 for (auto f : GeometryInfo<dim>::face_indices())
7417 if (cell->face(f)->at_boundary())
7418 for (const unsigned int v : cell->face(f)->vertex_indices())
7419 if (/* is the vertex on the outer ring? */
7420 (std::fabs(cell->face(f)->vertex(v).norm() - outer_radius) <
7421 1e-12 * outer_radius)
7422 /* and */
7423 &&
7424 /* is the vertex on one of the two diagonals? */
7425 (std::fabs(std::fabs(cell->face(f)->vertex(v)[0]) -
7426 std::fabs(cell->face(f)->vertex(v)[1])) <
7427 1e-12 * outer_radius))
7428 cell->face(f)->vertex(v) *= std::sqrt(2.);
7429 }
7430 const double eps = 1e-3 * outer_radius;
7431 for (const auto &cell : triangulation.active_cell_iterators())
7432 {
7433 for (const unsigned int f : cell->face_indices())
7434 if (cell->face(f)->at_boundary())
7435 {
7436 const double dx = cell->face(f)->center()[0] - center[0];
7437 const double dy = cell->face(f)->center()[1] - center[1];
7438 if (colorize)
7439 {
7440 if (std::abs(dx + outer_radius) < eps)
7441 cell->face(f)->set_boundary_id(0);
7442 else if (std::abs(dx - outer_radius) < eps)
7443 cell->face(f)->set_boundary_id(1);
7444 else if (std::abs(dy + outer_radius) < eps)
7445 cell->face(f)->set_boundary_id(2);
7446 else if (std::abs(dy - outer_radius) < eps)
7447 cell->face(f)->set_boundary_id(3);
7448 else
7449 {
7450 cell->face(f)->set_boundary_id(4);
7451 cell->face(f)->set_manifold_id(0);
7452 }
7453 }
7454 else
7455 {
7456 const double d = (cell->face(f)->center() - center).norm();
7457 if (d - inner_radius < 0)
7458 {
7459 cell->face(f)->set_boundary_id(1);
7460 cell->face(f)->set_manifold_id(0);
7461 }
7462 else
7463 cell->face(f)->set_boundary_id(0);
7464 }
7465 }
7466 }
7467 triangulation.set_manifold(0, PolarManifold<2, spacedim>(center));
7468 }
7469
7470
7471
7472 template <>
7473 void
7475 const double inner_radius,
7476 const double outer_radius,
7477 const double width,
7478 const unsigned int width_repetition,
7479 const bool colorize)
7480 {
7481 hyper_cube_with_cylindrical_hole_2D(triangulation,
7482 inner_radius,
7483 outer_radius,
7484 width,
7485 width_repetition,
7486 colorize);
7487 }
7488
7489
7490
7491 template <>
7492 void
7494 const double inner_radius,
7495 const double outer_radius,
7496 const double width,
7497 const unsigned int width_repetition,
7498 const bool colorize)
7499 {
7500 hyper_cube_with_cylindrical_hole_2D(triangulation,
7501 inner_radius,
7502 outer_radius,
7503 width,
7504 width_repetition,
7505 colorize);
7506 }
7507
7508
7509
7510 template <int dim>
7511 void
7513 const Point<dim> &center,
7514 const double inner_radius,
7515 const double outer_radius,
7516 const unsigned int n_shells,
7517 const double skewness,
7518 const unsigned int n_cells,
7519 const bool colorize)
7520 {
7521 Assert(dim == 2 || dim == 3, ExcNotImplemented());
7522 (void)colorize;
7523 (void)n_cells;
7524 Assert(inner_radius < outer_radius,
7525 ExcMessage("outer_radius has to be bigger than inner_radius."));
7526 if (n_shells == 0)
7527 return; // empty Triangulation
7528
7529 std::vector<double> radii;
7530 radii.push_back(inner_radius);
7531 for (unsigned int shell_n = 1; shell_n < n_shells; ++shell_n)
7532 if (skewness == 0.0)
7533 // same as below, but works in the limiting case of zero skewness
7534 radii.push_back(inner_radius +
7535 (outer_radius - inner_radius) *
7536 (1.0 - (1.0 - double(shell_n) / n_shells)));
7537 else
7538 radii.push_back(
7539 inner_radius +
7540 (outer_radius - inner_radius) *
7541 (1.0 - std::tanh(skewness * (1.0 - double(shell_n) / n_shells)) /
7542 std::tanh(skewness)));
7543 radii.push_back(outer_radius);
7544
7545 double grid_vertex_tolerance = 0.0;
7546 for (unsigned int shell_n = 0; shell_n < radii.size() - 1; ++shell_n)
7547 {
7548 Triangulation<dim> current_shell;
7549 GridGenerator::hyper_shell(current_shell,
7550 center,
7551 radii[shell_n],
7552 radii[shell_n + 1],
7553 n_cells == 0 ? (dim == 2 ? 8 : 12) :
7554 n_cells);
7555
7556 // The innermost shell has the smallest cells: use that to set the
7557 // vertex merging tolerance
7558 if (grid_vertex_tolerance == 0.0)
7559 grid_vertex_tolerance =
7560 0.5 * internal::minimal_vertex_distance(current_shell);
7561
7562 Triangulation<dim> temp(std::move(triangulation));
7563 triangulation.clear();
7565 temp,
7567 grid_vertex_tolerance);
7568 }
7569
7571 triangulation.set_all_manifold_ids(manifold_id);
7572 if (dim == 2)
7573 triangulation.set_manifold(manifold_id, PolarManifold<dim>(center));
7574 else if (dim == 3)
7575 triangulation.set_manifold(manifold_id, SphericalManifold<dim>(center));
7576
7577 // We use boundary vertex positions to see if things are on the inner or
7578 // outer boundary.
7579 constexpr double radial_vertex_tolerance =
7580 100.0 * std::numeric_limits<double>::epsilon();
7581 auto assert_vertex_distance_within_tolerance =
7582 [center, radial_vertex_tolerance](
7583 const TriaIterator<TriaAccessor<dim - 1, dim, dim>> face,
7584 const double radius) {
7585 (void)center;
7586 (void)radial_vertex_tolerance;
7587 (void)face;
7588 (void)radius;
7589 for (unsigned int vertex_n = 0;
7590 vertex_n < GeometryInfo<dim>::vertices_per_face;
7591 ++vertex_n)
7592 {
7593 Assert(std::abs((face->vertex(vertex_n) - center).norm() - radius) <
7594 (center.norm() + radius) * radial_vertex_tolerance,
7596 }
7597 };
7598 if (colorize)
7599 for (const auto &cell : triangulation.active_cell_iterators())
7600 for (const unsigned int face_n : GeometryInfo<dim>::face_indices())
7601 {
7602 auto face = cell->face(face_n);
7603 if (face->at_boundary())
7604 {
7605 if (((face->vertex(0) - center).norm() - inner_radius) <
7606 (center.norm() + inner_radius) * radial_vertex_tolerance)
7607 {
7608 // we must be at an inner face, but check
7609 assert_vertex_distance_within_tolerance(face, inner_radius);
7610 face->set_all_boundary_ids(0);
7611 }
7612 else
7613 {
7614 // we must be at an outer face, but check
7615 assert_vertex_distance_within_tolerance(face, outer_radius);
7616 face->set_all_boundary_ids(1);
7617 }
7618 }
7619 }
7620 }
7621
7622
7623
7624 template <>
7625 void
7627 const double inner_radius,
7628 const double outer_radius,
7629 const double L,
7630 const unsigned int Nz,
7631 const bool colorize)
7632 {
7633 const int dim = 3;
7634
7635 Assert(inner_radius < outer_radius,
7636 ExcMessage("outer_radius has to be bigger than inner_radius."));
7637 Assert(L > 0, ExcMessage("Must give positive extension L"));
7638 Assert(Nz >= 1, ExcLowerRange(1, Nz));
7639
7640 // Start with a cylinder shell with the correct inner and outer radius
7641 // and as many layers as requested
7642 cylinder_shell(triangulation, L, inner_radius, outer_radius, 8, Nz, false);
7643 triangulation.set_all_manifold_ids(numbers::flat_manifold_id);
7644
7645 // Then loop over all vertices that are at the boundary (by looping
7646 // over all cells, their faces, and if the face is at the boundary,
7647 // their vertices. If we haven't touched that vertex yet, see if
7648 // we need to move it from its cylinder mantle position to the
7649 // outer boundary of the box.
7650 std::vector<bool> treated_vertices(triangulation.n_vertices(), false);
7651 for (const auto &cell : triangulation.active_cell_iterators())
7652 {
7653 for (const auto f : cell->face_indices())
7654 if (cell->face(f)->at_boundary())
7655 {
7656 for (const unsigned int v : cell->face(f)->vertex_indices())
7657 {
7658 const unsigned int vv = cell->face(f)->vertex_index(v);
7659 if (treated_vertices[vv] == false)
7660 {
7661 treated_vertices[vv] = true;
7662
7663 // The vertices we have to treat are the ones that
7664 // have x=y or x=-y and are at the outer ring -- that is,
7665 // they are on the diagonal in the x-y plane and radius
7666 // equal to outer_radius. These need to be pulled out to
7667 // the corner point of the square, i.e., their x and y
7668 // coordinates need to be multiplied by sqrt(2),
7669 // whereas the z coordinate remains unchanged:
7670 const Point<dim> vertex_location =
7671 cell->face(f)->vertex(v);
7672 if ((std::fabs(std::fabs(vertex_location[0]) -
7673 std::fabs(vertex_location[1])) <
7674 1e-12 * outer_radius) &&
7675 (std::fabs(vertex_location[0] * vertex_location[0] +
7676 vertex_location[1] * vertex_location[1] -
7677 outer_radius * outer_radius) <
7678 1e-12 * outer_radius))
7679 cell->face(f)->vertex(v) =
7680 Point<3>(vertex_location[0] * std::sqrt(2.0),
7681 vertex_location[1] * std::sqrt(2.0),
7682 vertex_location[2]);
7683 }
7684 }
7685 }
7686 }
7687 double eps = 1e-3 * outer_radius;
7688 for (const auto &cell : triangulation.active_cell_iterators())
7689 {
7690 for (const unsigned int f : cell->face_indices())
7691 if (cell->face(f)->at_boundary())
7692 {
7693 const double dx = cell->face(f)->center()[0];
7694 const double dy = cell->face(f)->center()[1];
7695 const double dz = cell->face(f)->center()[2];
7696
7697 if (colorize)
7698 {
7699 if (std::abs(dx + outer_radius) < eps)
7700 cell->face(f)->set_boundary_id(0);
7701
7702 else if (std::abs(dx - outer_radius) < eps)
7703 cell->face(f)->set_boundary_id(1);
7704
7705 else if (std::abs(dy + outer_radius) < eps)
7706 cell->face(f)->set_boundary_id(2);
7707
7708 else if (std::abs(dy - outer_radius) < eps)
7709 cell->face(f)->set_boundary_id(3);
7710
7711 else if (std::abs(dz) < eps)
7712 cell->face(f)->set_boundary_id(4);
7713
7714 else if (std::abs(dz - L) < eps)
7715 cell->face(f)->set_boundary_id(5);
7716
7717 else
7718 {
7719 cell->face(f)->set_all_boundary_ids(6);
7720 cell->face(f)->set_all_manifold_ids(0);
7721 }
7722 }
7723 else
7724 {
7725 Point<dim> c = cell->face(f)->center();
7726 c[2] = 0;
7727 const double d = c.norm();
7728 if (d - inner_radius < 0)
7729 {
7730 cell->face(f)->set_all_boundary_ids(1);
7731 cell->face(f)->set_all_manifold_ids(0);
7732 }
7733 else
7734 cell->face(f)->set_boundary_id(0);
7735 }
7736 }
7737 }
7738 triangulation.set_manifold(0, CylindricalManifold<3>(2));
7739 }
7740
7741
7742
7743 template <int dim, int spacedim1, int spacedim2>
7744 void
7747 {
7748 Assert((dynamic_cast<
7750 &in_tria) == nullptr),
7751 ExcMessage(
7752 "This function cannot be used on "
7753 "parallel::distributed::Triangulation objects as inputs."));
7754 Assert(in_tria.has_hanging_nodes() == false,
7755 ExcMessage("This function does not work for meshes that have "
7756 "hanging nodes."));
7757
7758
7759 const unsigned int spacedim = std::min(spacedim1, spacedim2);
7760 const std::vector<Point<spacedim1>> &in_vertices = in_tria.get_vertices();
7761
7762 // Create an array of vertices, with components either truncated
7763 // or extended by zeroes.
7764 std::vector<Point<spacedim2>> v(in_vertices.size());
7765 for (unsigned int i = 0; i < in_vertices.size(); ++i)
7766 for (unsigned int d = 0; d < spacedim; ++d)
7767 v[i][d] = in_vertices[i][d];
7768
7769 std::vector<CellData<dim>> cells(in_tria.n_active_cells());
7770 for (const auto &cell : in_tria.active_cell_iterators())
7771 {
7772 const unsigned int id = cell->active_cell_index();
7773
7774 cells[id].vertices.resize(cell->n_vertices());
7775 for (const auto i : cell->vertex_indices())
7776 cells[id].vertices[i] = cell->vertex_index(i);
7777 cells[id].material_id = cell->material_id();
7778 cells[id].manifold_id = cell->manifold_id();
7779 }
7780
7781 SubCellData subcelldata;
7782 switch (dim)
7783 {
7784 case 1:
7785 {
7786 // Nothing to do in 1d
7787 break;
7788 }
7789
7790 case 2:
7791 {
7792 std::vector<bool> user_flags_line;
7793 in_tria.save_user_flags_line(user_flags_line);
7794 const_cast<Triangulation<dim, spacedim1> &>(in_tria)
7795 .clear_user_flags_line();
7796
7797 // Loop over all the faces of the triangulation and create
7798 // objects that describe their boundary and manifold ids.
7799 for (const auto &face : in_tria.active_face_iterators())
7800 {
7801 if (face->at_boundary())
7802 {
7803 CellData<1> boundary_line;
7804
7805 boundary_line.vertices.resize(face->n_vertices());
7806 for (const auto i : face->vertex_indices())
7807 boundary_line.vertices[i] = face->vertex_index(i);
7808 boundary_line.boundary_id = face->boundary_id();
7809 boundary_line.manifold_id = face->manifold_id();
7810
7811 subcelldata.boundary_lines.emplace_back(
7812 std::move(boundary_line));
7813 }
7814 else
7815 // The face is not at the boundary. We won't have to set
7816 // boundary_ids (that is not possible for interior faces), but
7817 // we need to do something if the manifold-id is not the
7818 // default.
7819 //
7820 // We keep track via the user flags whether we have already
7821 // dealt with a face or not. (We need to do that here because
7822 // we will return to interior faces twice, once for each
7823 // neighbor, whereas we only touch each of the boundary faces
7824 // above once.)
7825 if ((face->user_flag_set() == false) &&
7826 (face->manifold_id() != numbers::flat_manifold_id))
7827 {
7828 CellData<1> boundary_line;
7829
7830 boundary_line.vertices.resize(face->n_vertices());
7831 for (const auto i : face->vertex_indices())
7832 boundary_line.vertices[i] = face->vertex_index(i);
7833 boundary_line.boundary_id =
7835 boundary_line.manifold_id = face->manifold_id();
7836
7837 subcelldata.boundary_lines.emplace_back(
7838 std::move(boundary_line));
7839
7840 face->set_user_flag();
7841 }
7842 }
7843
7844 // Reset the user flags to their previous values:
7845 const_cast<Triangulation<dim, spacedim1> &>(in_tria)
7846 .load_user_flags_line(user_flags_line);
7847
7848 break;
7849 }
7850
7851 case 3:
7852 {
7853 std::vector<bool> user_flags_line;
7854 in_tria.save_user_flags_line(user_flags_line);
7855 const_cast<Triangulation<dim, spacedim1> &>(in_tria)
7856 .clear_user_flags_line();
7857
7858 std::vector<bool> user_flags_quad;
7859 in_tria.save_user_flags_quad(user_flags_quad);
7860 const_cast<Triangulation<dim, spacedim1> &>(in_tria)
7861 .clear_user_flags_quad();
7862
7863 // Loop over all the faces of the triangulation and create
7864 // objects that describe their boundary and manifold ids.
7865 for (const auto &face : in_tria.active_face_iterators())
7866 {
7867 if (face->at_boundary())
7868 {
7869 CellData<2> boundary_face;
7870
7871 boundary_face.vertices.resize(face->n_vertices());
7872 for (const auto i : face->vertex_indices())
7873 boundary_face.vertices[i] = face->vertex_index(i);
7874 boundary_face.boundary_id = face->boundary_id();
7875 boundary_face.manifold_id = face->manifold_id();
7876
7877 subcelldata.boundary_quads.emplace_back(
7878 std::move(boundary_face));
7879
7880 // Then also loop over the edges and do the same. We would
7881 // accidentally create duplicates for edges that are part of
7882 // two boundary faces. To avoid this, use the user_flag on
7883 // edges to mark those that we have already visited. (Note
7884 // how we save and restore those above and below.)
7885 for (unsigned int e = 0; e < face->n_lines(); ++e)
7886 if (face->line(e)->user_flag_set() == false)
7887 {
7888 const typename Triangulation<dim,
7889 spacedim1>::line_iterator
7890 edge = face->line(e);
7891 CellData<1> boundary_edge;
7892
7893 boundary_edge.vertices.resize(edge->n_vertices());
7894 for (const auto i : edge->vertex_indices())
7895 boundary_edge.vertices[i] = edge->vertex_index(i);
7896 boundary_edge.boundary_id = edge->boundary_id();
7897 boundary_edge.manifold_id = edge->manifold_id();
7898
7899 subcelldata.boundary_lines.emplace_back(
7900 std::move(boundary_edge));
7901
7902 edge->set_user_flag();
7903 }
7904 }
7905 else
7906 // The face is not at the boundary. We won't have to set
7907 // boundary_ids (that is not possible for interior faces), but
7908 // we need to do something if the manifold-id is not the
7909 // default.
7910 //
7911 // We keep track via the user flags whether we have already
7912 // dealt with a face or not. (We need to do that here because
7913 // we will return to interior faces twice, once for each
7914 // neighbor, whereas we only touch each of the boundary faces
7915 // above once.)
7916 //
7917 // Note that if we have already dealt with a face, then we
7918 // have also already dealt with the edges and don't have
7919 // to worry about that any more separately.
7920 if (face->user_flag_set() == false)
7921 {
7922 if (face->manifold_id() != numbers::flat_manifold_id)
7923 {
7924 CellData<2> boundary_face;
7925
7926 boundary_face.vertices.resize(face->n_vertices());
7927 for (const auto i : face->vertex_indices())
7928 boundary_face.vertices[i] = face->vertex_index(i);
7929 boundary_face.boundary_id =
7931 boundary_face.manifold_id = face->manifold_id();
7932
7933 subcelldata.boundary_quads.emplace_back(
7934 std::move(boundary_face));
7935
7936 face->set_user_flag();
7937 }
7938
7939 // Then also loop over the edges of this face. Because
7940 // every boundary edge must also be a part of a boundary
7941 // face, we can ignore these. But it is possible that we
7942 // have already encountered an interior edge through a
7943 // previous face, and in that case we have to just ignore
7944 // it
7945 for (unsigned int e = 0; e < face->n_lines(); ++e)
7946 if (face->line(e)->at_boundary() == false)
7947 if (face->line(e)->user_flag_set() == false)
7948 {
7949 const typename Triangulation<dim, spacedim1>::
7950 line_iterator edge = face->line(e);
7951 CellData<1> boundary_edge;
7952
7953 boundary_edge.vertices.resize(edge->n_vertices());
7954 for (const auto i : edge->vertex_indices())
7955 boundary_edge.vertices[i] =
7956 edge->vertex_index(i);
7957 boundary_edge.boundary_id =
7959 boundary_edge.manifold_id = edge->manifold_id();
7960
7961 subcelldata.boundary_lines.emplace_back(
7962 std::move(boundary_edge));
7963
7964 edge->set_user_flag();
7965 }
7966 }
7967 }
7968
7969 // Reset the user flags to their previous values:
7970 const_cast<Triangulation<dim, spacedim1> &>(in_tria)
7971 .load_user_flags_line(user_flags_line);
7972 const_cast<Triangulation<dim, spacedim1> &>(in_tria)
7973 .load_user_flags_quad(user_flags_quad);
7974
7975 break;
7976 }
7977 default:
7979 }
7980
7981 out_tria.create_triangulation(v, cells, subcelldata);
7982
7983 for (const auto i : out_tria.get_manifold_ids())
7984 if (i != numbers::flat_manifold_id)
7985 out_tria.set_manifold(i, FlatManifold<dim, spacedim2>());
7986 }
7987
7988
7989
7990 template <int dim, int spacedim>
7991 void
7994 {
7995 Assert(dim > 1, ExcNotImplemented());
7998 ExcMessage(
7999 "GridGenerator::convert_hypercube_to_simplex_mesh() expects a mesh that consists only of quads/hexes."));
8000
8002 if (in_tria.n_global_levels() > 1)
8003 {
8005 flatten_triangulation(in_tria, temp_tria);
8006 }
8007 const Triangulation<dim, spacedim> &ref_tria =
8008 in_tria.n_global_levels() > 1 ? temp_tria : in_tria;
8009
8010 // static tables with the definitions of cells, faces and edges by its
8011 // vertices for 2d and 3d. For the inheritance of the manifold_id,
8012 // definitions of inner-faces and boundary-faces are required. In case of
8013 // 3d, also inner-edges and boundary-edges need to be defined.
8014
8015 // Cell definition 2d:
8016 // A quadrilateral element is converted to 8 simplices elements. Each
8017 // triangle is defined by 3 vertices.
8018 static const ndarray<unsigned int, 8, 3> table_2D_cell = {{{{0, 6, 4}},
8019 {{8, 4, 6}},
8020 {{8, 6, 5}},
8021 {{1, 5, 6}},
8022 {{2, 4, 7}},
8023 {{8, 7, 4}},
8024 {{8, 5, 7}},
8025 {{3, 7, 5}}}};
8026
8027 // Cell definition 3d:
8028 // A hexahedron element is converted to 24 tetrahedron elements. Each
8029 // tetrahedron is defined by 4 vertices.
8030 static const ndarray<unsigned int, 24, 4> vertex_ids_for_cells_3d = {
8031 {{{0, 1, 12, 10}}, {{2, 3, 11, 12}}, {{7, 6, 11, 13}},
8032 {{5, 4, 13, 10}}, {{0, 2, 8, 12}}, {{4, 6, 13, 8}},
8033 {{5, 13, 7, 9}}, {{1, 9, 3, 12}}, {{0, 8, 4, 10}},
8034 {{1, 5, 9, 10}}, {{3, 7, 11, 9}}, {{2, 6, 8, 11}},
8035 {{12, 13, 10, 9}}, {{12, 13, 9, 11}}, {{12, 13, 11, 8}},
8036 {{12, 13, 8, 10}}, {{13, 8, 10, 4}}, {{13, 10, 9, 5}},
8037 {{13, 9, 11, 7}}, {{13, 11, 8, 6}}, {{10, 12, 9, 1}},
8038 {{9, 12, 11, 3}}, {{11, 12, 8, 2}}, {{8, 12, 10, 0}}}};
8039
8040 // Boundary-faces 2d:
8041 // After converting, each of the 4 quadrilateral faces is defined by faces
8042 // of 2 different triangles, i.e., lines. Note that lines are defined by 2
8043 // vertices.
8045 vertex_ids_for_boundary_faces_2d = {{{{{{0, 4}}, {{4, 2}}}},
8046 {{{{1, 5}}, {{5, 3}}}},
8047 {{{{0, 6}}, {{6, 1}}}},
8048 {{{{2, 7}}, {{7, 3}}}}}};
8049
8050 // Boundary-faces 3d:
8051 // After converting, each of the 6 hexahedron faces corresponds to faces of
8052 // 4 different tetrahedron faces, i.e., triangles. Note that a triangle is
8053 // defined by 3 vertices.
8055 vertex_ids_for_boundary_faces_3d = {
8056 {{{{{0, 4, 8}}, {{4, 8, 6}}, {{8, 6, 2}}, {{0, 2, 8}}}},
8057 {{{{1, 3, 9}}, {{3, 9, 7}}, {{9, 7, 5}}, {{1, 9, 5}}}},
8058 {{{{0, 1, 10}}, {{1, 10, 5}}, {{10, 5, 4}}, {{0, 10, 4}}}},
8059 {{{{2, 3, 11}}, {{3, 11, 7}}, {{11, 7, 6}}, {{2, 11, 6}}}},
8060 {{{{0, 1, 12}}, {{1, 12, 3}}, {{12, 3, 2}}, {{0, 12, 2}}}},
8061 {{{{4, 5, 13}}, {{5, 13, 7}}, {{13, 7, 6}}, {{4, 13, 6}}}}}};
8062
8063 // Inner-faces 2d:
8064 // The converted triangulation based on simplices has 8 faces that do not
8065 // form the boundary, i.e. inner-faces, each defined by 2 vertices.
8066 static const ndarray<unsigned int, 8, 2> vertex_ids_for_inner_faces_2d = {
8067 {{{6, 4}},
8068 {{6, 8}},
8069 {{6, 5}},
8070 {{4, 8}},
8071 {{8, 5}},
8072 {{7, 4}},
8073 {{7, 8}},
8074 {{7, 5}}}};
8075
8076 // Inner-faces 3d:
8077 // The converted triangulation based on simplices has 72 faces that do not
8078 // form the boundary, i.e. inner-faces, each defined by 3 vertices.
8079 static const ndarray<unsigned int, 72, 3> vertex_ids_for_inner_faces_3d = {
8080 {{{0, 12, 10}}, {{12, 1, 10}}, {{12, 1, 9}}, {{12, 3, 9}},
8081 {{12, 2, 11}}, {{12, 3, 11}}, {{12, 0, 8}}, {{12, 2, 8}},
8082 {{9, 13, 5}}, {{13, 7, 9}}, {{11, 7, 13}}, {{11, 6, 13}},
8083 {{4, 8, 13}}, {{6, 8, 13}}, {{4, 13, 10}}, {{13, 5, 10}},
8084 {{10, 9, 5}}, {{10, 9, 1}}, {{11, 9, 7}}, {{11, 9, 3}},
8085 {{8, 11, 2}}, {{8, 11, 6}}, {{8, 10, 0}}, {{8, 10, 4}},
8086 {{12, 3, 9}}, {{12, 9, 11}}, {{12, 3, 11}}, {{3, 9, 11}},
8087 {{2, 12, 8}}, {{2, 12, 11}}, {{2, 11, 8}}, {{8, 12, 11}},
8088 {{0, 12, 10}}, {{0, 12, 8}}, {{0, 8, 10}}, {{8, 10, 12}},
8089 {{12, 1, 10}}, {{12, 1, 9}}, {{1, 10, 9}}, {{10, 9, 12}},
8090 {{10, 8, 4}}, {{10, 8, 13}}, {{4, 13, 8}}, {{4, 13, 10}},
8091 {{10, 9, 13}}, {{10, 9, 5}}, {{13, 5, 10}}, {{13, 5, 9}},
8092 {{13, 7, 9}}, {{13, 7, 11}}, {{9, 11, 13}}, {{9, 11, 7}},
8093 {{8, 11, 13}}, {{8, 11, 6}}, {{6, 13, 8}}, {{6, 13, 11}},
8094 {{12, 13, 10}}, {{12, 13, 8}}, {{8, 10, 13}}, {{8, 10, 12}},
8095 {{12, 13, 10}}, {{12, 13, 9}}, {{10, 9, 13}}, {{10, 9, 12}},
8096 {{12, 13, 9}}, {{12, 13, 11}}, {{9, 11, 13}}, {{9, 11, 12}},
8097 {{12, 13, 11}}, {{12, 13, 8}}, {{8, 11, 13}}, {{8, 11, 12}}}};
8098
8099 // Inner-edges 3d:
8100 // The converted triangulation based on simplices has 60 edges that do not
8101 // coincide with the boundary, i.e. inner-edges, each defined by 2 vertices.
8102 static const ndarray<unsigned int, 60, 2> vertex_ids_for_inner_edges_3d = {
8103 {{{12, 10}}, {{12, 9}}, {{12, 11}}, {{12, 8}}, {{9, 13}}, {{11, 13}},
8104 {{8, 13}}, {{10, 13}}, {{10, 9}}, {{9, 11}}, {{11, 8}}, {{8, 10}},
8105 {{12, 9}}, {{12, 11}}, {{11, 9}}, {{12, 8}}, {{12, 11}}, {{11, 8}},
8106 {{12, 8}}, {{12, 10}}, {{10, 8}}, {{12, 10}}, {{12, 9}}, {{9, 10}},
8107 {{13, 10}}, {{13, 8}}, {{8, 10}}, {{13, 10}}, {{13, 9}}, {{9, 10}},
8108 {{13, 11}}, {{13, 9}}, {{11, 9}}, {{13, 11}}, {{13, 8}}, {{11, 8}},
8109 {{12, 13}}, {{8, 10}}, {{8, 13}}, {{10, 13}}, {{8, 12}}, {{10, 12}},
8110 {{12, 13}}, {{10, 9}}, {{10, 13}}, {{9, 13}}, {{10, 12}}, {{9, 12}},
8111 {{12, 13}}, {{9, 11}}, {{9, 13}}, {{11, 13}}, {{9, 12}}, {{11, 12}},
8112 {{12, 13}}, {{11, 8}}, {{11, 13}}, {{8, 13}}, {{11, 12}}, {{8, 12}}}};
8113
8114 // Boundary-edges 3d:
8115 // For each of the 6 boundary-faces of the hexahedron, there are 8 edges (of
8116 // different tetrahedrons) that coincide with the boundary, i.e.
8117 // boundary-edges. Each boundary-edge is defined by 2 vertices. 4 of these
8118 // edges are new (they are placed in the middle of a presently existing
8119 // face); the other 4 coincide with edges present in the hexahedral
8120 // triangulation. The new 4 edges inherit the manifold id of the relevant
8121 // face, but the other 4 need to be copied from the input and thus do not
8122 // require a lookup table.
8124 vertex_ids_for_new_boundary_edges_3d = {
8125 {{{{{4, 8}}, {{6, 8}}, {{0, 8}}, {{2, 8}}}},
8126 {{{{5, 9}}, {{7, 9}}, {{1, 9}}, {{3, 9}}}},
8127 {{{{4, 10}}, {{5, 10}}, {{0, 10}}, {{1, 10}}}},
8128 {{{{6, 11}}, {{7, 11}}, {{2, 11}}, {{3, 11}}}},
8129 {{{{2, 12}}, {{3, 12}}, {{0, 12}}, {{1, 12}}}},
8130 {{{{6, 13}}, {{7, 13}}, {{4, 13}}, {{5, 13}}}}}};
8131
8132 std::vector<Point<spacedim>> vertices;
8133 std::vector<CellData<dim>> cells;
8134 SubCellData subcell_data;
8135
8136 // store for each vertex and face the assigned index so that we only
8137 // assign them a value once
8138 std::vector<unsigned int> old_to_new_vertex_indices(
8140 std::vector<unsigned int> face_to_new_vertex_indices(
8142
8143 // We first have to create all of the new vertices. To do this, we loop over
8144 // all cells and on each cell
8145 // (i) copy the existing vertex locations (and record their new indices in
8146 // the 'old_to_new_vertex_indices' vector),
8147 // (ii) create new midpoint vertex locations for each face (and record their
8148 // new indices in the 'face_to_new_vertex_indices' vector),
8149 // (iii) create new midpoint vertex locations for each cell (dim = 2 only)
8150 for (const auto &cell : ref_tria.cell_iterators())
8151 {
8152 // temporary array storing the global indices of each cell entity in the
8153 // sequence: vertices, edges/faces, cell
8154 std::array<unsigned int, dim == 2 ? 9 : 14> local_vertex_indices;
8155
8156 // (i) copy the existing vertex locations
8157 for (const auto v : cell->vertex_indices())
8158 {
8159 const auto v_global = cell->vertex_index(v);
8160
8161 if (old_to_new_vertex_indices[v_global] ==
8163 {
8164 old_to_new_vertex_indices[v_global] = vertices.size();
8165 vertices.push_back(cell->vertex(v));
8166 }
8167
8168 AssertIndexRange(v, local_vertex_indices.size());
8169 local_vertex_indices[v] = old_to_new_vertex_indices[v_global];
8170 }
8171
8172 // (ii) create new midpoint vertex locations for each face
8173 for (const auto f : cell->face_indices())
8174 {
8175 const auto f_global = cell->face_index(f);
8176
8177 if (face_to_new_vertex_indices[f_global] ==
8179 {
8180 face_to_new_vertex_indices[f_global] = vertices.size();
8181 vertices.push_back(
8182 cell->face(f)->center(/*respect_manifold*/ true));
8183 }
8184
8185 AssertIndexRange(cell->n_vertices() + f,
8186 local_vertex_indices.size());
8187 local_vertex_indices[cell->n_vertices() + f] =
8188 face_to_new_vertex_indices[f_global];
8189 }
8190
8191 // (iii) create new midpoint vertex locations for each cell
8192 if (dim == 2)
8193 {
8194 AssertIndexRange(cell->n_vertices() + cell->n_faces(),
8195 local_vertex_indices.size());
8196 local_vertex_indices[cell->n_vertices() + cell->n_faces()] =
8197 vertices.size();
8198 vertices.push_back(cell->center(/*respect_manifold*/ true));
8199 }
8200
8201 // helper function for creating cells and subcells
8202 const auto add_cell = [&](const unsigned int struct_dim,
8203 const auto &index_vertices,
8204 const unsigned int material_or_boundary_id,
8205 const unsigned int manifold_id = 0) {
8206 // sub-cell data only has to be stored if the information differs
8207 // from the default
8208 if (struct_dim < dim &&
8209 (material_or_boundary_id == numbers::internal_face_boundary_id &&
8210 manifold_id == numbers::flat_manifold_id))
8211 return;
8212
8213 if (struct_dim == dim) // cells
8214 {
8215 AssertDimension(index_vertices.size(), dim + 1);
8216
8217 CellData<dim> cell_data(index_vertices.size());
8218 cell_data.material_id =
8219 material_or_boundary_id; // inherit material id
8220 cell_data.manifold_id = manifold_id; // inherit cell-manifold id
8221 for (unsigned int i = 0; i < index_vertices.size(); ++i)
8222 {
8223 AssertIndexRange(index_vertices[i],
8224 local_vertex_indices.size());
8225 cell_data.vertices[i] =
8226 local_vertex_indices[index_vertices[i]];
8227 }
8228 cells.push_back(cell_data);
8229 }
8230 else if (dim == 2 && struct_dim == 1) // an edge of a simplex
8231 {
8232 Assert(index_vertices.size() == 2, ExcInternalError());
8233 CellData<1> boundary_line(2);
8234 boundary_line.boundary_id = material_or_boundary_id;
8235 boundary_line.manifold_id = manifold_id;
8236 for (unsigned int i = 0; i < index_vertices.size(); ++i)
8237 {
8238 AssertIndexRange(index_vertices[i],
8239 local_vertex_indices.size());
8240 boundary_line.vertices[i] =
8241 local_vertex_indices[index_vertices[i]];
8242 }
8243 subcell_data.boundary_lines.push_back(boundary_line);
8244 }
8245 else if (dim == 3 && struct_dim == 2) // a face of a tetrahedron
8246 {
8247 Assert(index_vertices.size() == 3, ExcInternalError());
8248 CellData<2> boundary_quad(3);
8249 boundary_quad.material_id = material_or_boundary_id;
8250 boundary_quad.manifold_id = manifold_id;
8251 for (unsigned int i = 0; i < index_vertices.size(); ++i)
8252 {
8253 AssertIndexRange(index_vertices[i],
8254 local_vertex_indices.size());
8255 boundary_quad.vertices[i] =
8256 local_vertex_indices[index_vertices[i]];
8257 }
8258 subcell_data.boundary_quads.push_back(boundary_quad);
8259 }
8260 else if (dim == 3 && struct_dim == 1) // an edge of a tetrahedron
8261 {
8262 Assert(index_vertices.size() == 2, ExcInternalError());
8263 CellData<1> boundary_line(2);
8264 boundary_line.boundary_id = material_or_boundary_id;
8265 boundary_line.manifold_id = manifold_id;
8266 for (unsigned int i = 0; i < index_vertices.size(); ++i)
8267 {
8268 AssertIndexRange(index_vertices[i],
8269 local_vertex_indices.size());
8270 boundary_line.vertices[i] =
8271 local_vertex_indices[index_vertices[i]];
8272 }
8273 subcell_data.boundary_lines.push_back(boundary_line);
8274 }
8275 else
8276 {
8278 }
8279 };
8280
8281 const auto material_id_cell = cell->material_id();
8282
8283 // create cells one by one
8284 if (dim == 2)
8285 {
8286 // get cell-manifold id from current quad cell
8287 const auto manifold_id_cell = cell->manifold_id();
8288 // inherit cell manifold
8289 for (const auto &cell_vertices : table_2D_cell)
8290 add_cell(dim, cell_vertices, material_id_cell, manifold_id_cell);
8291
8292 // inherit inner manifold (faces)
8293 for (const auto &face_vertices : vertex_ids_for_inner_faces_2d)
8294 // set inner tri-faces according to cell-manifold of quad
8295 // element, set invalid b_id, since we do not want to modify
8296 // b_id inside
8297 add_cell(1,
8298 face_vertices,
8300 manifold_id_cell);
8301 }
8302 else if (dim == 3)
8303 {
8304 // get cell-manifold id from current quad cell
8305 const auto manifold_id_cell = cell->manifold_id();
8306 // inherit cell manifold
8307 for (const auto &cell_vertices : vertex_ids_for_cells_3d)
8308 add_cell(dim, cell_vertices, material_id_cell, manifold_id_cell);
8309
8310 // set manifold of inner FACES of tets according to
8311 // hex-cell-manifold
8312 for (const auto &face_vertices : vertex_ids_for_inner_faces_3d)
8313 add_cell(2,
8314 face_vertices,
8316 manifold_id_cell);
8317
8318 // set manifold of inner EDGES of tets according to
8319 // hex-cell-manifold
8320 for (const auto &edge_vertices : vertex_ids_for_inner_edges_3d)
8321 add_cell(1,
8322 edge_vertices,
8324 manifold_id_cell);
8325 }
8326 else
8328
8329 // Set up sub-cell data.
8330 for (const auto f : cell->face_indices())
8331 {
8332 const auto bid = cell->face(f)->boundary_id();
8333 const auto mid = cell->face(f)->manifold_id();
8334
8335 // process boundary-faces: set boundary and manifold ids
8336 if (dim == 2) // 2d boundary-faces
8337 {
8338 for (const auto &face_vertices :
8339 vertex_ids_for_boundary_faces_2d[f])
8340 add_cell(1, face_vertices, bid, mid);
8341 }
8342 else if (dim == 3) // 3d boundary-faces
8343 {
8344 // set manifold ids of tet-boundary-faces according to
8345 // hex-boundary-faces
8346 for (const auto &face_vertices :
8347 vertex_ids_for_boundary_faces_3d[f])
8348 add_cell(2, face_vertices, bid, mid);
8349 // set manifold ids of new tet-boundary-edges according to
8350 // hex-boundary-faces
8351 for (const auto &edge_vertices :
8352 vertex_ids_for_new_boundary_edges_3d[f])
8353 add_cell(1, edge_vertices, bid, mid);
8354 }
8355 else
8357 }
8358
8359 // set manifold ids of edges that were already present in the
8360 // triangulation.
8361 if (dim == 3)
8362 {
8363 for (const auto e : cell->line_indices())
8364 {
8365 auto edge = cell->line(e);
8366 // Rather than use add_cell(), which does additional index
8367 // translation, just add edges directly into subcell_data since
8368 // we already know the correct global vertex indices.
8369 CellData<1> edge_data;
8370 edge_data.vertices[0] =
8371 old_to_new_vertex_indices[edge->vertex_index(0)];
8372 edge_data.vertices[1] =
8373 old_to_new_vertex_indices[edge->vertex_index(1)];
8374 edge_data.boundary_id = edge->boundary_id();
8375 edge_data.manifold_id = edge->manifold_id();
8376
8377 subcell_data.boundary_lines.push_back(std::move(edge_data));
8378 }
8379 }
8380 }
8381
8382 out_tria.clear();
8383 out_tria.create_triangulation(vertices, cells, subcell_data);
8384
8385 for (const auto i : out_tria.get_manifold_ids())
8386 if (i != numbers::flat_manifold_id)
8387 out_tria.set_manifold(i, FlatManifold<dim, spacedim>());
8388 }
8389
8390
8391
8392 template <int spacedim>
8393 void
8396 {
8397 out_tria.copy_triangulation(in_tria);
8398 return;
8399 }
8400
8401
8402
8403 template <int dim, int spacedim>
8404 void
8407 {
8409 if (in_tria.n_global_levels() > 1)
8410 {
8412 GridGenerator::flatten_triangulation(in_tria, temp_tria);
8413 }
8414 const Triangulation<dim, spacedim> &ref_tria =
8415 in_tria.n_global_levels() > 1 ? temp_tria : in_tria;
8416
8417 // Three triangles connecting to barycenter with vertex index 3:
8418 static const ndarray<unsigned int, 3, 3> table_2D_cell = {
8419 {{{0, 1, 3}}, {{1, 2, 3}}, {{2, 0, 3}}}};
8420
8421 // Boundary-faces 2d:
8422 // Each face of the original simplex is defined by the following vertices:
8424 vertex_ids_for_boundary_faces_2d = {
8425 {{{{{0, 1}}}}, {{{{1, 2}}}}, {{{{2, 0}}}}}};
8426
8427 // Three tetrahedra connecting to barycenter with vertex index 4:
8428 static const ndarray<unsigned int, 4, 4> table_3D_cell = {
8429 {{{0, 1, 2, 4}}, {{1, 0, 3, 4}}, {{0, 2, 3, 4}}, {{2, 1, 3, 4}}}};
8430
8431 // Boundary-faces 3d:
8432 // Each face of the original simplex is defined by the following vertices:
8434 vertex_ids_for_boundary_faces_3d = {
8435 {{{{{0, 1, 2}}}}, {{{{1, 0, 3}}}}, {{{{0, 2, 3}}}}, {{{{2, 1, 3}}}}}};
8436
8437 // Boundary-lines 3d:
8438 // Each line/edge of the original simplex is defined by the following
8439 // vertices:
8441 vertex_ids_for_boundary_lines_3d = {{{{{{0, 1}}}},
8442 {{{{1, 2}}}},
8443 {{{{2, 0}}}},
8444 {{{{0, 3}}}},
8445 {{{{1, 3}}}},
8446 {{{{2, 3}}}}}};
8447
8448 std::vector<Point<spacedim>> vertices;
8449 std::vector<CellData<dim>> cells;
8450 SubCellData subcell_data;
8451
8452 // for each vertex we store the assigned index so that we only
8453 // assign them a value once
8454 std::vector<unsigned int> old_to_new_vertex_indices(
8456
8457 // We first have to create all of the new vertices. To do this, we loop over
8458 // all cells and on each cell
8459 // (i) copy the existing vertex locations (and record their new indices in
8460 // the 'old_to_new_vertex_indices' vector),
8461 // (ii) create new barycenter vertex location
8462 for (const auto &cell : ref_tria.cell_iterators())
8463 {
8465 cell->reference_cell().is_simplex(),
8466 ExcMessage(
8467 "Cell with invalid ReferenceCell encountered. GridGenerator::alfeld_split_of_simplex_mesh() "
8468 "only supports simplex meshes as input."));
8469
8470 // temporary array storing the global indices of each cell entity in the
8471 // sequence: vertices, edges/faces, cell
8472 std::array<unsigned int, (dim == 3) ? 5 : 4> local_vertex_indices;
8473
8474 // (i) copy the existing vertex locations
8475 Point<spacedim> barycenter;
8476 for (const auto v : cell->vertex_indices())
8477 {
8478 const auto v_global = cell->vertex_index(v);
8479
8480 if (old_to_new_vertex_indices[v_global] ==
8482 {
8483 old_to_new_vertex_indices[v_global] = vertices.size();
8484 vertices.push_back(cell->vertex(v));
8485 }
8486
8487 AssertIndexRange(v, local_vertex_indices.size());
8488 local_vertex_indices[v] = old_to_new_vertex_indices[v_global];
8489
8490 barycenter += vertices[local_vertex_indices[v]] - Point<spacedim>();
8491 }
8492
8493 // (ii) barycenter:
8494 local_vertex_indices[local_vertex_indices.size() - 1] = vertices.size();
8495 vertices.push_back(barycenter / static_cast<double>(dim + 1));
8496
8497 // helper function for creating cells and subcells
8498 const auto add_cell = [&](const unsigned int struct_dim,
8499 const auto &index_vertices,
8500 const unsigned int material_or_boundary_id,
8501 const unsigned int manifold_id = 0) {
8502 // sub-cell data only has to be stored if the information differs
8503 // from the default
8504 if (struct_dim < dim &&
8505 (material_or_boundary_id == numbers::internal_face_boundary_id &&
8506 manifold_id == numbers::flat_manifold_id))
8507 return;
8508
8509 if (struct_dim == dim) // cells
8510 {
8511 AssertDimension(index_vertices.size(), dim + 1);
8512
8513 CellData<dim> cell_data(index_vertices.size());
8514 for (unsigned int i = 0; i < index_vertices.size(); ++i)
8515 {
8516 AssertIndexRange(index_vertices[i],
8517 local_vertex_indices.size());
8518 cell_data.vertices[i] =
8519 local_vertex_indices[index_vertices[i]];
8520 }
8521 cell_data.material_id =
8522 material_or_boundary_id; // inherit material id
8523 cell_data.manifold_id = manifold_id; // inherit cell-manifold id
8524 cells.push_back(cell_data);
8525 }
8526 else if (dim == 2 && struct_dim == 1) // an edge of a simplex
8527 {
8528 Assert(index_vertices.size() == 2, ExcInternalError());
8529 CellData<1> boundary_line(2);
8530 boundary_line.boundary_id = material_or_boundary_id;
8531 boundary_line.manifold_id = manifold_id;
8532 for (unsigned int i = 0; i < index_vertices.size(); ++i)
8533 {
8534 AssertIndexRange(index_vertices[i],
8535 local_vertex_indices.size());
8536 boundary_line.vertices[i] =
8537 local_vertex_indices[index_vertices[i]];
8538 }
8539 subcell_data.boundary_lines.push_back(boundary_line);
8540 }
8541 else if (dim == 3 && struct_dim == 2) // a face of a tetrahedron
8542 {
8543 Assert(index_vertices.size() == 3, ExcInternalError());
8544 CellData<2> boundary_quad(3);
8545 boundary_quad.material_id = material_or_boundary_id;
8546 boundary_quad.manifold_id = manifold_id;
8547 for (unsigned int i = 0; i < index_vertices.size(); ++i)
8548 {
8549 AssertIndexRange(index_vertices[i],
8550 local_vertex_indices.size());
8551 boundary_quad.vertices[i] =
8552 local_vertex_indices[index_vertices[i]];
8553 }
8554 subcell_data.boundary_quads.push_back(boundary_quad);
8555 }
8556 else if (dim == 3 && struct_dim == 1) // an edge of a tetrahedron
8557 {
8558 Assert(index_vertices.size() == 2, ExcInternalError());
8559 CellData<1> boundary_line(2);
8560 boundary_line.boundary_id = material_or_boundary_id;
8561 boundary_line.manifold_id = manifold_id;
8562 for (unsigned int i = 0; i < index_vertices.size(); ++i)
8563 {
8564 AssertIndexRange(index_vertices[i],
8565 local_vertex_indices.size());
8566 boundary_line.vertices[i] =
8567 local_vertex_indices[index_vertices[i]];
8568 }
8569 subcell_data.boundary_lines.push_back(boundary_line);
8570 }
8571 else
8572 {
8574 }
8575 };
8576
8577 const auto material_id_cell = cell->material_id();
8578 const auto manifold_id_cell = cell->manifold_id();
8579
8580 // create cells one by one
8581 if (dim == 2)
8582 {
8583 for (const auto &cell_vertices : table_2D_cell)
8584 add_cell(dim, cell_vertices, material_id_cell, manifold_id_cell);
8585 }
8586 else if (dim == 3)
8587 {
8588 for (const auto &cell_vertices : table_3D_cell)
8589 add_cell(dim, cell_vertices, material_id_cell, manifold_id_cell);
8590 }
8591 else
8593
8594 // Set up sub-cell data.
8595 for (const auto f : cell->face_indices())
8596 {
8597 const auto bid = cell->face(f)->boundary_id();
8598 const auto mid = cell->face(f)->manifold_id();
8599
8600 // process boundary-faces: set boundary and manifold ids
8601 if (dim == 2) // 2d boundary-faces
8602 {
8603 for (const auto &face_vertices :
8604 vertex_ids_for_boundary_faces_2d[f])
8605 add_cell(1, face_vertices, bid, mid);
8606 }
8607 else if (dim == 3)
8608 {
8609 for (const auto &face_vertices :
8610 vertex_ids_for_boundary_faces_3d[f])
8611 add_cell(2, face_vertices, bid, mid);
8612 }
8613 else
8615 }
8616
8617 // In 3D we need to treat boundary lines separately.
8618 if (dim == 3)
8619 {
8620 for (const auto l : cell->line_indices())
8621 {
8622 const auto bid = cell->line(l)->boundary_id();
8623 const auto mid = cell->line(l)->manifold_id();
8624
8625 for (const auto &line_vertices :
8626 vertex_ids_for_boundary_lines_3d[l])
8627 add_cell(1, line_vertices, bid, mid);
8628 }
8629 }
8630 }
8631
8632 out_tria.clear();
8633 out_tria.create_triangulation(vertices, cells, subcell_data);
8634
8635 for (const auto i : out_tria.get_manifold_ids())
8636 if (i != numbers::flat_manifold_id)
8637 out_tria.set_manifold(i, FlatManifold<dim, spacedim>());
8638 }
8639
8640
8641
8642 template <template <int, int> class MeshType, int dim, int spacedim>
8644 (concepts::is_triangulation_or_dof_handler<MeshType<dim, spacedim>>))
8645# ifndef _MSC_VER
8646 std::map<typename MeshType<dim - 1, spacedim>::cell_iterator,
8647 typename MeshType<dim, spacedim>::face_iterator>
8648# else
8649 typename ExtractBoundaryMesh<MeshType, dim, spacedim>::return_type
8650# endif
8651 extract_boundary_mesh(const MeshType<dim, spacedim> &volume_mesh,
8652 MeshType<dim - 1, spacedim> &surface_mesh,
8653 const std::set<types::boundary_id> &boundary_ids)
8654 {
8655 Assert((dynamic_cast<
8657 &volume_mesh.get_triangulation()) == nullptr),
8659
8660 // This function works using the following assumption:
8661 // Triangulation::create_triangulation(...) will create cells that
8662 // preserve the order of cells passed in using the CellData argument;
8663 // also, that it will not reorder the vertices.
8664
8665 // dimension of the boundary mesh
8666 const unsigned int boundary_dim = dim - 1;
8667
8668 // temporary map for level==0
8669 // iterator to face is stored along with face number
8670 // (this is required by the algorithm to adjust the normals of the
8671 // cells of the boundary mesh)
8672 std::vector<
8673 std::pair<typename MeshType<dim, spacedim>::face_iterator, unsigned int>>
8674 temporary_mapping_level0;
8675
8676 // vector indicating whether a vertex of the volume mesh has
8677 // already been visited (necessary to avoid duplicate vertices in
8678 // boundary mesh)
8679 std::vector<bool> touched(volume_mesh.get_triangulation().n_vertices(),
8680 false);
8681
8682 // data structures required for creation of boundary mesh
8683 std::vector<CellData<boundary_dim>> cells;
8684 SubCellData subcell_data;
8685 std::vector<Point<spacedim>> vertices;
8686
8687 // volume vertex indices to surf ones
8688 std::map<unsigned int, unsigned int> map_vert_index;
8689
8690 // define swapping of vertices to get proper normal orientation of boundary
8691 // mesh;
8692 // the entry (i,j) of swap_matrix stores the index of the vertex of
8693 // the boundary cell corresponding to the j-th vertex on the i-th face
8694 // of the underlying volume cell
8695 // if e.g. face 3 of a volume cell is considered and vertices 1 and 2 of the
8696 // corresponding boundary cell are swapped to get
8697 // proper normal orientation, swap_matrix[3]=( 0, 2, 1, 3 )
8698 Table<2, unsigned int> swap_matrix(
8701 for (unsigned int i1 = 0; i1 < GeometryInfo<spacedim>::faces_per_cell; ++i1)
8702 {
8703 for (unsigned int i2 = 0; i2 < GeometryInfo<dim - 1>::vertices_per_cell;
8704 i2++)
8705 swap_matrix[i1][i2] = i2;
8706 }
8707 // vertex swapping such that normals on the surface mesh point out of the
8708 // underlying volume
8709 if (dim == 3)
8710 {
8711 std::swap(swap_matrix[0][1], swap_matrix[0][2]);
8712 std::swap(swap_matrix[2][1], swap_matrix[2][2]);
8713 std::swap(swap_matrix[4][1], swap_matrix[4][2]);
8714 }
8715 else if (dim == 2)
8716 {
8717 std::swap(swap_matrix[1][0], swap_matrix[1][1]);
8718 std::swap(swap_matrix[2][0], swap_matrix[2][1]);
8719 }
8720
8721 // Create boundary mesh and mapping
8722 // from only level(0) cells of volume_mesh
8723 for (typename MeshType<dim, spacedim>::cell_iterator cell =
8724 volume_mesh.begin(0);
8725 cell != volume_mesh.end(0);
8726 ++cell)
8727 for (const unsigned int i : GeometryInfo<dim>::face_indices())
8728 {
8729 const typename MeshType<dim, spacedim>::face_iterator face =
8730 cell->face(i);
8731
8732 if (face->at_boundary() &&
8733 (boundary_ids.empty() ||
8734 (boundary_ids.find(face->boundary_id()) != boundary_ids.end())))
8735 {
8737
8738 for (const unsigned int j :
8739 GeometryInfo<boundary_dim>::vertex_indices())
8740 {
8741 const unsigned int v_index = face->vertex_index(j);
8742
8743 if (!touched[v_index])
8744 {
8745 vertices.push_back(face->vertex(j));
8746 map_vert_index[v_index] = vertices.size() - 1;
8747 touched[v_index] = true;
8748 }
8749
8750 c_data.vertices[swap_matrix[i][j]] = map_vert_index[v_index];
8751 }
8752 c_data.material_id =
8753 static_cast<types::material_id>(face->boundary_id());
8754 c_data.manifold_id = face->manifold_id();
8755
8756
8757 // in 3d, we need to make sure we copy the manifold
8758 // indicators from the edges of the volume mesh to the
8759 // edges of the surface mesh
8760 //
8761 // we set default boundary ids for boundary lines
8762 // and numbers::internal_face_boundary_id for internal lines
8763 if (dim == 3)
8764 for (unsigned int e = 0; e < 4; ++e)
8765 {
8766 // see if we already saw this edge from a
8767 // neighboring face, either in this or the reverse
8768 // orientation. if so, skip it.
8769 {
8770 bool edge_found = false;
8771 for (auto &boundary_line : subcell_data.boundary_lines)
8772 if (((boundary_line.vertices[0] ==
8773 map_vert_index[face->line(e)->vertex_index(0)]) &&
8774 (boundary_line.vertices[1] ==
8775 map_vert_index[face->line(e)->vertex_index(
8776 1)])) ||
8777 ((boundary_line.vertices[0] ==
8778 map_vert_index[face->line(e)->vertex_index(1)]) &&
8779 (boundary_line.vertices[1] ==
8780 map_vert_index[face->line(e)->vertex_index(0)])))
8781 {
8782 boundary_line.boundary_id =
8784 edge_found = true;
8785 break;
8786 }
8787 if (edge_found == true)
8788 // try next edge of current face
8789 continue;
8790 }
8791
8792 CellData<1> edge;
8793 edge.vertices[0] =
8794 map_vert_index[face->line(e)->vertex_index(0)];
8795 edge.vertices[1] =
8796 map_vert_index[face->line(e)->vertex_index(1)];
8797 edge.boundary_id = 0;
8798 edge.manifold_id = face->line(e)->manifold_id();
8799
8800 subcell_data.boundary_lines.push_back(edge);
8801 }
8802
8803 cells.push_back(c_data);
8804 temporary_mapping_level0.push_back(std::make_pair(face, i));
8805 }
8806 }
8807
8808 // create level 0 surface triangulation
8809 Assert(cells.size() > 0, ExcMessage("No boundary faces selected"));
8810 const_cast<Triangulation<dim - 1, spacedim> &>(
8811 surface_mesh.get_triangulation())
8812 .create_triangulation(vertices, cells, subcell_data);
8813
8814 // in 2d: set default boundary ids for "boundary vertices"
8815 if (dim == 2)
8816 {
8817 for (const auto &cell : surface_mesh.active_cell_iterators())
8818 for (unsigned int vertex = 0; vertex < 2; ++vertex)
8819 if (cell->face(vertex)->at_boundary())
8820 cell->face(vertex)->set_boundary_id(0);
8821 }
8822
8823 // Make mapping for level 0
8824
8825 // temporary map between cells on the boundary and corresponding faces of
8826 // domain mesh (each face is characterized by an iterator to the face and
8827 // the face number within the underlying cell)
8828 std::vector<std::pair<
8829 const typename MeshType<dim - 1, spacedim>::cell_iterator,
8830 std::pair<typename MeshType<dim, spacedim>::face_iterator, unsigned int>>>
8831 temporary_map_boundary_cell_face;
8832 for (const auto &cell : surface_mesh.active_cell_iterators())
8833 temporary_map_boundary_cell_face.push_back(
8834 std::make_pair(cell, temporary_mapping_level0.at(cell->index())));
8835
8836
8837 // refine the boundary mesh according to the refinement of the underlying
8838 // volume mesh,
8839 // algorithm:
8840 // (1) check which cells on refinement level i need to be refined
8841 // (2) do refinement (yields cells on level i+1)
8842 // (3) repeat for the next level (i+1->i) until refinement is completed
8843
8844 // stores the index into temporary_map_boundary_cell_face at which
8845 // presently deepest refinement level of boundary mesh begins
8846 unsigned int index_cells_deepest_level = 0;
8847 do
8848 {
8849 bool changed = false;
8850
8851 // vector storing cells which have been marked for
8852 // refinement
8853 std::vector<unsigned int> cells_refined;
8854
8855 // loop over cells of presently deepest level of boundary triangulation
8856 for (unsigned int cell_n = index_cells_deepest_level;
8857 cell_n < temporary_map_boundary_cell_face.size();
8858 cell_n++)
8859 {
8860 // mark boundary cell for refinement if underlying volume face has
8861 // children
8862 if (temporary_map_boundary_cell_face[cell_n]
8863 .second.first->has_children())
8864 {
8865 // algorithm only works for
8866 // isotropic refinement!
8867 Assert(temporary_map_boundary_cell_face[cell_n]
8868 .second.first->refinement_case() ==
8871 temporary_map_boundary_cell_face[cell_n]
8872 .first->set_refine_flag();
8873 cells_refined.push_back(cell_n);
8874 changed = true;
8875 }
8876 }
8877
8878 // if cells have been marked for refinement (i.e., presently deepest
8879 // level is not the deepest level of the volume mesh)
8880 if (changed)
8881 {
8882 // do actual refinement
8883 const_cast<Triangulation<dim - 1, spacedim> &>(
8884 surface_mesh.get_triangulation())
8885 .execute_coarsening_and_refinement();
8886
8887 // add new level of cells to temporary_map_boundary_cell_face
8888 index_cells_deepest_level = temporary_map_boundary_cell_face.size();
8889 for (const auto &refined_cell_n : cells_refined)
8890 {
8891 const typename MeshType<dim - 1, spacedim>::cell_iterator
8892 refined_cell =
8893 temporary_map_boundary_cell_face[refined_cell_n].first;
8894 const typename MeshType<dim,
8895 spacedim>::face_iterator refined_face =
8896 temporary_map_boundary_cell_face[refined_cell_n].second.first;
8897 const unsigned int refined_face_number =
8898 temporary_map_boundary_cell_face[refined_cell_n]
8899 .second.second;
8900 for (unsigned int child_n = 0;
8901 child_n < refined_cell->n_children();
8902 ++child_n)
8903 // at this point, the swapping of vertices done earlier must
8904 // be taken into account to get the right association between
8905 // volume faces and boundary cells!
8906 temporary_map_boundary_cell_face.push_back(
8907 std::make_pair(refined_cell->child(
8908 swap_matrix[refined_face_number][child_n]),
8909 std::make_pair(refined_face->child(child_n),
8910 refined_face_number)));
8911 }
8912 }
8913 // we are at the deepest level of refinement of the volume mesh
8914 else
8915 break;
8916 }
8917 while (true);
8918
8919 // generate the final mapping from the temporary mapping
8920 std::map<typename MeshType<dim - 1, spacedim>::cell_iterator,
8921 typename MeshType<dim, spacedim>::face_iterator>
8922 surface_to_volume_mapping;
8923 for (unsigned int i = 0; i < temporary_map_boundary_cell_face.size(); ++i)
8924 surface_to_volume_mapping[temporary_map_boundary_cell_face[i].first] =
8925 temporary_map_boundary_cell_face[i].second.first;
8926
8927 // TODO: we attach flat manifolds here; one should attach submanifolds here
8928 const auto attached_mids =
8929 surface_mesh.get_triangulation().get_manifold_ids();
8930 for (const auto i : volume_mesh.get_triangulation().get_manifold_ids())
8931 if (i != numbers::flat_manifold_id &&
8932 std::find(attached_mids.begin(), attached_mids.end(), i) ==
8933 attached_mids.end())
8934 const_cast<Triangulation<dim - 1, spacedim> &>(
8935 surface_mesh.get_triangulation())
8936 .set_manifold(i, FlatManifold<dim - 1, spacedim>());
8937
8938 return surface_to_volume_mapping;
8939 }
8940
8941
8942
8943 template <int dim, int spacedim>
8944 void
8947 const std::vector<unsigned int> &repetitions,
8948 const Point<dim> &p1,
8949 const Point<dim> &p2,
8950 const bool colorize)
8951 {
8952 AssertDimension(dim, spacedim);
8953
8954 std::vector<Point<spacedim>> vertices;
8955 std::vector<CellData<dim>> cells;
8956
8957 if (dim == 2)
8958 {
8959 // determine cell sizes
8960 const Point<dim> dx((p2[0] - p1[0]) / repetitions[0],
8961 (p2[1] - p1[1]) / repetitions[1]);
8962
8963 // create vertices
8964 for (unsigned int j = 0; j <= repetitions[1]; ++j)
8965 for (unsigned int i = 0; i <= repetitions[0]; ++i)
8966 vertices.push_back(
8967 Point<spacedim>(p1[0] + dx[0] * i, p1[1] + dx[1] * j));
8968
8969 // create cells
8970 for (unsigned int j = 0; j < repetitions[1]; ++j)
8971 for (unsigned int i = 0; i < repetitions[0]; ++i)
8972 {
8973 // create reference QUAD cell
8974 std::array<unsigned int, 4> quad{{
8975 (j + 0) * (repetitions[0] + 1) + i + 0, //
8976 (j + 0) * (repetitions[0] + 1) + i + 1, //
8977 (j + 1) * (repetitions[0] + 1) + i + 0, //
8978 (j + 1) * (repetitions[0] + 1) + i + 1 //
8979 }}; //
8980
8981 // TRI cell 0
8982 {
8983 CellData<dim> tri;
8984 tri.vertices = {quad[0], quad[1], quad[2]};
8985 cells.push_back(tri);
8986 }
8987
8988 // TRI cell 1
8989 {
8990 CellData<dim> tri;
8991 tri.vertices = {quad[3], quad[2], quad[1]};
8992 cells.push_back(tri);
8993 }
8994 }
8995 }
8996 else if (dim == 3)
8997 {
8998 // determine cell sizes
8999 const Point<dim> dx((p2[0] - p1[0]) / repetitions[0],
9000 (p2[1] - p1[1]) / repetitions[1],
9001 (p2[2] - p1[2]) / repetitions[2]);
9002
9003 // create vertices
9004 for (unsigned int k = 0; k <= repetitions[2]; ++k)
9005 for (unsigned int j = 0; j <= repetitions[1]; ++j)
9006 for (unsigned int i = 0; i <= repetitions[0]; ++i)
9007 vertices.push_back(Point<spacedim>(p1[0] + dx[0] * i,
9008 p1[1] + dx[1] * j,
9009 p1[2] + dx[2] * k));
9010
9011 // create cells
9012 for (unsigned int k = 0; k < repetitions[2]; ++k)
9013 for (unsigned int j = 0; j < repetitions[1]; ++j)
9014 for (unsigned int i = 0; i < repetitions[0]; ++i)
9015 {
9016 // create reference HEX cell
9017 std::array<unsigned int, 8> quad{
9018 {(k + 0) * (repetitions[0] + 1) * (repetitions[1] + 1) +
9019 (j + 0) * (repetitions[0] + 1) + i + 0,
9020 (k + 0) * (repetitions[0] + 1) * (repetitions[1] + 1) +
9021 (j + 0) * (repetitions[0] + 1) + i + 1,
9022 (k + 0) * (repetitions[0] + 1) * (repetitions[1] + 1) +
9023 (j + 1) * (repetitions[0] + 1) + i + 0,
9024 (k + 0) * (repetitions[0] + 1) * (repetitions[1] + 1) +
9025 (j + 1) * (repetitions[0] + 1) + i + 1,
9026 (k + 1) * (repetitions[0] + 1) * (repetitions[1] + 1) +
9027 (j + 0) * (repetitions[0] + 1) + i + 0,
9028 (k + 1) * (repetitions[0] + 1) * (repetitions[1] + 1) +
9029 (j + 0) * (repetitions[0] + 1) + i + 1,
9030 (k + 1) * (repetitions[0] + 1) * (repetitions[1] + 1) +
9031 (j + 1) * (repetitions[0] + 1) + i + 0,
9032 (k + 1) * (repetitions[0] + 1) * (repetitions[1] + 1) +
9033 (j + 1) * (repetitions[0] + 1) + i + 1}};
9034
9035 // TET cell 0
9036 {
9037 CellData<dim> cell;
9038 if (((i % 2) + (j % 2) + (k % 2)) % 2 == 0)
9039 cell.vertices = {{quad[0], quad[1], quad[2], quad[4]}};
9040 else
9041 cell.vertices = {{quad[0], quad[1], quad[3], quad[5]}};
9042
9043 cells.push_back(cell);
9044 }
9045
9046 // TET cell 1
9047 {
9048 CellData<dim> cell;
9049 if (((i % 2) + (j % 2) + (k % 2)) % 2 == 0)
9050 cell.vertices = {{quad[2], quad[1], quad[3], quad[7]}};
9051 else
9052 cell.vertices = {{quad[0], quad[3], quad[2], quad[6]}};
9053 cells.push_back(cell);
9054 }
9055
9056 // TET cell 2
9057 {
9058 CellData<dim> cell;
9059 if (((i % 2) + (j % 2) + (k % 2)) % 2 == 0)
9060 cell.vertices = {{quad[1], quad[4], quad[5], quad[7]}};
9061 else
9062 cell.vertices = {{quad[0], quad[4], quad[5], quad[6]}};
9063 cells.push_back(cell);
9064 }
9065
9066 // TET cell 3
9067 {
9068 CellData<dim> cell;
9069 if (((i % 2) + (j % 2) + (k % 2)) % 2 == 0)
9070 cell.vertices = {{quad[2], quad[4], quad[7], quad[6]}};
9071 else
9072 cell.vertices = {{quad[3], quad[5], quad[7], quad[6]}};
9073 cells.push_back(cell);
9074 }
9075
9076 // TET cell 4
9077 {
9078 CellData<dim> cell;
9079 if (((i % 2) + (j % 2) + (k % 2)) % 2 == 0)
9080 cell.vertices = {{quad[1], quad[2], quad[4], quad[7]}};
9081 else
9082 cell.vertices = {{quad[0], quad[3], quad[6], quad[5]}};
9083 cells.push_back(cell);
9084 }
9085 }
9086 }
9087 else
9088 {
9090 }
9091
9092 // actually create triangulation
9093 tria.create_triangulation(vertices, cells, SubCellData());
9094
9095 if (colorize)
9096 {
9097 // to colorize, run through all
9098 // faces of all cells and set
9099 // boundary indicator to the
9100 // correct value if it was 0.
9101
9102 // use a large epsilon to
9103 // compare numbers to avoid
9104 // roundoff problems.
9105 double epsilon = std::numeric_limits<double>::max();
9106 for (unsigned int i = 0; i < dim; ++i)
9107 epsilon = std::min(epsilon,
9108 0.01 * (std::abs(p2[i] - p1[i]) / repetitions[i]));
9109 Assert(epsilon > 0,
9110 ExcMessage(
9111 "The distance between corner points must be positive."));
9112
9113 // actual code is external since
9114 // 1-D is different from 2/3d.
9115 colorize_subdivided_hyper_rectangle(tria, p1, p2, epsilon);
9116 }
9117 }
9118
9119
9120
9121 template <int dim, int spacedim>
9122 void
9124 const unsigned int repetitions,
9125 const double p1,
9126 const double p2,
9127 const bool colorize)
9128 {
9129 if (dim == 2)
9130 {
9132 tria, {{repetitions, repetitions}}, {p1, p1}, {p2, p2}, colorize);
9133 }
9134 else if (dim == 3)
9135 {
9137 tria,
9138 {{repetitions, repetitions, repetitions}},
9139 {p1, p1, p1},
9140 {p2, p2, p2},
9141 colorize);
9142 }
9143 else
9144 {
9146 }
9147 }
9148} // namespace GridGenerator
9149
9150// explicit instantiations
9151# include "grid/grid_generator.inst"
9152
9153#endif // DOXYGEN
9154
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 > center
numbers::NumberTraits< Number >::real_type norm() const
void initialize(const Triangulation< dim, spacedim > &triangulation)
virtual void add_periodicity(const std::vector< GridTools::PeriodicFacePair< cell_iterator > > &)
virtual types::global_cell_index n_global_active_cells() const
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:35
constexpr bool running_in_debug_mode()
Definition config.h:73
#define DEAL_II_CXX20_REQUIRES(condition)
Definition config.h:242
#define DEAL_II_NAMESPACE_CLOSE
Definition config.h:36
DerivativeForm< 1, spacedim, dim, Number > transpose(const DerivativeForm< 1, dim, spacedim, Number > &DF)
#define DEAL_II_ASSERT_UNREACHABLE()
#define DEAL_II_NOT_IMPLEMENTED()
Point< 2 > second
Definition grid_out.cc:4633
bool colorize
Definition grid_out.cc:4634
Point< 2 > first
Definition grid_out.cc:4632
unsigned int vertex_indices[2]
unsigned int cell_index
static ::ExceptionBase & ExcNotImplemented()
#define Assert(cond, exc)
#define AssertDimension(dim1, dim2)
static ::ExceptionBase & ExcLowerRange(int arg1, int arg2)
#define AssertIndexRange(index, range)
static ::ExceptionBase & ExcInternalError()
static ::ExceptionBase & ExcIndexRange(std::size_t arg1, std::size_t arg2, std::size_t arg3)
static ::ExceptionBase & ExcMessage(std::string arg1)
#define AssertThrow(cond, exc)
void set_all_manifold_ids_on_boundary(const types::manifold_id number)
const Manifold< dim, spacedim > & get_manifold(const types::manifold_id number) const
void copy_boundary_to_manifold_id(Triangulation< dim, spacedim > &tria, const bool reset_boundary_ids=false)
virtual std::vector< types::manifold_id > get_manifold_ids() const
void set_manifold(const types::manifold_id number, const Manifold< dim, spacedim > &manifold_object)
void reset_all_manifolds()
void set_all_manifold_ids(const types::manifold_id number)
void consistently_order_cells(std::vector< CellData< dim > > &cells)
const Mapping< dim, spacedim > & get_default_linear_mapping(const Triangulation< dim, spacedim > &triangulation)
Definition mapping.cc:316
std::vector< index_type > data
Definition mpi.cc:746
std::size_t size
Definition mpi.cc:745
CGAL::Exact_predicates_exact_constructions_kernel_with_sqrt K
Expression fabs(const Expression &x)
void interpolate(const DoFHandler< dim, spacedim > &dof1, const InVector &u1, const DoFHandler< dim, spacedim > &dof2, OutVector &u2)
void create_triangulation(Triangulation< dim, dim > &tria, const AdditionalData &additional_data=AdditionalData())
void 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 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 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 convert_hypercube_to_simplex_mesh(const Triangulation< dim, spacedim > &in_tria, Triangulation< dim, spacedim > &out_tria)
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 transform(const Transformation &transformation, Triangulation< dim, spacedim > &triangulation)
void shift(const Tensor< 1, spacedim > &shift_vector, 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 >())
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)
Tensor< 2, dim, Number > l(const Tensor< 2, dim, Number > &F, const Tensor< 2, dim, Number > &dF_dt)
SymmetricTensor< 2, dim, Number > b(const Tensor< 2, dim, Number > &F)
SymmetricTensor< 2, dim, Number > d(const Tensor< 2, dim, Number > &F, const Tensor< 2, dim, Number > &dF_dt)
SymmetricTensor< 2, dim, Number > epsilon(const Tensor< 2, dim, Number > &Grad_u)
Tensor< 2, dim, Number > F(const Tensor< 2, dim, Number > &Grad_u)
Tensor< 2, 2, Number > rotation_matrix_2d(const Number &angle)
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:14904
void copy(const T *begin, const T *end, U *dest)
constexpr double PI_2
Definition numbers.h:245
constexpr double E
Definition numbers.h:215
constexpr double PI
Definition numbers.h:240
constexpr unsigned int invalid_unsigned_int
Definition types.h: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
STL namespace.
::VectorizedArray< Number, width > tan(const ::VectorizedArray< Number, width > &)
inline ::VectorizedArray< Number, width > tanh(const ::VectorizedArray< Number, width > &x)
::VectorizedArray< Number, width > min(const ::VectorizedArray< Number, width > &, const ::VectorizedArray< Number, width > &)
::VectorizedArray< Number, width > max(const ::VectorizedArray< Number, width > &, const ::VectorizedArray< Number, width > &)
::VectorizedArray< Number, width > cos(const ::VectorizedArray< Number, width > &)
::VectorizedArray< Number, width > sin(const ::VectorizedArray< Number, width > &)
::VectorizedArray< Number, width > sqrt(const ::VectorizedArray< Number, width > &)
::VectorizedArray< Number, width > pow(const ::VectorizedArray< Number, width > &, const Number p)
inline ::VectorizedArray< Number, width > atan(const ::VectorizedArray< Number, width > &x)
::VectorizedArray< Number, width > abs(const ::VectorizedArray< Number, width > &)
unsigned int manifold_id
Definition types.h:173
unsigned int material_id
Definition types.h:184
unsigned int boundary_id
Definition types.h:161
typename internal::ndarray::HelperArray< T, Ns... >::type ndarray
Definition ndarray.h:107
const ::parallel::distributed::Triangulation< dim, spacedim > * triangulation
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)
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 > &)