Reference documentation for deal.II version 9.4.1
\(\newcommand{\dealvcentcolon}{\mathrel{\mathop{:}}}\) \(\newcommand{\dealcoloneq}{\dealvcentcolon\mathrel{\mkern-1.2mu}=}\) \(\newcommand{\jump}[1]{\left[\!\left[ #1 \right]\!\right]}\) \(\newcommand{\average}[1]{\left\{\!\left\{ #1 \right\}\!\right\}}\)
Loading...
Searching...
No Matches
grid_generator.cc
Go to the documentation of this file.
1// ---------------------------------------------------------------------
2//
3// Copyright (C) 1999 - 2022 by the deal.II authors
4//
5// This file is part of the deal.II library.
6//
7// The deal.II library is free software; you can use it, redistribute
8// it, and/or modify it under the terms of the GNU Lesser General
9// Public License as published by the Free Software Foundation; either
10// version 2.1 of the License, or (at your option) any later version.
11// The full text of the license can be found in the file LICENSE.md at
12// the top level directory of deal.II.
13//
14// ---------------------------------------------------------------------
15
17
21
28#include <deal.II/grid/tria.h>
31
33
34#include <array>
35#include <cmath>
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 for (unsigned int i = 0; i < number_points; ++i)
540 circle_points.emplace_back(center[0] - radius * cos(i * theta),
541 center[1] - radius * sin(i * theta));
542
543 return circle_points;
544 }
545
554 static std::vector<Point<2>>
555 joukowski_transform(const std::vector<Point<2>> &circle_points)
556 {
557 std::vector<Point<2>> joukowski_points(circle_points.size());
558
559 // transform each point
560 for (unsigned int i = 0; i < circle_points.size(); ++i)
561 {
562 const double chi = circle_points[i](0);
563 const double eta = circle_points[i](1);
564 const std::complex<double> zeta(chi, eta);
565 const std::complex<double> z = zeta + 1. / zeta;
566
567 joukowski_points[i] = {real(z), imag(z)};
568 }
569 return joukowski_points;
570 }
571
588 static std::array<std::vector<Point<2>>, 2>
589 naca(const std::string &serialnumber,
590 const unsigned int number_points,
591 const unsigned int factor)
592 {
593 // number of non_equidistant airfoilpoints among which will be
594 // interpolated
595 const unsigned int n_airfoilpoints = factor * number_points;
596
597 // create equidistant airfoil points for upper and lower side
598 return {{make_points_equidistant(
599 naca_create_points(serialnumber, n_airfoilpoints, true),
600 number_points),
601 make_points_equidistant(
602 naca_create_points(serialnumber, n_airfoilpoints, false),
603 number_points)}};
604 }
605
617 static std::vector<Point<2>>
618 naca_create_points(const std::string &serialnumber,
619 const unsigned int number_points,
620 const bool is_upper)
621 {
622 Assert(serialnumber.size() == 4,
623 ExcMessage("This NACA-serial number is not implemented!"));
624
625 return naca_create_points_4_digits(serialnumber,
626 number_points,
627 is_upper);
628 }
629
644 static std::vector<Point<2>>
645 naca_create_points_4_digits(const std::string &serialnumber,
646 const unsigned int number_points,
647 const bool is_upper)
648 {
649 // conversion string (char * ) to int
650 const unsigned int digit_0 = (serialnumber[0] - '0');
651 const unsigned int digit_1 = (serialnumber[1] - '0');
652 const unsigned int digit_2 = (serialnumber[2] - '0');
653 const unsigned int digit_3 = (serialnumber[3] - '0');
654
655 const unsigned int digit_23 = 10 * digit_2 + digit_3;
656
657 // maximum thickness in percentage of the cord
658 const double t = static_cast<double>(digit_23) / 100.0;
659
660 std::vector<Point<2>> naca_points;
661
662 if (digit_0 == 0 && digit_1 == 0) // is symmetric
663 for (unsigned int i = 0; i < number_points; ++i)
664 {
665 const double x = i * 1 / (1.0 * number_points - 1);
666 const double y_t =
667 5 * t *
668 (0.2969 * std::pow(x, 0.5) - 0.126 * x -
669 0.3516 * std::pow(x, 2) + 0.2843 * std::pow(x, 3) -
670 0.1036 * std::pow(x, 4)); // half thickness at a position x
671
672 if (is_upper)
673 naca_points.emplace_back(x, +y_t);
674 else
675 naca_points.emplace_back(x, -y_t);
676 }
677 else // is asymmetric
678 for (unsigned int i = 0; i < number_points; ++i)
679 {
680 const double m = 1.0 * digit_0 / 100; // max. chamber
681 const double p = 1.0 * digit_1 / 10; // location of max. chamber
682 const double x = i * 1 / (1.0 * number_points - 1);
683
684 const double y_c =
685 (x <= p) ? m / std::pow(p, 2) * (2 * p * x - std::pow(x, 2)) :
686 m / std::pow(1 - p, 2) *
687 ((1 - 2 * p) + 2 * p * x - std::pow(x, 2));
688
689 const double dy_c = (x <= p) ?
690 2 * m / std::pow(p, 2) * (p - x) :
691 2 * m / std::pow(1 - p, 2) * (p - x);
692
693 const double y_t =
694 5 * t *
695 (0.2969 * std::pow(x, 0.5) - 0.126 * x -
696 0.3516 * std::pow(x, 2) + 0.2843 * std::pow(x, 3) -
697 0.1036 * std::pow(x, 4)); // half thickness at a position x
698
699 const double theta = std::atan(dy_c);
700
701 if (is_upper)
702 naca_points.emplace_back(x - y_t * std::sin(theta),
703 y_c + y_t * std::cos(theta));
704 else
705 naca_points.emplace_back(x + y_t * std::sin(theta),
706 y_c - y_t * std::cos(theta));
707 }
708
709 return naca_points;
710 }
711
712
713
722 static std::array<std::vector<Point<2>>, 2>
723 set_airfoil_length(const std::array<std::vector<Point<2>>, 2> &input,
724 const double desired_len)
725 {
726 std::array<std::vector<Point<2>>, 2> output;
727 output[0] = set_airfoil_length(input[0], desired_len);
728 output[1] = set_airfoil_length(input[1], desired_len);
729
730 return output;
731 }
732
740 static std::vector<Point<2>>
741 set_airfoil_length(const std::vector<Point<2>> &input,
742 const double desired_len)
743 {
744 std::vector<Point<2>> output = input;
745
746 const double scale =
747 desired_len / input.front().distance(input.back());
748
749 for (auto &x : output)
750 x *= scale;
751
752 return output;
753 }
754
765 static std::vector<Point<2>>
766 make_points_equidistant(
767 const std::vector<Point<2>> &non_equidistant_points,
768 const unsigned int number_points)
769 {
770 const unsigned int n_points =
771 non_equidistant_points
772 .size(); // number provided airfoilpoints to interpolate
773
774 // calculate arclength
775 std::vector<double> arclength_L(non_equidistant_points.size(), 0);
776 for (unsigned int i = 0; i < non_equidistant_points.size() - 1; ++i)
777 arclength_L[i + 1] =
778 arclength_L[i] +
779 non_equidistant_points[i + 1].distance(non_equidistant_points[i]);
780
781
782 const auto airfoil_length =
783 arclength_L.back(); // arclength upper or lower side
784 const auto deltaX = airfoil_length / (number_points - 1);
785
786 // Create equidistant points: keep the first (and last) point
787 // unchanged
788 std::vector<Point<2>> equidist(
789 number_points); // number_points is required points on each side for
790 // mesh
791 equidist[0] = non_equidistant_points[0];
792 equidist[number_points - 1] = non_equidistant_points[n_points - 1];
793
794
795 // loop over all subsections
796 for (unsigned int j = 0, i = 1; j < n_points - 1; ++j)
797 {
798 // get reference left and right end of this section
799 const auto Lj = arclength_L[j];
800 const auto Ljp = arclength_L[j + 1];
801
802 while (Lj <= i * deltaX && i * deltaX <= Ljp &&
803 i < number_points - 1)
804 {
805 equidist[i] = Point<2>((i * deltaX - Lj) / (Ljp - Lj) *
806 (non_equidistant_points[j + 1] -
807 non_equidistant_points[j]) +
808 non_equidistant_points[j]);
809 ++i;
810 }
811 }
812 return equidist;
813 }
814
815
816
823 void
824 make_coarse_grid(Triangulation<2> &tria) const
825 {
826 // create vector of serial triangulations for each block and
827 // temporary storage for merging them
828 std::vector<Triangulation<2>> trias(10);
829
830 // helper function to create a subdivided quadrilateral
831 auto make = [](Triangulation<2> & tria,
832 const std::vector<Point<2>> & corner_vertices,
833 const std::vector<unsigned int> &repetitions,
834 const unsigned int material_id) {
835 // create subdivided rectangle with corner points (-1,-1)
836 // and (+1, +1). It serves as reference system
838 repetitions,
839 {-1, -1},
840 {+1, +1});
841
842 // move all vertices to the correct position
843 for (auto it = tria.begin_vertex(); it < tria.end_vertex(); ++it)
844 {
845 auto & point = it->vertex();
846 const double xi = point(0);
847 const double eta = point(1);
848
849 // bilinear mapping
850 point = 0.25 * ((1 - xi) * (1 - eta) * corner_vertices[0] +
851 (1 + xi) * (1 - eta) * corner_vertices[1] +
852 (1 - xi) * (1 + eta) * corner_vertices[2] +
853 (1 + xi) * (1 + eta) * corner_vertices[3]);
854 }
855
856 // set material id of block
857 for (auto cell : tria.active_cell_iterators())
858 cell->set_material_id(material_id);
859 };
860
861 // create a subdivided quadrilateral for each block (see last number
862 // of block id)
863 make(trias[0],
864 {A, B, D, C},
865 {n_subdivision_y, n_subdivision_x_0},
866 id_block_1);
867 make(trias[1],
868 {F, E, A, B},
869 {n_subdivision_y, n_subdivision_x_0},
870 id_block_4);
871 make(trias[2],
872 {C, H, D, G},
873 {n_subdivision_x_1, n_subdivision_y},
874 id_block_2);
875 make(trias[3],
876 {F, I, E, H},
877 {n_subdivision_x_1, n_subdivision_y},
878 id_block_5);
879 make(trias[4],
880 {H, J, G, K},
881 {n_subdivision_x_2, n_subdivision_y},
882 id_block_3);
883 make(trias[5],
884 {I, L, H, J},
885 {n_subdivision_x_2, n_subdivision_y},
886 id_block_6);
887
888
889 // merge triangulation (warning: do not change the order here since
890 // this might change the face ids)
891 GridGenerator::merge_triangulations(trias[0], trias[1], trias[6]);
892 GridGenerator::merge_triangulations(trias[2], trias[3], trias[7]);
893 GridGenerator::merge_triangulations(trias[4], trias[5], trias[8]);
894 GridGenerator::merge_triangulations(trias[6], trias[7], trias[9]);
895 GridGenerator::merge_triangulations(trias[8], trias[9], tria);
896 }
897
898 /*
899 * Loop over all (cells and) boundary faces of a given triangulation
900 * and set the boundary_ids depending on the material_id of the cell and
901 * the face number. The resulting boundary_ids are:
902 * - 0: inlet
903 * - 1: outlet
904 * - 2: upper airfoil surface (aka. suction side)
905 * - 3, lower airfoil surface (aka. pressure side),
906 * - 4: upper far-field side
907 * - 5: lower far-field side
908 */
909 static void
910 set_boundary_ids(Triangulation<2> &tria)
911 {
912 for (auto cell : tria.active_cell_iterators())
913 for (unsigned int f : GeometryInfo<2>::face_indices())
914 {
915 if (cell->face(f)->at_boundary() == false)
916 continue;
917
918 const auto mid = cell->material_id();
919
920 if ((mid == id_block_1 && f == 0) ||
921 (mid == id_block_4 && f == 0))
922 cell->face(f)->set_boundary_id(0); // inlet
923 else if ((mid == id_block_3 && f == 0) ||
924 (mid == id_block_6 && f == 2))
925 cell->face(f)->set_boundary_id(1); // outlet
926 else if ((mid == id_block_1 && f == 1) ||
927 (mid == id_block_2 && f == 1))
928 cell->face(f)->set_boundary_id(2); // upper airfoil side
929 else if ((mid == id_block_4 && f == 1) ||
930 (mid == id_block_5 && f == 3))
931 cell->face(f)->set_boundary_id(3); // lower airfoil side
932 else if ((mid == id_block_2 && f == 0) ||
933 (mid == id_block_3 && f == 2))
934 cell->face(f)->set_boundary_id(4); // upper far-field side
935 else if ((mid == id_block_5 && f == 2) ||
936 (mid == id_block_6 && f == 0))
937 cell->face(f)->set_boundary_id(5); // lower far-field side
938 else
939 Assert(false, ExcIndexRange(mid, id_block_1, id_block_6));
940 }
941 }
942
943 /*
944 * Interpolate all vertices of the given triangulation onto the airfoil
945 * geometry, depending on the material_id of the block.
946 * Due to symmetry of coarse grid in respect to
947 * x-axis (by definition of points A-L), blocks 1&4, 2&4 and 3&6 can be
948 * interpolated with the same geometric computations Consider a
949 * bias_factor and incline_factor during interpolation to obtain a more
950 * dense mesh next to airfoil geometry and receive an inclined boundary
951 * between block 2&3 and 5&6, respectively
952 */
953 void
955 {
956 // array storing the information if a vertex was processed
957 std::vector<bool> vertex_processed(tria.n_vertices(), false);
958
959 // rotation matrix for clockwise rotation of block 1 by angle gamma
960 const Tensor<2, 2, double> rotation_matrix_1 =
962 const Tensor<2, 2, double> rotation_matrix_2 =
963 transpose(rotation_matrix_1);
964
965 // horizontal offset in order to place coarse-grid node A in the
966 // origin
967 const Point<2, double> horizontal_offset(A(0), 0.0);
968
969 // Move block 1 so that face BC coincides the x-axis
970 const Point<2, double> trapeze_offset(0.0,
971 std::sin(gamma) * edge_length);
972
973 // loop over vertices of all cells
974 for (auto &cell : tria)
975 for (const unsigned int v : GeometryInfo<2>::vertex_indices())
976 {
977 // vertex has been already processed: nothing to do
978 if (vertex_processed[cell.vertex_index(v)])
979 continue;
980
981 // mark vertex as processed
982 vertex_processed[cell.vertex_index(v)] = true;
983
984 auto &node = cell.vertex(v);
985
986 // distinguish blocks
987 if (cell.material_id() == id_block_1 ||
988 cell.material_id() == id_block_4) // block 1 and 4
989 {
990 // step 1: rotate block 1 clockwise by gamma and move block
991 // 1 so that A(0) is on y-axis so that faces AD and BC are
992 // horizontal. This simplifies the computation of the
993 // required indices for interpolation (all x-nodes are
994 // positive) Move trapeze to be in first quadrant by adding
995 // trapeze_offset
996 Point<2, double> node_;
997 if (cell.material_id() == id_block_1)
998 {
999 node_ = Point<2, double>(rotation_matrix_1 *
1000 (node - horizontal_offset) +
1001 trapeze_offset);
1002 }
1003 // step 1: rotate block 4 counterclockwise and move down so
1004 // that trapeze is located in fourth quadrant (subtracting
1005 // trapeze_offset)
1006 else if (cell.material_id() == id_block_4)
1007 {
1008 node_ = Point<2, double>(rotation_matrix_2 *
1009 (node - horizontal_offset) -
1010 trapeze_offset);
1011 }
1012 // step 2: compute indices ix and iy and interpolate
1013 // trapezoid to a rectangle of length pi/2.
1014 {
1015 const double trapeze_height =
1016 std::sin(gamma) * edge_length;
1017 const double L = height / std::sin(gamma);
1018 const double l_a = std::cos(gamma) * edge_length;
1019 const double l_b = trapeze_height * std::tan(gamma);
1020 const double x1 = std::abs(node_(1)) / std::tan(gamma);
1021 const double x2 = L - l_a - l_b;
1022 const double x3 = std::abs(node_(1)) * std::tan(gamma);
1023 const double Dx = x1 + x2 + x3;
1024 const double deltax =
1025 (trapeze_height - std::abs(node_(1))) / std::tan(gamma);
1026 const double dx = Dx / n_cells_x_0;
1027 const double dy = trapeze_height / n_cells_y;
1028 const int ix =
1029 static_cast<int>(std::round((node_(0) - deltax) / dx));
1030 const int iy =
1031 static_cast<int>(std::round(std::abs(node_(1)) / dy));
1032
1033 node_(0) = numbers::PI / 2 * (1.0 * ix) / n_cells_x_0;
1034 node_(1) = height * (1.0 * iy) / n_cells_y;
1035 }
1036
1037 // step 3: Interpolation between semicircle (of C-Mesh) and
1038 // airfoil contour
1039 {
1040 const double dx = numbers::PI / 2 / n_cells_x_0;
1041 const double dy = height / n_cells_y;
1042 const int ix =
1043 static_cast<int>(std::round(node_(0) / dx));
1044 const int iy =
1045 static_cast<int>(std::round(node_(1) / dy));
1046 const double alpha =
1047 bias_alpha(1 - (1.0 * iy) / n_cells_y);
1048 const double theta = node_(0);
1049 const Point<2> p(-height * std::cos(theta) + center_mesh,
1050 ((cell.material_id() == id_block_1) ?
1051 (height) :
1052 (-height)) *
1053 std::sin(theta));
1054 node =
1055 airfoil_1D[(
1056 (cell.material_id() == id_block_1) ? (0) : (1))][ix] *
1057 alpha +
1058 p * (1 - alpha);
1059 }
1060 }
1061 else if (cell.material_id() == id_block_2 ||
1062 cell.material_id() == id_block_5) // block 2 and 5
1063 {
1064 // geometric parameters and indices for interpolation
1065 Assert(
1066 (std::abs(D(1) - C(1)) == std::abs(F(1) - E(1))) &&
1067 (std::abs(C(1)) == std::abs(E(1))) &&
1068 (std::abs(G(1)) == std::abs(I(1))),
1069 ExcMessage(
1070 "Points D,C,G and E,F,I are not defined symmetric to "
1071 "x-axis, which is required to interpolate block 2"
1072 " and 5 with same geometric computations."));
1073 const double l_y = D(1) - C(1);
1074 const double l_h = D(1) - l_y;
1075 const double by = -l_h / length_b1_x * (node(0) - H(0));
1076 const double dy = (height - by) / n_cells_y;
1077 const int iy = static_cast<int>(
1078 std::round((std::abs(node(1)) - by) / dy));
1079 const double dx = length_b1_x / n_cells_x_1;
1080 const int ix = static_cast<int>(
1081 std::round(std::abs(node(0) - center_mesh) / dx));
1082
1083 const double alpha = bias_alpha(1 - (1.0 * iy) / n_cells_y);
1084 // define points on upper/lower horizontal far field side,
1085 // i.e. face DG or FI. Incline factor to move points G and I
1086 // to the right by distance incline_facor*lenght_b2
1087 const Point<2> p(ix * dx + center_mesh +
1088 incline_factor * length_b2 * ix /
1089 n_cells_x_1,
1090 ((cell.material_id() == id_block_2) ?
1091 (height) :
1092 (-height)));
1093 // interpolate between y = height and upper airfoil points
1094 // (block2) or y = -height and lower airfoil points (block5)
1095 node = airfoil_1D[(
1096 (cell.material_id() == id_block_2) ? (0) : (1))]
1097 [n_cells_x_0 + ix] *
1098 alpha +
1099 p * (1 - alpha);
1100 }
1101 else if (cell.material_id() == id_block_3 ||
1102 cell.material_id() == id_block_6) // block 3 and 6
1103 {
1104 // compute indices ix and iy
1105 const double dx = length_b2 / n_cells_x_2;
1106 const double dy = height / n_cells_y;
1107 const int ix = static_cast<int>(
1108 std::round(std::abs(node(0) - H(0)) / dx));
1109 const int iy =
1110 static_cast<int>(std::round(std::abs(node(1)) / dy));
1111
1112 const double alpha_y = bias_alpha(1 - 1.0 * iy / n_cells_y);
1113 const double alpha_x =
1114 bias_alpha(1 - (static_cast<double>(ix)) / n_cells_x_2);
1115 // define on upper/lower horizontal far field side at y =
1116 // +/- height, i.e. face GK or IL incline factor to move
1117 // points G and H to the right
1118 const Point<2> p1(J(0) - (1 - incline_factor) * length_b2 *
1119 (alpha_x),
1120 ((cell.material_id() == id_block_3) ?
1121 (height) :
1122 (-height)));
1123 // define points on HJ but use tail_y as y-coordinate, in
1124 // case last airfoil point has y =/= 0
1125 const Point<2> p2(J(0) - alpha_x * length_b2, tail_y);
1126 node = p1 * (1 - alpha_y) + p2 * alpha_y;
1127 }
1128 else
1129 {
1130 Assert(false,
1131 ExcIndexRange(cell.material_id(),
1132 id_block_1,
1133 id_block_6));
1134 }
1135 }
1136 }
1137
1138
1139 /*
1140 * This function returns a bias factor 'alpha' which is used to make the
1141 * mesh more tight in close distance of the airfoil.
1142 * It is a bijective function mapping from [0,1] onto [0,1] where values
1143 * near 1 are made tighter.
1144 */
1145 double
1146 bias_alpha(double alpha) const
1147 {
1148 return std::tanh(bias_factor * alpha) / std::tanh(bias_factor);
1149 }
1150 };
1151 } // namespace
1152
1153
1154
1155 void
1156 internal_create_triangulation(
1158 std::vector<GridTools::PeriodicFacePair<
1159 typename Triangulation<2, 2>::cell_iterator>> *periodic_faces,
1160 const AdditionalData & additional_data)
1161 {
1162 MeshGenerator mesh_generator(additional_data);
1163 // Cast the triangulation to the right type so that the right
1164 // specialization of the function create_triangulation is picked up.
1165 if (auto parallel_tria =
1167 &tria))
1168 mesh_generator.create_triangulation(*parallel_tria, periodic_faces);
1169 else if (auto parallel_tria = dynamic_cast<
1171 &tria))
1172 mesh_generator.create_triangulation(*parallel_tria, periodic_faces);
1173 else
1174 mesh_generator.create_triangulation(tria, periodic_faces);
1175 }
1176
1177 template <>
1178 void
1179 create_triangulation(Triangulation<1, 1> &, const AdditionalData &)
1180 {
1181 Assert(false, ExcMessage("Airfoils only exist for 2D and 3D!"));
1182 }
1183
1184
1185
1186 template <>
1187 void
1189 std::vector<GridTools::PeriodicFacePair<
1191 const AdditionalData &)
1192 {
1193 Assert(false, ExcMessage("Airfoils only exist for 2D and 3D!"));
1194 }
1195
1196
1197
1198 template <>
1199 void
1201 const AdditionalData &additional_data)
1202 {
1203 internal_create_triangulation(tria, nullptr, additional_data);
1204 }
1205
1206
1207
1208 template <>
1209 void
1212 std::vector<GridTools::PeriodicFacePair<
1213 typename Triangulation<2, 2>::cell_iterator>> &periodic_faces,
1214 const AdditionalData & additional_data)
1215 {
1216 internal_create_triangulation(tria, &periodic_faces, additional_data);
1217 }
1218
1219
1220
1221 template <>
1222 void
1225 std::vector<GridTools::PeriodicFacePair<
1226 typename Triangulation<3, 3>::cell_iterator>> &periodic_faces,
1227 const AdditionalData & additional_data)
1228 {
1229 Assert(false, ExcMessage("3D airfoils are not implemented yet!"));
1230 (void)tria;
1231 (void)additional_data;
1232 (void)periodic_faces;
1233 }
1234 } // namespace Airfoil
1235
1236
1237 namespace
1238 {
1243 template <int dim, int spacedim>
1244 void
1245 colorize_hyper_rectangle(Triangulation<dim, spacedim> &tria)
1246 {
1247 // there is nothing to do in 1d
1248 if (dim > 1)
1249 {
1250 // there is only one cell, so
1251 // simple task
1253 tria.begin();
1254 for (auto f : GeometryInfo<dim>::face_indices())
1255 cell->face(f)->set_boundary_id(f);
1256 }
1257 }
1258
1259
1260
1261 template <int spacedim>
1262 void
1263 colorize_subdivided_hyper_rectangle(Triangulation<1, spacedim> &tria,
1264 const Point<spacedim> &,
1265 const Point<spacedim> &,
1266 const double)
1267 {
1269 tria.begin();
1270 cell != tria.end();
1271 ++cell)
1272 if (cell->center()(0) > 0)
1273 cell->set_material_id(1);
1274 // boundary indicators are set to
1275 // 0 (left) and 1 (right) by default.
1276 }
1277
1278
1279
1280 template <int dim, int spacedim>
1281 void
1282 colorize_subdivided_hyper_rectangle(Triangulation<dim, spacedim> &tria,
1283 const Point<spacedim> & p1,
1284 const Point<spacedim> & p2,
1285 const double epsilon)
1286 {
1287 // run through all faces and check
1288 // if one of their center coordinates matches
1289 // one of the corner points. Comparisons
1290 // are made using an epsilon which
1291 // should be smaller than the smallest cell
1292 // diameter.
1293
1295 tria.begin_face(),
1296 endface =
1297 tria.end_face();
1298 for (; face != endface; ++face)
1299 if (face->at_boundary())
1300 if (face->boundary_id() == 0)
1301 {
1302 const Point<spacedim> center(face->center());
1303
1304 if (std::abs(center(0) - p1[0]) < epsilon)
1305 face->set_boundary_id(0);
1306 else if (std::abs(center(0) - p2[0]) < epsilon)
1307 face->set_boundary_id(1);
1308 else if (dim > 1 && std::abs(center(1) - p1[1]) < epsilon)
1309 face->set_boundary_id(2);
1310 else if (dim > 1 && std::abs(center(1) - p2[1]) < epsilon)
1311 face->set_boundary_id(3);
1312 else if (dim > 2 && std::abs(center(2) - p1[2]) < epsilon)
1313 face->set_boundary_id(4);
1314 else if (dim > 2 && std::abs(center(2) - p2[2]) < epsilon)
1315 face->set_boundary_id(5);
1316 else
1317 // triangulation says it
1318 // is on the boundary,
1319 // but we could not find
1320 // on which boundary.
1321 Assert(false, ExcInternalError());
1322 }
1323
1324 for (const auto &cell : tria.cell_iterators())
1325 {
1326 types::material_id id = 0;
1327 for (unsigned int d = 0; d < dim; ++d)
1328 if (cell->center()(d) > 0)
1329 id += (1 << d);
1330 cell->set_material_id(id);
1331 }
1332 }
1333
1334
1339 void
1340 colorize_hyper_shell(Triangulation<2> &tria,
1341 const Point<2> &,
1342 const double,
1343 const double)
1344 {
1345 // In spite of receiving geometrical
1346 // data, we do this only based on
1347 // topology.
1348
1349 // For the mesh based on cube,
1350 // this is highly irregular
1352 cell != tria.end();
1353 ++cell)
1354 {
1355 Assert(cell->face(2)->at_boundary(), ExcInternalError());
1356 cell->face(2)->set_all_boundary_ids(1);
1357 }
1358 }
1359
1360
1365 void
1366 colorize_hyper_shell(Triangulation<3> &tria,
1367 const Point<3> &,
1368 const double,
1369 const double)
1370 {
1371 // the following uses a good amount
1372 // of knowledge about the
1373 // orientation of cells. this is
1374 // probably not good style...
1375 if (tria.n_cells() == 6)
1376 {
1378
1379 Assert(cell->face(4)->at_boundary(), ExcInternalError());
1380 cell->face(4)->set_all_boundary_ids(1);
1381
1382 ++cell;
1383 Assert(cell->face(2)->at_boundary(), ExcInternalError());
1384 cell->face(2)->set_all_boundary_ids(1);
1385
1386 ++cell;
1387 Assert(cell->face(2)->at_boundary(), ExcInternalError());
1388 cell->face(2)->set_all_boundary_ids(1);
1389
1390 ++cell;
1391 Assert(cell->face(0)->at_boundary(), ExcInternalError());
1392 cell->face(0)->set_all_boundary_ids(1);
1393
1394 ++cell;
1395 Assert(cell->face(2)->at_boundary(), ExcInternalError());
1396 cell->face(2)->set_all_boundary_ids(1);
1397
1398 ++cell;
1399 Assert(cell->face(0)->at_boundary(), ExcInternalError());
1400 cell->face(0)->set_all_boundary_ids(1);
1401 }
1402 else if (tria.n_cells() == 12)
1403 {
1404 // again use some internal
1405 // knowledge
1407 cell != tria.end();
1408 ++cell)
1409 {
1410 Assert(cell->face(5)->at_boundary(), ExcInternalError());
1411 cell->face(5)->set_all_boundary_ids(1);
1412 }
1413 }
1414 else if (tria.n_cells() == 96)
1415 {
1416 // the 96-cell hypershell is
1417 // based on a once refined
1418 // 12-cell mesh. consequently,
1419 // since the outer faces all
1420 // are face_no==5 above, so
1421 // they are here (unless they
1422 // are in the interior). Use
1423 // this to assign boundary
1424 // indicators, but also make
1425 // sure that we encounter
1426 // exactly 48 such faces
1427 unsigned int count = 0;
1429 cell != tria.end();
1430 ++cell)
1431 if (cell->face(5)->at_boundary())
1432 {
1433 cell->face(5)->set_all_boundary_ids(1);
1434 ++count;
1435 }
1436 Assert(count == 48, ExcInternalError());
1437 }
1438 else
1439 Assert(false, ExcNotImplemented());
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 (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
1528 Assert(false, ExcInternalError());
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:
1582 Assert(false, ExcNotImplemented());
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
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# ifdef DEBUG
1630 Tensor<2, dim> vector_matrix;
1631 for (unsigned int d = 0; d < dim; ++d)
1632 for (unsigned int c = 1; c <= dim; ++c)
1633 vector_matrix[c - 1][d] = vertices[c](d) - vertices[0](d);
1634 Assert(determinant(vector_matrix) > 0.,
1635 ExcMessage("Vertices of simplex must form a right handed system"));
1636# endif
1637
1638 // Set up the vertices by first copying into points.
1639 std::vector<Point<dim>> points = vertices;
1641 // Compute the edge midpoints and add up everything to compute the
1642 // center point.
1643 for (unsigned int i = 0; i <= dim; ++i)
1644 {
1645 points.push_back(0.5 * (points[i] + points[(i + 1) % (dim + 1)]));
1646 center += points[i];
1647 }
1648 if (dim > 2)
1649 {
1650 // In 3D, we have some more edges to deal with
1651 for (unsigned int i = 1; i < dim; ++i)
1652 points.push_back(0.5 * (points[i - 1] + points[i + 1]));
1653 // And we need face midpoints
1654 for (unsigned int i = 0; i <= dim; ++i)
1655 points.push_back(1. / 3. *
1656 (points[i] + points[(i + 1) % (dim + 1)] +
1657 points[(i + 2) % (dim + 1)]));
1658 }
1659 points.push_back((1. / (dim + 1)) * center);
1660
1661 std::vector<CellData<dim>> cells(dim + 1);
1662 switch (dim)
1663 {
1664 case 2:
1665 AssertDimension(points.size(), 7);
1666 cells[0].vertices[0] = 0;
1667 cells[0].vertices[1] = 3;
1668 cells[0].vertices[2] = 5;
1669 cells[0].vertices[3] = 6;
1670 cells[0].material_id = 0;
1671
1672 cells[1].vertices[0] = 3;
1673 cells[1].vertices[1] = 1;
1674 cells[1].vertices[2] = 6;
1675 cells[1].vertices[3] = 4;
1676 cells[1].material_id = 0;
1677
1678 cells[2].vertices[0] = 5;
1679 cells[2].vertices[1] = 6;
1680 cells[2].vertices[2] = 2;
1681 cells[2].vertices[3] = 4;
1682 cells[2].material_id = 0;
1683 break;
1684 case 3:
1685 AssertDimension(points.size(), 15);
1686 cells[0].vertices[0] = 0;
1687 cells[0].vertices[1] = 4;
1688 cells[0].vertices[2] = 8;
1689 cells[0].vertices[3] = 10;
1690 cells[0].vertices[4] = 7;
1691 cells[0].vertices[5] = 13;
1692 cells[0].vertices[6] = 12;
1693 cells[0].vertices[7] = 14;
1694 cells[0].material_id = 0;
1695
1696 cells[1].vertices[0] = 4;
1697 cells[1].vertices[1] = 1;
1698 cells[1].vertices[2] = 10;
1699 cells[1].vertices[3] = 5;
1700 cells[1].vertices[4] = 13;
1701 cells[1].vertices[5] = 9;
1702 cells[1].vertices[6] = 14;
1703 cells[1].vertices[7] = 11;
1704 cells[1].material_id = 0;
1705
1706 cells[2].vertices[0] = 8;
1707 cells[2].vertices[1] = 10;
1708 cells[2].vertices[2] = 2;
1709 cells[2].vertices[3] = 5;
1710 cells[2].vertices[4] = 12;
1711 cells[2].vertices[5] = 14;
1712 cells[2].vertices[6] = 6;
1713 cells[2].vertices[7] = 11;
1714 cells[2].material_id = 0;
1715
1716 cells[3].vertices[0] = 7;
1717 cells[3].vertices[1] = 13;
1718 cells[3].vertices[2] = 12;
1719 cells[3].vertices[3] = 14;
1720 cells[3].vertices[4] = 3;
1721 cells[3].vertices[5] = 9;
1722 cells[3].vertices[6] = 6;
1723 cells[3].vertices[7] = 11;
1724 cells[3].material_id = 0;
1725 break;
1726 default:
1727 Assert(false, ExcNotImplemented());
1728 }
1729 tria.create_triangulation(points, cells, SubCellData());
1730 }
1731
1732
1733
1734 template <int dim, int spacedim>
1735 void
1737 const ReferenceCell & reference_cell)
1738 {
1739 AssertDimension(dim, reference_cell.get_dimension());
1740
1741 if (reference_cell == ReferenceCells::get_hypercube<dim>())
1742 {
1744 }
1745 else
1746 {
1747 // Create an array that contains the vertices of the reference cell.
1748 // We can query these points from ReferenceCell, but then we have
1749 // to embed them into the spacedim-dimensional space.
1750 std::vector<Point<spacedim>> vertices(reference_cell.n_vertices());
1751 for (const unsigned int v : reference_cell.vertex_indices())
1752 {
1753 const Point<dim> this_vertex = reference_cell.vertex<dim>(v);
1754 for (unsigned int d = 0; d < dim; ++d)
1755 vertices[v][d] = this_vertex[d];
1756 // Point<spacedim> initializes everything to zero, so any remaining
1757 // elements are left at zero and we don't have to explicitly pad
1758 // from 'dim' to 'spacedim' here.
1759 }
1760
1761 // Then make one cell out of these vertices. They are ordered correctly
1762 // already, so we just need to enumerate them
1763 std::vector<CellData<dim>> cells(1);
1764 cells[0].vertices.resize(reference_cell.n_vertices());
1765 for (const unsigned int v : reference_cell.vertex_indices())
1766 cells[0].vertices[v] = v;
1767
1768 // Turn all of this into a triangulation
1770 }
1771 }
1772
1773 void
1775 const unsigned int n_cells,
1776 const unsigned int n_rotations,
1777 const double R,
1778 const double r)
1779 {
1780 const unsigned int dim = 3;
1781 Assert(n_cells > 4,
1782 ExcMessage(
1783 "More than 4 cells are needed to create a moebius grid."));
1784 Assert(r > 0 && R > 0,
1785 ExcMessage("Outer and inner radius must be positive."));
1786 Assert(R > r,
1787 ExcMessage("Outer radius must be greater than inner radius."));
1788
1789
1790 std::vector<Point<dim>> vertices(4 * n_cells);
1791 double beta_step = n_rotations * numbers::PI / 2.0 / n_cells;
1792 double alpha_step = 2.0 * numbers::PI / n_cells;
1793
1794 for (unsigned int i = 0; i < n_cells; ++i)
1795 for (unsigned int j = 0; j < 4; ++j)
1796 {
1797 vertices[4 * i + j][0] =
1798 R * std::cos(i * alpha_step) +
1799 r * std::cos(i * beta_step + j * numbers::PI / 2.0) *
1800 std::cos(i * alpha_step);
1801 vertices[4 * i + j][1] =
1802 R * std::sin(i * alpha_step) +
1803 r * std::cos(i * beta_step + j * numbers::PI / 2.0) *
1804 std::sin(i * alpha_step);
1805 vertices[4 * i + j][2] =
1806 r * std::sin(i * beta_step + j * numbers::PI / 2.0);
1807 }
1808
1809 unsigned int offset = 0;
1810
1811 // This Triangulation is constructed using the UCD numbering scheme since,
1812 // in that numbering, the front face is first and the back face is second,
1813 // which is more convenient for creating a moebius
1814 std::vector<CellData<dim>> cells(n_cells);
1815 for (unsigned int i = 0; i < n_cells; ++i)
1816 {
1817 for (unsigned int j = 0; j < 2; ++j)
1818 {
1819 cells[i].vertices[GeometryInfo<3>::ucd_to_deal[0 + 4 * j]] =
1820 offset + 0 + 4 * j;
1821 cells[i].vertices[GeometryInfo<3>::ucd_to_deal[1 + 4 * j]] =
1822 offset + 3 + 4 * j;
1823 cells[i].vertices[GeometryInfo<3>::ucd_to_deal[2 + 4 * j]] =
1824 offset + 2 + 4 * j;
1825 cells[i].vertices[GeometryInfo<3>::ucd_to_deal[3 + 4 * j]] =
1826 offset + 1 + 4 * j;
1827 }
1828 offset += 4;
1829 cells[i].material_id = 0;
1830 }
1831
1832 // now correct the last four vertices
1833 cells[n_cells - 1].vertices[GeometryInfo<3>::ucd_to_deal[4]] =
1834 (0 + n_rotations) % 4;
1835 cells[n_cells - 1].vertices[GeometryInfo<3>::ucd_to_deal[5]] =
1836 (3 + n_rotations) % 4;
1837 cells[n_cells - 1].vertices[GeometryInfo<3>::ucd_to_deal[6]] =
1838 (2 + n_rotations) % 4;
1839 cells[n_cells - 1].vertices[GeometryInfo<3>::ucd_to_deal[7]] =
1840 (1 + n_rotations) % 4;
1841
1844 }
1845
1846
1847
1848 template <>
1849 void
1850 torus<2, 3>(Triangulation<2, 3> &tria,
1851 const double R,
1852 const double r,
1853 const unsigned int,
1854 const double)
1855 {
1856 Assert(R > r,
1857 ExcMessage("Outer radius R must be greater than the inner "
1858 "radius r."));
1859 Assert(r > 0.0, ExcMessage("The inner radius r must be positive."));
1860
1861 const unsigned int dim = 2;
1862 const unsigned int spacedim = 3;
1863 std::vector<Point<spacedim>> vertices(16);
1864
1865 vertices[0] = Point<spacedim>(R - r, 0, 0);
1866 vertices[1] = Point<spacedim>(R, -r, 0);
1867 vertices[2] = Point<spacedim>(R + r, 0, 0);
1868 vertices[3] = Point<spacedim>(R, r, 0);
1869 vertices[4] = Point<spacedim>(0, 0, R - r);
1870 vertices[5] = Point<spacedim>(0, -r, R);
1871 vertices[6] = Point<spacedim>(0, 0, R + r);
1872 vertices[7] = Point<spacedim>(0, r, R);
1873 vertices[8] = Point<spacedim>(-(R - r), 0, 0);
1874 vertices[9] = Point<spacedim>(-R, -r, 0);
1875 vertices[10] = Point<spacedim>(-(R + r), 0, 0);
1876 vertices[11] = Point<spacedim>(-R, r, 0);
1877 vertices[12] = Point<spacedim>(0, 0, -(R - r));
1878 vertices[13] = Point<spacedim>(0, -r, -R);
1879 vertices[14] = Point<spacedim>(0, 0, -(R + r));
1880 vertices[15] = Point<spacedim>(0, r, -R);
1881
1882 std::vector<CellData<dim>> cells(16);
1883 // Right Hand Orientation
1884 cells[0].vertices[0] = 0;
1885 cells[0].vertices[1] = 4;
1886 cells[0].vertices[2] = 3;
1887 cells[0].vertices[3] = 7;
1888 cells[0].material_id = 0;
1889
1890 cells[1].vertices[0] = 1;
1891 cells[1].vertices[1] = 5;
1892 cells[1].vertices[2] = 0;
1893 cells[1].vertices[3] = 4;
1894 cells[1].material_id = 0;
1895
1896 cells[2].vertices[0] = 2;
1897 cells[2].vertices[1] = 6;
1898 cells[2].vertices[2] = 1;
1899 cells[2].vertices[3] = 5;
1900 cells[2].material_id = 0;
1901
1902 cells[3].vertices[0] = 3;
1903 cells[3].vertices[1] = 7;
1904 cells[3].vertices[2] = 2;
1905 cells[3].vertices[3] = 6;
1906 cells[3].material_id = 0;
1907
1908 cells[4].vertices[0] = 4;
1909 cells[4].vertices[1] = 8;
1910 cells[4].vertices[2] = 7;
1911 cells[4].vertices[3] = 11;
1912 cells[4].material_id = 0;
1913
1914 cells[5].vertices[0] = 5;
1915 cells[5].vertices[1] = 9;
1916 cells[5].vertices[2] = 4;
1917 cells[5].vertices[3] = 8;
1918 cells[5].material_id = 0;
1919
1920 cells[6].vertices[0] = 6;
1921 cells[6].vertices[1] = 10;
1922 cells[6].vertices[2] = 5;
1923 cells[6].vertices[3] = 9;
1924 cells[6].material_id = 0;
1925
1926 cells[7].vertices[0] = 7;
1927 cells[7].vertices[1] = 11;
1928 cells[7].vertices[2] = 6;
1929 cells[7].vertices[3] = 10;
1930 cells[7].material_id = 0;
1931
1932 cells[8].vertices[0] = 8;
1933 cells[8].vertices[1] = 12;
1934 cells[8].vertices[2] = 11;
1935 cells[8].vertices[3] = 15;
1936 cells[8].material_id = 0;
1937
1938 cells[9].vertices[0] = 9;
1939 cells[9].vertices[1] = 13;
1940 cells[9].vertices[2] = 8;
1941 cells[9].vertices[3] = 12;
1942 cells[9].material_id = 0;
1943
1944 cells[10].vertices[0] = 10;
1945 cells[10].vertices[1] = 14;
1946 cells[10].vertices[2] = 9;
1947 cells[10].vertices[3] = 13;
1948 cells[10].material_id = 0;
1949
1950 cells[11].vertices[0] = 11;
1951 cells[11].vertices[1] = 15;
1952 cells[11].vertices[2] = 10;
1953 cells[11].vertices[3] = 14;
1954 cells[11].material_id = 0;
1955
1956 cells[12].vertices[0] = 12;
1957 cells[12].vertices[1] = 0;
1958 cells[12].vertices[2] = 15;
1959 cells[12].vertices[3] = 3;
1960 cells[12].material_id = 0;
1961
1962 cells[13].vertices[0] = 13;
1963 cells[13].vertices[1] = 1;
1964 cells[13].vertices[2] = 12;
1965 cells[13].vertices[3] = 0;
1966 cells[13].material_id = 0;
1967
1968 cells[14].vertices[0] = 14;
1969 cells[14].vertices[1] = 2;
1970 cells[14].vertices[2] = 13;
1971 cells[14].vertices[3] = 1;
1972 cells[14].material_id = 0;
1973
1974 cells[15].vertices[0] = 15;
1975 cells[15].vertices[1] = 3;
1976 cells[15].vertices[2] = 14;
1977 cells[15].vertices[3] = 2;
1978 cells[15].material_id = 0;
1979
1982
1985 }
1986
1987
1988
1989 template <>
1990 void
1991 torus<3, 3>(Triangulation<3, 3> &tria,
1992 const double R,
1993 const double r,
1994 const unsigned int n_cells_toroidal,
1995 const double phi)
1996 {
1997 Assert(R > r,
1998 ExcMessage("Outer radius R must be greater than the inner "
1999 "radius r."));
2000 Assert(r > 0.0, ExcMessage("The inner radius r must be positive."));
2001 Assert(n_cells_toroidal > 2,
2002 ExcMessage("Number of cells in toroidal direction has "
2003 "to be at least 3."));
2004 AssertThrow(phi > 0.0 && phi < 2.0 * numbers::PI + 1.0e-15,
2005 ExcMessage("Invalid angle phi specified."));
2006
2007 // the first 8 vertices are in the x-y-plane
2008 Point<3> const p = Point<3>(R, 0.0, 0.0);
2009 double const a = 1. / (1 + std::sqrt(2.0));
2010 // A value of 1 indicates "open" torus with angle < 2*pi, which
2011 // means that we need an additional layer of vertices
2012 const unsigned int additional_layer =
2013 (phi < 2.0 * numbers::PI - 1.0e-15) ?
2014 1 :
2015 0; // torus is closed (angle of 2*pi)
2016 const unsigned int n_point_layers_toroidal =
2017 n_cells_toroidal + additional_layer;
2018 std::vector<Point<3>> vertices(8 * n_point_layers_toroidal);
2019 vertices[0] = p + Point<3>(-1, -1, 0) * (r / std::sqrt(2.0)),
2020 vertices[1] = p + Point<3>(+1, -1, 0) * (r / std::sqrt(2.0)),
2021 vertices[2] = p + Point<3>(-1, -1, 0) * (r / std::sqrt(2.0) * a),
2022 vertices[3] = p + Point<3>(+1, -1, 0) * (r / std::sqrt(2.0) * a),
2023 vertices[4] = p + Point<3>(-1, +1, 0) * (r / std::sqrt(2.0) * a),
2024 vertices[5] = p + Point<3>(+1, +1, 0) * (r / std::sqrt(2.0) * a),
2025 vertices[6] = p + Point<3>(-1, +1, 0) * (r / std::sqrt(2.0)),
2026 vertices[7] = p + Point<3>(+1, +1, 0) * (r / std::sqrt(2.0));
2027
2028 // create remaining vertices by rotating around negative y-axis (the
2029 // direction is to ensure positive cell measures)
2030 double const phi_cell = phi / n_cells_toroidal;
2031 for (unsigned int c = 1; c < n_point_layers_toroidal; ++c)
2032 {
2033 for (unsigned int v = 0; v < 8; ++v)
2034 {
2035 double const r_2d = vertices[v][0];
2036 vertices[8 * c + v][0] = r_2d * std::cos(phi_cell * c);
2037 vertices[8 * c + v][1] = vertices[v][1];
2038 vertices[8 * c + v][2] = r_2d * std::sin(phi_cell * c);
2039 }
2040 }
2041
2042 // cell connectivity
2043 std::vector<CellData<3>> cells(5 * n_cells_toroidal);
2044 for (unsigned int c = 0; c < n_cells_toroidal; ++c)
2045 {
2046 for (unsigned int j = 0; j < 2; ++j)
2047 {
2048 const unsigned int offset =
2049 (8 * (c + j)) % (8 * n_point_layers_toroidal);
2050
2051 // cell 0 in x-y-plane
2052 cells[5 * c].vertices[0 + j * 4] = offset + 0;
2053 cells[5 * c].vertices[1 + j * 4] = offset + 1;
2054 cells[5 * c].vertices[2 + j * 4] = offset + 2;
2055 cells[5 * c].vertices[3 + j * 4] = offset + 3;
2056 // cell 1 in x-y-plane (cell on torus centerline)
2057 cells[5 * c + 1].vertices[0 + j * 4] = offset + 2;
2058 cells[5 * c + 1].vertices[1 + j * 4] = offset + 3;
2059 cells[5 * c + 1].vertices[2 + j * 4] = offset + 4;
2060 cells[5 * c + 1].vertices[3 + j * 4] = offset + 5;
2061 // cell 2 in x-y-plane
2062 cells[5 * c + 2].vertices[0 + j * 4] = offset + 4;
2063 cells[5 * c + 2].vertices[1 + j * 4] = offset + 5;
2064 cells[5 * c + 2].vertices[2 + j * 4] = offset + 6;
2065 cells[5 * c + 2].vertices[3 + j * 4] = offset + 7;
2066 // cell 3 in x-y-plane
2067 cells[5 * c + 3].vertices[0 + j * 4] = offset + 0;
2068 cells[5 * c + 3].vertices[1 + j * 4] = offset + 2;
2069 cells[5 * c + 3].vertices[2 + j * 4] = offset + 6;
2070 cells[5 * c + 3].vertices[3 + j * 4] = offset + 4;
2071 // cell 4 in x-y-plane
2072 cells[5 * c + 4].vertices[0 + j * 4] = offset + 3;
2073 cells[5 * c + 4].vertices[1 + j * 4] = offset + 1;
2074 cells[5 * c + 4].vertices[2 + j * 4] = offset + 5;
2075 cells[5 * c + 4].vertices[3 + j * 4] = offset + 7;
2076 }
2077
2078 cells[5 * c].material_id = 0;
2079 // mark cell on torus centerline
2080 cells[5 * c + 1].material_id = 1;
2081 cells[5 * c + 2].material_id = 0;
2082 cells[5 * c + 3].material_id = 0;
2083 cells[5 * c + 4].material_id = 0;
2084 }
2085
2087
2090
2091 for (auto &cell : tria.cell_iterators())
2092 {
2093 // identify faces on torus surface and set manifold to 1
2094 for (unsigned int f : GeometryInfo<3>::face_indices())
2095 {
2096 // faces 4 and 5 are those with normal vector aligned with torus
2097 // centerline
2098 if (cell->face(f)->at_boundary() && f != 4 && f != 5)
2099 {
2100 cell->face(f)->set_all_manifold_ids(1);
2101 }
2102 }
2103
2104 // set manifold id to 2 for those cells that are on the torus centerline
2105 if (cell->material_id() == 1)
2106 {
2107 cell->set_all_manifold_ids(2);
2108 // reset to 0
2109 cell->set_material_id(0);
2110 }
2111 }
2112
2116 Point<3>()));
2118 transfinite.initialize(tria);
2119 tria.set_manifold(0, transfinite);
2120 }
2121
2122
2123
2124 template <int dim, int spacedim>
2125 void
2127 const std::vector<Point<spacedim>> &vertices,
2128 const bool colorize)
2129 {
2131 ExcMessage("Wrong number of vertices."));
2132
2133 // First create a hyper_rectangle and then deform it.
2134 hyper_cube(tria, 0, 1, colorize);
2135
2138 for (const unsigned int i : GeometryInfo<dim>::vertex_indices())
2139 cell->vertex(i) = vertices[i];
2140
2141 // Check that the order of the vertices makes sense, i.e., the volume of the
2142 // cell is positive.
2144 ExcMessage(
2145 "The volume of the cell is not greater than zero. "
2146 "This could be due to the wrong ordering of the vertices."));
2147 }
2148
2149
2150
2151 template <>
2152 void
2154 const Point<3> (&/*corners*/)[3],
2155 const bool /*colorize*/)
2156 {
2157 Assert(false, ExcNotImplemented());
2158 }
2159
2160 template <>
2161 void
2163 const Point<1> (&/*corners*/)[1],
2164 const bool /*colorize*/)
2165 {
2166 Assert(false, ExcNotImplemented());
2167 }
2168
2169 // Implementation for 2D only
2170 template <>
2171 void
2173 const Point<2> (&corners)[2],
2174 const bool colorize)
2175 {
2176 Point<2> origin;
2177 std::array<Tensor<1, 2>, 2> edges;
2178 edges[0] = corners[0];
2179 edges[1] = corners[1];
2180 std::vector<unsigned int> subdivisions;
2181 subdivided_parallelepiped<2, 2>(
2182 tria, origin, edges, subdivisions, colorize);
2183 }
2184
2185
2186
2187 template <int dim>
2188 void
2190 const Point<dim> (&corners)[dim],
2191 const bool colorize)
2192 {
2193 unsigned int n_subdivisions[dim];
2194 for (unsigned int i = 0; i < dim; ++i)
2195 n_subdivisions[i] = 1;
2196
2197 // and call the function below
2198 subdivided_parallelepiped(tria, n_subdivisions, corners, colorize);
2199 }
2200
2201 template <int dim>
2202 void
2204 const unsigned int n_subdivisions,
2205 const Point<dim> (&corners)[dim],
2206 const bool colorize)
2207 {
2208 // Equalize number of subdivisions in each dim-direction, their
2209 // validity will be checked later
2210 unsigned int n_subdivisions_[dim];
2211 for (unsigned int i = 0; i < dim; ++i)
2212 n_subdivisions_[i] = n_subdivisions;
2213
2214 // and call the function below
2215 subdivided_parallelepiped(tria, n_subdivisions_, corners, colorize);
2216 }
2217
2218 template <int dim>
2219 void
2221# ifndef _MSC_VER
2222 const unsigned int (&n_subdivisions)[dim],
2223# else
2224 const unsigned int *n_subdivisions,
2225# endif
2226 const Point<dim> (&corners)[dim],
2227 const bool colorize)
2228 {
2229 Point<dim> origin;
2230 std::vector<unsigned int> subdivisions;
2231 std::array<Tensor<1, dim>, dim> edges;
2232 for (unsigned int i = 0; i < dim; ++i)
2233 {
2234 subdivisions.push_back(n_subdivisions[i]);
2235 edges[i] = corners[i];
2236 }
2237
2238 subdivided_parallelepiped<dim, dim>(
2239 tria, origin, edges, subdivisions, colorize);
2240 }
2241
2242 // Parallelepiped implementation in 1d, 2d, and 3d. @note The
2243 // implementation in 1d is similar to hyper_rectangle(), and in 2d is
2244 // similar to parallelogram().
2245 template <int dim, int spacedim>
2246 void
2248 const Point<spacedim> & origin,
2249 const std::array<Tensor<1, spacedim>, dim> &edges,
2250 const std::vector<unsigned int> &subdivisions,
2251 const bool colorize)
2252 {
2253 std::vector<unsigned int> compute_subdivisions = subdivisions;
2254 if (compute_subdivisions.size() == 0)
2255 {
2256 compute_subdivisions.resize(dim, 1);
2257 }
2258
2259 Assert(compute_subdivisions.size() == dim,
2260 ExcMessage("One subdivision must be provided for each dimension."));
2261 // check subdivisions
2262 for (unsigned int i = 0; i < dim; ++i)
2263 {
2264 Assert(compute_subdivisions[i] > 0,
2265 ExcInvalidRepetitions(subdivisions[i]));
2266 Assert(
2267 edges[i].norm() > 0,
2268 ExcMessage(
2269 "Edges in subdivided_parallelepiped() must not be degenerate."));
2270 }
2271
2272 /*
2273 * Verify that the edge points to the right in 1D, vectors are oriented in
2274 * a counter clockwise direction in 2D, or form a right handed system in
2275 * 3D.
2276 */
2277 bool twisted_data = false;
2278 switch (dim)
2279 {
2280 case 1:
2281 {
2282 twisted_data = (edges[0][0] < 0);
2283 break;
2284 }
2285 case 2:
2286 {
2287 if (spacedim == 2) // this check does not make sense otherwise
2288 {
2289 const double plane_normal =
2290 edges[0][0] * edges[1][1] - edges[0][1] * edges[1][0];
2291 twisted_data = (plane_normal < 0.0);
2292 }
2293 break;
2294 }
2295 case 3:
2296 {
2297 // Check that the first two vectors are not linear combinations to
2298 // avoid zero division later on.
2299 Assert(std::abs(edges[0] * edges[1] /
2300 (edges[0].norm() * edges[1].norm()) -
2301 1.0) > 1.0e-15,
2302 ExcMessage(
2303 "Edges in subdivided_parallelepiped() must point in"
2304 " different directions."));
2305 const Tensor<1, spacedim> plane_normal =
2306 cross_product_3d(edges[0], edges[1]);
2307
2308 /*
2309 * Ensure that edges 1, 2, and 3 form a right-handed set of
2310 * vectors. This works by applying the definition of the dot product
2311 *
2312 * cos(theta) = dot(x, y)/(norm(x)*norm(y))
2313 *
2314 * and then, since the normal vector and third edge should both
2315 * point away from the plane formed by the first two edges, the
2316 * angle between them must be between 0 and pi/2; hence we just need
2317 *
2318 * 0 < dot(x, y).
2319 */
2320 twisted_data = (plane_normal * edges[2] < 0.0);
2321 break;
2322 }
2323 default:
2324 Assert(false, ExcInternalError());
2325 }
2326 (void)twisted_data; // make the static analyzer happy
2327 Assert(
2328 !twisted_data,
2329 ExcInvalidInputOrientation(
2330 "The triangulation you are trying to create will consist of cells"
2331 " with negative measures. This is usually the result of input data"
2332 " that does not define a right-handed coordinate system. The usual"
2333 " fix for this is to ensure that in 1D the given point is to the"
2334 " right of the origin (or the given edge tensor is positive), in 2D"
2335 " that the two edges (and their cross product) obey the right-hand"
2336 " rule (which may usually be done by switching the order of the"
2337 " points or edge tensors), or in 3D that the edges form a"
2338 " right-handed coordinate system (which may also be accomplished by"
2339 " switching the order of the first two points or edge tensors)."));
2340
2341 // Check corners do not overlap (unique)
2342 for (unsigned int i = 0; i < dim; ++i)
2343 for (unsigned int j = i + 1; j < dim; ++j)
2344 Assert((edges[i] != edges[j]),
2345 ExcMessage(
2346 "Degenerate edges of subdivided_parallelepiped encountered."));
2347
2348 // Create a list of points
2349 std::vector<Point<spacedim>> points;
2350
2351 switch (dim)
2352 {
2353 case 1:
2354 for (unsigned int x = 0; x <= compute_subdivisions[0]; ++x)
2355 points.push_back(origin + edges[0] / compute_subdivisions[0] * x);
2356 break;
2357
2358 case 2:
2359 for (unsigned int y = 0; y <= compute_subdivisions[1]; ++y)
2360 for (unsigned int x = 0; x <= compute_subdivisions[0]; ++x)
2361 points.push_back(origin + edges[0] / compute_subdivisions[0] * x +
2362 edges[1] / compute_subdivisions[1] * y);
2363 break;
2364
2365 case 3:
2366 for (unsigned int z = 0; z <= compute_subdivisions[2]; ++z)
2367 for (unsigned int y = 0; y <= compute_subdivisions[1]; ++y)
2368 for (unsigned int x = 0; x <= compute_subdivisions[0]; ++x)
2369 points.push_back(origin +
2370 edges[0] / compute_subdivisions[0] * x +
2371 edges[1] / compute_subdivisions[1] * y +
2372 edges[2] / compute_subdivisions[2] * z);
2373 break;
2374
2375 default:
2376 Assert(false, ExcNotImplemented());
2377 }
2378
2379 // Prepare cell data
2380 unsigned int n_cells = 1;
2381 for (unsigned int i = 0; i < dim; ++i)
2382 n_cells *= compute_subdivisions[i];
2383 std::vector<CellData<dim>> cells(n_cells);
2384
2385 // Create fixed ordering of
2386 switch (dim)
2387 {
2388 case 1:
2389 for (unsigned int x = 0; x < compute_subdivisions[0]; ++x)
2390 {
2391 cells[x].vertices[0] = x;
2392 cells[x].vertices[1] = x + 1;
2393
2394 // wipe material id
2395 cells[x].material_id = 0;
2396 }
2397 break;
2398
2399 case 2:
2400 {
2401 // Shorthand
2402 const unsigned int n_dy = compute_subdivisions[1];
2403 const unsigned int n_dx = compute_subdivisions[0];
2404
2405 for (unsigned int y = 0; y < n_dy; ++y)
2406 for (unsigned int x = 0; x < n_dx; ++x)
2407 {
2408 const unsigned int c = y * n_dx + x;
2409 cells[c].vertices[0] = y * (n_dx + 1) + x;
2410 cells[c].vertices[1] = y * (n_dx + 1) + x + 1;
2411 cells[c].vertices[2] = (y + 1) * (n_dx + 1) + x;
2412 cells[c].vertices[3] = (y + 1) * (n_dx + 1) + x + 1;
2413
2414 // wipe material id
2415 cells[c].material_id = 0;
2416 }
2417 }
2418 break;
2419
2420 case 3:
2421 {
2422 // Shorthand
2423 const unsigned int n_dz = compute_subdivisions[2];
2424 const unsigned int n_dy = compute_subdivisions[1];
2425 const unsigned int n_dx = compute_subdivisions[0];
2426
2427 for (unsigned int z = 0; z < n_dz; ++z)
2428 for (unsigned int y = 0; y < n_dy; ++y)
2429 for (unsigned int x = 0; x < n_dx; ++x)
2430 {
2431 const unsigned int c = z * n_dy * n_dx + y * n_dx + x;
2432
2433 cells[c].vertices[0] =
2434 z * (n_dy + 1) * (n_dx + 1) + y * (n_dx + 1) + x;
2435 cells[c].vertices[1] =
2436 z * (n_dy + 1) * (n_dx + 1) + y * (n_dx + 1) + x + 1;
2437 cells[c].vertices[2] =
2438 z * (n_dy + 1) * (n_dx + 1) + (y + 1) * (n_dx + 1) + x;
2439 cells[c].vertices[3] = z * (n_dy + 1) * (n_dx + 1) +
2440 (y + 1) * (n_dx + 1) + x + 1;
2441 cells[c].vertices[4] =
2442 (z + 1) * (n_dy + 1) * (n_dx + 1) + y * (n_dx + 1) + x;
2443 cells[c].vertices[5] = (z + 1) * (n_dy + 1) * (n_dx + 1) +
2444 y * (n_dx + 1) + x + 1;
2445 cells[c].vertices[6] = (z + 1) * (n_dy + 1) * (n_dx + 1) +
2446 (y + 1) * (n_dx + 1) + x;
2447 cells[c].vertices[7] = (z + 1) * (n_dy + 1) * (n_dx + 1) +
2448 (y + 1) * (n_dx + 1) + x + 1;
2449
2450 // wipe material id
2451 cells[c].material_id = 0;
2452 }
2453 break;
2454 }
2455
2456 default:
2457 Assert(false, ExcNotImplemented());
2458 }
2459
2460 // Create triangulation
2461 // reorder the cells to ensure that they satisfy the convention for
2462 // edge and face directions
2464 tria.create_triangulation(points, cells, SubCellData());
2465
2466 // Finally assign boundary indicators according to hyper_rectangle
2467 if (colorize)
2468 {
2471 endc = tria.end();
2472 for (; cell != endc; ++cell)
2473 {
2474 for (const unsigned int face : GeometryInfo<dim>::face_indices())
2475 {
2476 if (cell->face(face)->at_boundary())
2477 cell->face(face)->set_boundary_id(face);
2478 }
2479 }
2480 }
2481 }
2482
2483
2484 template <int dim, int spacedim>
2485 void
2487 const unsigned int repetitions,
2488 const double left,
2489 const double right,
2490 const bool colorize)
2491 {
2492 Assert(repetitions >= 1, ExcInvalidRepetitions(repetitions));
2493 Assert(left < right,
2494 ExcMessage("Invalid left-to-right bounds of hypercube"));
2495
2496 Point<dim> p0, p1;
2497 for (unsigned int i = 0; i < dim; ++i)
2498 {
2499 p0[i] = left;
2500 p1[i] = right;
2501 }
2502
2503 std::vector<unsigned int> reps(dim, repetitions);
2505 }
2506
2507
2508
2509 template <int dim, int spacedim>
2510 void
2512 const std::vector<unsigned int> &repetitions,
2513 const Point<dim> & p_1,
2514 const Point<dim> & p_2,
2515 const bool colorize)
2516 {
2517 Assert(repetitions.size() == dim, ExcInvalidRepetitionsDimension(dim));
2518
2519 // First, extend dimensions from dim to spacedim and
2520 // normalize such that p1 is lower in all coordinate
2521 // directions. Additional entries will be 0.
2522 Point<spacedim> p1, p2;
2523 for (unsigned int i = 0; i < dim; ++i)
2524 {
2525 p1(i) = std::min(p_1(i), p_2(i));
2526 p2(i) = std::max(p_1(i), p_2(i));
2527 }
2528
2529 // calculate deltas and validate input
2530 std::array<Point<spacedim>, dim> delta;
2531 for (unsigned int i = 0; i < dim; ++i)
2532 {
2533 Assert(repetitions[i] >= 1, ExcInvalidRepetitions(repetitions[i]));
2534
2535 delta[i][i] = (p2[i] - p1[i]) / repetitions[i];
2536 Assert(
2537 delta[i][i] > 0.0,
2538 ExcMessage(
2539 "The first dim entries of coordinates of p1 and p2 need to be different."));
2540 }
2541
2542 // then generate the points
2543 std::vector<Point<spacedim>> points;
2544 switch (dim)
2545 {
2546 case 1:
2547 for (unsigned int x = 0; x <= repetitions[0]; ++x)
2548 points.push_back(p1 + x * delta[0]);
2549 break;
2550
2551 case 2:
2552 for (unsigned int y = 0; y <= repetitions[1]; ++y)
2553 for (unsigned int x = 0; x <= repetitions[0]; ++x)
2554 points.push_back(p1 + x * delta[0] + y * delta[1]);
2555 break;
2556
2557 case 3:
2558 for (unsigned int z = 0; z <= repetitions[2]; ++z)
2559 for (unsigned int y = 0; y <= repetitions[1]; ++y)
2560 for (unsigned int x = 0; x <= repetitions[0]; ++x)
2561 points.push_back(p1 + x * delta[0] + y * delta[1] +
2562 z * delta[2]);
2563 break;
2564
2565 default:
2566 Assert(false, ExcNotImplemented());
2567 }
2568
2569 // next create the cells
2570 std::vector<CellData<dim>> cells;
2571 switch (dim)
2572 {
2573 case 1:
2574 {
2575 cells.resize(repetitions[0]);
2576 for (unsigned int x = 0; x < repetitions[0]; ++x)
2577 {
2578 cells[x].vertices[0] = x;
2579 cells[x].vertices[1] = x + 1;
2580 cells[x].material_id = 0;
2581 }
2582 break;
2583 }
2584
2585 case 2:
2586 {
2587 cells.resize(repetitions[1] * repetitions[0]);
2588 for (unsigned int y = 0; y < repetitions[1]; ++y)
2589 for (unsigned int x = 0; x < repetitions[0]; ++x)
2590 {
2591 const unsigned int c = x + y * repetitions[0];
2592 cells[c].vertices[0] = y * (repetitions[0] + 1) + x;
2593 cells[c].vertices[1] = y * (repetitions[0] + 1) + x + 1;
2594 cells[c].vertices[2] = (y + 1) * (repetitions[0] + 1) + x;
2595 cells[c].vertices[3] = (y + 1) * (repetitions[0] + 1) + x + 1;
2596 cells[c].material_id = 0;
2597 }
2598 break;
2599 }
2600
2601 case 3:
2602 {
2603 const unsigned int n_x = (repetitions[0] + 1);
2604 const unsigned int n_xy =
2605 (repetitions[0] + 1) * (repetitions[1] + 1);
2606
2607 cells.resize(repetitions[2] * repetitions[1] * repetitions[0]);
2608 for (unsigned int z = 0; z < repetitions[2]; ++z)
2609 for (unsigned int y = 0; y < repetitions[1]; ++y)
2610 for (unsigned int x = 0; x < repetitions[0]; ++x)
2611 {
2612 const unsigned int c = x + y * repetitions[0] +
2613 z * repetitions[0] * repetitions[1];
2614 cells[c].vertices[0] = z * n_xy + y * n_x + x;
2615 cells[c].vertices[1] = z * n_xy + y * n_x + x + 1;
2616 cells[c].vertices[2] = z * n_xy + (y + 1) * n_x + x;
2617 cells[c].vertices[3] = z * n_xy + (y + 1) * n_x + x + 1;
2618 cells[c].vertices[4] = (z + 1) * n_xy + y * n_x + x;
2619 cells[c].vertices[5] = (z + 1) * n_xy + y * n_x + x + 1;
2620 cells[c].vertices[6] = (z + 1) * n_xy + (y + 1) * n_x + x;
2621 cells[c].vertices[7] =
2622 (z + 1) * n_xy + (y + 1) * n_x + x + 1;
2623 cells[c].material_id = 0;
2624 }
2625 break;
2626 }
2627
2628 default:
2629 Assert(false, ExcNotImplemented());
2630 }
2631
2632 tria.create_triangulation(points, cells, SubCellData());
2633
2634 if (colorize)
2635 {
2636 // to colorize, run through all
2637 // faces of all cells and set
2638 // boundary indicator to the
2639 // correct value if it was 0.
2640
2641 // use a large epsilon to
2642 // compare numbers to avoid
2643 // roundoff problems.
2644 double epsilon = 10;
2645 for (unsigned int i = 0; i < dim; ++i)
2646 epsilon = std::min(epsilon, 0.01 * delta[i][i]);
2647 Assert(epsilon > 0,
2648 ExcMessage(
2649 "The distance between corner points must be positive."))
2650
2651 // actual code is external since
2652 // 1-D is different from 2/3D.
2653 colorize_subdivided_hyper_rectangle(tria, p1, p2, epsilon);
2654 }
2655 }
2656
2657
2658
2659 template <int dim>
2660 void
2661 subdivided_hyper_rectangle(Triangulation<dim> & tria,
2662 const std::vector<std::vector<double>> &step_sz,
2663 const Point<dim> & p_1,
2664 const Point<dim> & p_2,
2665 const bool colorize)
2666 {
2667 Assert(step_sz.size() == dim, ExcInvalidRepetitionsDimension(dim));
2668
2669 // First, normalize input such that
2670 // p1 is lower in all coordinate
2671 // directions and check the consistency of
2672 // step sizes, i.e. that they all
2673 // add up to the sizes specified by
2674 // p_1 and p_2
2675 Point<dim> p1(p_1);
2676 Point<dim> p2(p_2);
2677 std::vector<std::vector<double>> step_sizes(step_sz);
2678
2679 for (unsigned int i = 0; i < dim; ++i)
2680 {
2681 if (p1(i) > p2(i))
2682 {
2683 std::swap(p1(i), p2(i));
2684 std::reverse(step_sizes[i].begin(), step_sizes[i].end());
2685 }
2686
2687# ifdef DEBUG
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# endif
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:
2782 Assert(false, ExcNotImplemented());
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:
2850 Assert(false, ExcNotImplemented());
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)
2935 Assert(false, ExcNotImplemented());
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;
3011 for (; cell != endc; ++cell)
3012 {
3013 Point<2> cell_center = cell->center();
3014 for (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:
3193 Assert(false, ExcNotImplemented());
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:
3251 Assert(false, ExcNotImplemented());
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 {
3275 Assert(false, ExcNotImplemented());
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 {
3288 Assert(false, ExcNotImplemented());
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 < GeometryInfo<2>::lines_per_cell; ++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
3430 GridGenerator::merge_triangulations(tria_without_cylinder,
3431 cylinder_tria,
3432 tria,
3433 tolerance);
3434 }
3435
3436 // now set manifold ids:
3437 for (const auto &cell : tria.active_cell_iterators())
3438 {
3439 // set all non-boundary manifold ids on the cells that came from the
3440 // grid around the cylinder to the new TFI manifold id.
3441 if (cell->manifold_id() == tfi_manifold_id)
3442 {
3443 for (const unsigned int face_n : GeometryInfo<2>::face_indices())
3444 {
3445 const auto &face = cell->face(face_n);
3446 if (face->at_boundary() &&
3447 internal::point_in_2d_box(face->center(),
3448 center,
3449 outer_radius))
3450 face->set_manifold_id(polar_manifold_id);
3451 else
3452 face->set_manifold_id(tfi_manifold_id);
3453 }
3454 }
3455 else
3456 {
3457 // ensure that all other manifold ids (including the faces
3458 // opposite the cylinder) are set to the flat id
3459 cell->set_all_manifold_ids(numbers::flat_manifold_id);
3460 }
3461 }
3462
3463 static constexpr double tol =
3464 std::numeric_limits<double>::epsilon() * 10000;
3465 if (colorize)
3466 for (const auto &cell : tria.active_cell_iterators())
3467 for (const unsigned int face_n : GeometryInfo<2>::face_indices())
3468 {
3469 const auto face = cell->face(face_n);
3470 if (face->at_boundary())
3471 {
3472 const Point<2> center = face->center();
3473 // left side
3474 if (std::abs(center[0] - bl[0]) < tol * std::abs(bl[0]))
3475 face->set_boundary_id(0);
3476 // right side
3477 else if (std::abs(center[0] - tr[0]) < tol * std::abs(tr[0]))
3478 face->set_boundary_id(1);
3479 // bottom
3480 else if (std::abs(center[1] - bl[1]) < tol * std::abs(bl[1]))
3481 face->set_boundary_id(2);
3482 // top
3483 else if (std::abs(center[1] - tr[1]) < tol * std::abs(tr[1]))
3484 face->set_boundary_id(3);
3485 // cylinder boundary
3486 else
3487 {
3488 Assert(cell->manifold_id() == tfi_manifold_id,
3490 face->set_boundary_id(4);
3491 }
3492 }
3493 }
3494
3495 // move to the new center
3496 GridTools::shift(new_center, tria);
3497
3498 PolarManifold<2> polar_manifold(new_center);
3499 tria.set_manifold(polar_manifold_id, polar_manifold);
3501 inner_manifold.initialize(tria);
3502 tria.set_manifold(tfi_manifold_id, inner_manifold);
3503 }
3504
3505
3506
3507 template <>
3508 void
3510 const double inner_radius,
3511 const double outer_radius,
3512 const double pad_bottom,
3513 const double pad_top,
3514 const double pad_left,
3515 const double pad_right,
3516 const Point<3> & new_center,
3517 const types::manifold_id polar_manifold_id,
3518 const types::manifold_id tfi_manifold_id,
3519 const double L,
3520 const unsigned int n_slices,
3521 const bool colorize)
3522 {
3523 Triangulation<2> tria_2;
3524 plate_with_a_hole(tria_2,
3525 inner_radius,
3526 outer_radius,
3527 pad_bottom,
3528 pad_top,
3529 pad_left,
3530 pad_right,
3531 Point<2>(new_center[0], new_center[1]),
3532 polar_manifold_id,
3533 tfi_manifold_id,
3534 L,
3535 n_slices,
3536 colorize);
3537
3538 // extrude to 3D
3539 extrude_triangulation(tria_2, n_slices, L, tria, true);
3540
3541 // shift in Z direction to match specified center
3542 GridTools::shift(Point<3>(0, 0, new_center[2] - L / 2.), tria);
3543
3544 // set up the new manifolds
3545 const Tensor<1, 3> direction{{0.0, 0.0, 1.0}};
3546 const CylindricalManifold<3> cylindrical_manifold(
3547 direction,
3548 /*axial_point*/ new_center);
3550 inner_manifold.initialize(tria);
3551 tria.set_manifold(polar_manifold_id, cylindrical_manifold);
3552 tria.set_manifold(tfi_manifold_id, inner_manifold);
3553 }
3554
3555
3556
3557 template <>
3558 void
3560 const double shell_region_width,
3561 const unsigned int n_shells,
3562 const double skewness,
3563 const bool colorize)
3564 {
3565 Assert(0.0 <= shell_region_width && shell_region_width < 0.05,
3566 ExcMessage("The width of the shell region must be less than 0.05 "
3567 "(and preferably close to 0.03)"));
3568 const types::manifold_id polar_manifold_id = 0;
3569 const types::manifold_id tfi_manifold_id = 1;
3570
3571 // We begin by setting up a grid that is 4 by 22 cells. While not
3572 // squares, these have pretty good aspect ratios.
3573 Triangulation<2> bulk_tria;
3575 {22u, 4u},
3576 Point<2>(0.0, 0.0),
3577 Point<2>(2.2, 0.41));
3578 // bulk_tria now looks like this:
3579 //
3580 // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
3581 // | | | | | | | | | | | | | | | | | | | | | | |
3582 // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
3583 // | |XX|XX| | | | | | | | | | | | | | | | | | | |
3584 // +--+--O--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
3585 // | |XX|XX| | | | | | | | | | | | | | | | | | | |
3586 // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
3587 // | | | | | | | | | | | | | | | | | | | | | | |
3588 // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
3589 //
3590 // Note that these cells are not quite squares: they are all 0.1 by
3591 // 0.1025.
3592 //
3593 // The next step is to remove the cells marked with XXs: we will place
3594 // the grid around the cylinder there later. The next loop does two
3595 // things:
3596 // 1. Determines which cells need to be removed from the Triangulation
3597 // (i.e., find the cells marked with XX in the picture).
3598 // 2. Finds the location of the vertex marked with 'O' and uses that to
3599 // calculate the shift vector for aligning cylinder_tria with
3600 // tria_without_cylinder.
3601 std::set<Triangulation<2>::active_cell_iterator> cells_to_remove;
3602 Tensor<1, 2> cylinder_triangulation_offset;
3603 for (const auto &cell : bulk_tria.active_cell_iterators())
3604 {
3605 if ((cell->center() - Point<2>(0.2, 0.2)).norm() < 0.15)
3606 cells_to_remove.insert(cell);
3607
3608 if (cylinder_triangulation_offset == Tensor<1, 2>())
3609 {
3610 for (const unsigned int vertex_n :
3612 if (cell->vertex(vertex_n) == Point<2>())
3613 {
3614 // cylinder_tria is centered at zero, so we need to
3615 // shift it up and to the right by two cells:
3616 cylinder_triangulation_offset =
3617 2.0 * (cell->vertex(3) - Point<2>());
3618 break;
3619 }
3620 }
3621 }
3622 Triangulation<2> tria_without_cylinder;
3624 bulk_tria, cells_to_remove, tria_without_cylinder);
3625
3626 // set up the cylinder triangulation. Note that this function sets the
3627 // manifold ids of the interior boundary cells to 0
3628 // (polar_manifold_id).
3629 Triangulation<2> cylinder_tria;
3631 0.05 + shell_region_width,
3632 0.41 / 4.0);
3633 // The bulk cells are not quite squares, so we need to move the left
3634 // and right sides of cylinder_tria inwards so that it fits in
3635 // bulk_tria:
3636 for (const auto &cell : cylinder_tria.active_cell_iterators())
3637 for (const unsigned int vertex_n : GeometryInfo<2>::vertex_indices())
3638 {
3639 if (std::abs(cell->vertex(vertex_n)[0] - -0.41 / 4.0) < 1e-10)
3640 cell->vertex(vertex_n)[0] = -0.1;
3641 else if (std::abs(cell->vertex(vertex_n)[0] - 0.41 / 4.0) < 1e-10)
3642 cell->vertex(vertex_n)[0] = 0.1;
3643 }
3644
3645 // Assign interior manifold ids to be the TFI id.
3646 for (const auto &cell : cylinder_tria.active_cell_iterators())
3647 {
3648 cell->set_manifold_id(tfi_manifold_id);
3649 for (const unsigned int face_n : GeometryInfo<2>::face_indices())
3650 if (!cell->face(face_n)->at_boundary())
3651 cell->face(face_n)->set_manifold_id(tfi_manifold_id);
3652 }
3653 if (0.0 < shell_region_width)
3654 {
3655 Assert(0 < n_shells,
3656 ExcMessage("If the shell region has positive width then "
3657 "there must be at least one shell."));
3658 Triangulation<2> shell_tria;
3660 Point<2>(),
3661 0.05,
3662 0.05 + shell_region_width,
3663 n_shells,
3664 skewness,
3665 8);
3666
3667 // Make the tolerance as large as possible since these cells can
3668 // be quite close together
3669 const double vertex_tolerance =
3670 std::min(internal::minimal_vertex_distance(shell_tria),
3671 internal::minimal_vertex_distance(cylinder_tria)) *
3672 0.5;
3673
3674 shell_tria.set_all_manifold_ids(polar_manifold_id);
3675 Triangulation<2> temp;
3677 shell_tria, cylinder_tria, temp, vertex_tolerance, true);
3678 cylinder_tria = std::move(temp);
3679 }
3680 GridTools::shift(cylinder_triangulation_offset, cylinder_tria);
3681
3682 // Compute the tolerance again, since the shells may be very close to
3683 // each-other:
3684 const double vertex_tolerance =
3685 std::min(internal::minimal_vertex_distance(tria_without_cylinder),
3686 internal::minimal_vertex_distance(cylinder_tria)) /
3687 10;
3689 tria_without_cylinder, cylinder_tria, tria, vertex_tolerance, true);
3690
3691 // Move the vertices in the middle of the faces of cylinder_tria slightly
3692 // to give a better mesh quality. We have to balance the quality of these
3693 // cells with the quality of the outer cells (initially rectangles). For
3694 // constant radial distance, we would place them at the distance 0.1 *
3695 // sqrt(2.) from the center. In case the shell region width is more than
3696 // 0.1/6., we choose to place them at 0.1 * 4./3. from the center, which
3697 // ensures that the shortest edge of the outer cells is 2./3. of the
3698 // original length. If the shell region width is less, we make the edge
3699 // length of the inner part and outer part (in the shorter x direction)
3700 // the same.
3701 {
3702 const double shift =
3703 std::min(0.125 + shell_region_width * 0.5, 0.1 * 4. / 3.);
3704 for (const auto &cell : tria.active_cell_iterators())
3705 for (const unsigned int v : GeometryInfo<2>::vertex_indices())
3706 if (cell->vertex(v).distance(Point<2>(0.1, 0.205)) < 1e-10)
3707 cell->vertex(v) = Point<2>(0.2 - shift, 0.205);
3708 else if (cell->vertex(v).distance(Point<2>(0.3, 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.2, 0.1025)) < 1e-10)
3711 cell->vertex(v) = Point<2>(0.2, 0.2 - shift);
3712 else if (cell->vertex(v).distance(Point<2>(0.2, 0.3075)) < 1e-10)
3713 cell->vertex(v) = Point<2>(0.2, 0.2 + shift);
3714 }
3715
3716 // Ensure that all manifold ids on a polar cell really are set to the
3717 // polar manifold id:
3718 for (const auto &cell : tria.active_cell_iterators())
3719 if (cell->manifold_id() == polar_manifold_id)
3720 cell->set_all_manifold_ids(polar_manifold_id);
3721
3722 // Ensure that all other manifold ids (including the interior faces
3723 // opposite the cylinder) are set to the flat manifold id:
3724 for (const auto &cell : tria.active_cell_iterators())
3725 if (cell->manifold_id() != polar_manifold_id &&
3726 cell->manifold_id() != tfi_manifold_id)
3727 cell->set_all_manifold_ids(numbers::flat_manifold_id);
3728
3729 // We need to calculate the current center so that we can move it later:
3730 // to start get a unique list of (points to) vertices on the cylinder
3731 std::vector<Point<2> *> cylinder_pointers;
3732 for (const auto &face : tria.active_face_iterators())
3733 if (face->manifold_id() == polar_manifold_id)
3734 {
3735 cylinder_pointers.push_back(&face->vertex(0));
3736 cylinder_pointers.push_back(&face->vertex(1));
3737 }
3738 // de-duplicate
3739 std::sort(cylinder_pointers.begin(), cylinder_pointers.end());
3740 cylinder_pointers.erase(std::unique(cylinder_pointers.begin(),
3741 cylinder_pointers.end()),
3742 cylinder_pointers.end());
3743
3744 // find the current center...
3746 for (const Point<2> *const ptr : cylinder_pointers)
3747 center += *ptr / double(cylinder_pointers.size());
3748
3749 // and recenter at (0.2, 0.2)
3750 for (Point<2> *const ptr : cylinder_pointers)
3751 *ptr += Point<2>(0.2, 0.2) - center;
3752
3753 // attach manifolds
3754 PolarManifold<2> polar_manifold(Point<2>(0.2, 0.2));
3755 tria.set_manifold(polar_manifold_id, polar_manifold);
3757 inner_manifold.initialize(tria);
3758 tria.set_manifold(tfi_manifold_id, inner_manifold);
3759
3760 if (colorize)
3761 for (const auto &face : tria.active_face_iterators())
3762 if (face->at_boundary())
3763 {
3764 const Point<2> center = face->center();
3765 // left side
3766 if (std::abs(center[0] - 0.0) < 1e-10)
3767 face->set_boundary_id(0);
3768 // right side
3769 else if (std::abs(center[0] - 2.2) < 1e-10)
3770 face->set_boundary_id(1);
3771 // cylinder boundary
3772 else if (face->manifold_id() == polar_manifold_id)
3773 face->set_boundary_id(2);
3774 // sides of channel
3775 else
3776 {
3777 Assert(std::abs(center[1] - 0.00) < 1.0e-10 ||
3778 std::abs(center[1] - 0.41) < 1.0e-10,
3780 face->set_boundary_id(3);
3781 }
3782 }
3783 }
3784
3785
3786
3787 template <>
3788 void
3790 const double shell_region_width,
3791 const unsigned int n_shells,
3792 const double skewness,
3793 const bool colorize)
3794 {
3795 Triangulation<2> tria_2;
3797 tria_2, shell_region_width, n_shells, skewness, colorize);
3798 extrude_triangulation(tria_2, 5, 0.41, tria, true);
3799
3800 // set up the new 3D manifolds
3801 const types::manifold_id cylindrical_manifold_id = 0;
3802 const types::manifold_id tfi_manifold_id = 1;
3803 const PolarManifold<2> *const m_ptr =
3804 dynamic_cast<const PolarManifold<2> *>(
3805 &tria_2.get_manifold(cylindrical_manifold_id));
3806 Assert(m_ptr != nullptr, ExcInternalError());
3807 const Point<3> axial_point(m_ptr->center[0], m_ptr->center[1], 0.0);
3808 const Tensor<1, 3> direction{{0.0, 0.0, 1.0}};
3809
3810 const CylindricalManifold<3> cylindrical_manifold(direction, axial_point);
3812 inner_manifold.initialize(tria);
3813 tria.set_manifold(cylindrical_manifold_id, cylindrical_manifold);
3814 tria.set_manifold(tfi_manifold_id, inner_manifold);
3815
3816 // From extrude_triangulation: since the maximum boundary id of tria_2 was
3817 // 3, the bottom boundary id is 4 and the top is 5: both are walls, so set
3818 // them to 3
3819 if (colorize)
3820 for (const auto &face : tria.active_face_iterators())
3821 if (face->boundary_id() == 4 || face->boundary_id() == 5)
3822 face->set_boundary_id(3);
3823 }
3824
3825
3826
3827 template <int dim, int spacedim>
3828 void
3830 const std::vector<unsigned int> &sizes,
3831 const bool colorize)
3832 {
3834 Assert(dim > 1, ExcNotImplemented());
3835 Assert(dim < 4, ExcNotImplemented());
3836
3837 // If there is a desire at some point to change the geometry of
3838 // the cells, this tensor can be made an argument to the function.
3839 Tensor<1, dim> dimensions;
3840 for (unsigned int d = 0; d < dim; ++d)
3841 dimensions[d] = 1.;
3842
3843 std::vector<Point<spacedim>> points;
3844 unsigned int n_cells = 1;
3845 for (unsigned int i : GeometryInfo<dim>::face_indices())
3846 n_cells += sizes[i];
3847
3848 std::vector<CellData<dim>> cells(n_cells);
3849 // Vertices of the center cell
3850 for (const unsigned int i : GeometryInfo<dim>::vertex_indices())
3851 {
3853 for (unsigned int d = 0; d < dim; ++d)
3854 p(d) = 0.5 * dimensions[d] *
3857 points.push_back(p);
3858 cells[0].vertices[i] = i;
3859 }
3860 cells[0].material_id = 0;
3861
3862 // The index of the first cell of the leg.
3863 unsigned int cell_index = 1;
3864 // The legs of the cross
3865 for (const unsigned int face : GeometryInfo<dim>::face_indices())
3866 {
3867 const unsigned int oface = GeometryInfo<dim>::opposite_face[face];
3868 const unsigned int dir = GeometryInfo<dim>::unit_normal_direction[face];
3869
3870 // We are moving in the direction of face
3871 for (unsigned int j = 0; j < sizes[face]; ++j, ++cell_index)
3872 {
3873 const unsigned int last_cell = (j == 0) ? 0U : (cell_index - 1);
3874
3875 for (unsigned int v = 0; v < GeometryInfo<dim>::vertices_per_face;
3876 ++v)
3877 {
3878 const unsigned int cellv =
3880 const unsigned int ocellv =
3882 // First the vertices which already exist
3883 cells[cell_index].vertices[ocellv] =
3884 cells[last_cell].vertices[cellv];
3885
3886 // Now the new vertices
3887 cells[cell_index].vertices[cellv] = points.size();
3888
3889 Point<spacedim> p = points[cells[cell_index].vertices[ocellv]];
3891 dimensions[dir];
3892 points.push_back(p);
3893 }
3894 cells[cell_index].material_id = (colorize) ? (face + 1U) : 0U;
3895 }
3896 }
3897 tria.create_triangulation(points, cells, SubCellData());
3898 }
3899
3900
3901 template <>
3902 void
3903 hyper_cube_slit(Triangulation<1> &, const double, const double, const bool)
3904 {
3905 Assert(false, ExcNotImplemented());
3906 }
3907
3908
3909
3910 template <>
3911 void
3913 const double,
3914 const double,
3915 const double,
3916 const bool)
3917 {
3918 Assert(false, ExcNotImplemented());
3919 }
3920
3921
3922
3923 template <>
3924 void
3925 hyper_L(Triangulation<1> &, const double, const double, const bool)
3926 {
3927 Assert(false, ExcNotImplemented());
3928 }
3929
3930
3931
3932 template <>
3933 void
3934 hyper_ball(Triangulation<1> &, const Point<1> &, const double, const bool)
3935 {
3936 Assert(false, ExcNotImplemented());
3937 }
3938
3939
3940
3941 template <>
3942 void
3943 hyper_ball_balanced(Triangulation<1> &, const Point<1> &, const double)
3944 {
3945 Assert(false, ExcNotImplemented());
3946 }
3947
3948
3949
3950 template <>
3951 void
3952 cylinder(Triangulation<1> &, const double, const double)
3953 {
3954 Assert(false, ExcNotImplemented());
3955 }
3956
3957
3958 template <>
3959 void
3961 const unsigned int,
3962 const double,
3963 const double)
3964 {
3965 Assert(false, ExcNotImplemented());
3966 }
3967
3968
3969
3970 template <>
3971 void
3972 truncated_cone(Triangulation<1> &, const double, const double, const double)
3973 {
3974 Assert(false, ExcNotImplemented());
3975 }
3976
3977
3978
3979 template <>
3980 void
3982 const Point<1> &,
3983 const double,
3984 const double,
3985 const unsigned int,
3986 const bool)
3987 {
3988 Assert(false, ExcNotImplemented());
3989 }
3990
3991 template <>
3992 void
3994 const double,
3995 const double,
3996 const double,
3997 const unsigned int,
3998 const unsigned int)
3999 {
4000 Assert(false, ExcNotImplemented());
4001 }
4002
4003
4004 template <>
4005 void
4006 quarter_hyper_ball(Triangulation<1> &, const Point<1> &, const double)
4007 {
4008 Assert(false, ExcNotImplemented());
4009 }
4010
4011
4012 template <>
4013 void
4014 half_hyper_ball(Triangulation<1> &, const Point<1> &, const double)
4015 {
4016 Assert(false, ExcNotImplemented());
4017 }
4018
4019
4020 template <>
4021 void
4023 const Point<1> &,
4024 const double,
4025 const double,
4026 const unsigned int,
4027 const bool)
4028 {
4029 Assert(false, ExcNotImplemented());
4030 }
4031
4032 template <>
4033 void
4035 const Point<1> &,
4036 const double,
4037 const double,
4038 const unsigned int,
4039 const bool)
4040 {
4041 Assert(false, ExcNotImplemented());
4042 }
4043
4044 template <>
4045 void
4047 const double left,
4048 const double right,
4049 const double thickness,
4050 const bool colorize)
4051 {
4052 Assert(left < right,
4053 ExcMessage("Invalid left-to-right bounds of enclosed hypercube"));
4054
4055 std::vector<Point<2>> vertices(16);
4056 double coords[4];
4057 coords[0] = left - thickness;
4058 coords[1] = left;
4059 coords[2] = right;
4060 coords[3] = right + thickness;
4061
4062 unsigned int k = 0;
4063 for (const double y : coords)
4064 for (const double x : coords)
4065 vertices[k++] = Point<2>(x, y);
4066
4067 const types::material_id materials[9] = {5, 4, 6, 1, 0, 2, 9, 8, 10};
4068
4069 std::vector<CellData<2>> cells(9);
4070 k = 0;
4071 for (unsigned int i0 = 0; i0 < 3; ++i0)
4072 for (unsigned int i1 = 0; i1 < 3; ++i1)
4073 {
4074 cells[k].vertices[0] = i1 + 4 * i0;
4075 cells[k].vertices[1] = i1 + 4 * i0 + 1;
4076 cells[k].vertices[2] = i1 + 4 * i0 + 4;
4077 cells[k].vertices[3] = i1 + 4 * i0 + 5;
4078 if (colorize)
4079 cells[k].material_id = materials[k];
4080 ++k;
4081 }
4083 cells,
4084 SubCellData()); // no boundary information
4085 }
4086
4087
4088
4089 // Implementation for 2D only
4090 template <>
4091 void
4093 const double left,
4094 const double right,
4095 const bool colorize)
4096 {
4097 const double rl2 = (right + left) / 2;
4098 const Point<2> vertices[10] = {Point<2>(left, left),
4099 Point<2>(rl2, left),
4100 Point<2>(rl2, rl2),
4101 Point<2>(left, rl2),
4102 Point<2>(right, left),
4103 Point<2>(right, rl2),
4104 Point<2>(rl2, right),
4105 Point<2>(left, right),
4106 Point<2>(right, right),
4107 Point<2>(rl2, left)};
4108 const int cell_vertices[4][4] = {{0, 1, 3, 2},
4109 {9, 4, 2, 5},
4110 {3, 2, 7, 6},
4111 {2, 5, 6, 8}};
4112 std::vector<CellData<2>> cells(4, CellData<2>());
4113 for (unsigned int i = 0; i < 4; ++i)
4114 {
4115 for (unsigned int j = 0; j < 4; ++j)
4116 cells[i].vertices[j] = cell_vertices[i][j];
4117 cells[i].material_id = 0;
4118 }
4119 tria.create_triangulation(std::vector<Point<2>>(std::begin(vertices),
4120 std::end(vertices)),
4121 cells,
4122 SubCellData()); // no boundary information
4123
4124 if (colorize)
4125 {
4127 cell->face(1)->set_boundary_id(1);
4128 ++cell;
4129 cell->face(0)->set_boundary_id(2);
4130 }
4131 }
4132
4133
4134
4135 template <>
4136 void
4138 const double radius_0,
4139 const double radius_1,
4140 const double half_length)
4141 {
4142 Point<2> vertices_tmp[4];
4143
4144 vertices_tmp[0] = Point<2>(-half_length, -radius_0);
4145 vertices_tmp[1] = Point<2>(half_length, -radius_1);
4146 vertices_tmp[2] = Point<2>(-half_length, radius_0);
4147 vertices_tmp[3] = Point<2>(half_length, radius_1);
4148
4149 const std::vector<Point<2>> vertices(std::begin(vertices_tmp),
4150 std::end(vertices_tmp));
4151 unsigned int cell_vertices[1][GeometryInfo<2>::vertices_per_cell];
4152
4153 for (const unsigned int i : GeometryInfo<2>::vertex_indices())
4154 cell_vertices[0][i] = i;
4155
4156 std::vector<CellData<2>> cells(1, CellData<2>());
4157
4158 for (const unsigned int i : GeometryInfo<2>::vertex_indices())
4159 cells[0].vertices[i] = cell_vertices[0][i];
4160
4161 cells[0].material_id = 0;
4162 triangulation.create_triangulation(vertices, cells, SubCellData());
4163
4165
4166 cell->face(0)->set_boundary_id(1);
4167 cell->face(1)->set_boundary_id(2);
4168
4169 for (unsigned int i = 2; i < 4; ++i)
4170 cell->face(i)->set_boundary_id(0);
4171 }
4172
4173
4174
4175 // Implementation for 2D only
4176 template <>
4177 void
4179 const double a,
4180 const double b,
4181 const bool colorize)
4182 {
4183 const Point<2> vertices[8] = {Point<2>(a, a),
4184 Point<2>((a + b) / 2, a),
4185 Point<2>(b, a),
4186 Point<2>(a, (a + b) / 2),
4187 Point<2>((a + b) / 2, (a + b) / 2),
4188 Point<2>(b, (a + b) / 2),
4189 Point<2>(a, b),
4190 Point<2>((a + b) / 2, b)};
4191 const int cell_vertices[3][4] = {{0, 1, 3, 4}, {1, 2, 4, 5}, {3, 4, 6, 7}};
4192
4193 std::vector<CellData<2>> cells(3, CellData<2>());
4194
4195 for (unsigned int i = 0; i < 3; ++i)
4196 {
4197 for (unsigned int j = 0; j < 4; ++j)
4198 cells[i].vertices[j] = cell_vertices[i][j];
4199 cells[i].material_id = 0;
4200 }
4201
4202 tria.create_triangulation(std::vector<Point<2>>(std::begin(vertices),
4203 std::end(vertices)),
4204 cells,
4205 SubCellData());
4206
4207 if (colorize)
4208 {
4210
4211 cell->face(0)->set_boundary_id(0);
4212 cell->face(2)->set_boundary_id(1);
4213 cell++;
4214
4215 cell->face(1)->set_boundary_id(2);
4216 cell->face(2)->set_boundary_id(1);
4217 cell->face(3)->set_boundary_id(3);
4218 cell++;
4219
4220 cell->face(0)->set_boundary_id(0);
4221 cell->face(1)->set_boundary_id(4);
4222 cell->face(3)->set_boundary_id(5);
4223 }
4224 }
4225
4226
4227
4228 template <int dim, int spacedim>
4229 void
4231 const std::vector<unsigned int> &repetitions,
4232 const Point<dim> & bottom_left,
4233 const Point<dim> & top_right,
4234 const std::vector<int> & n_cells_to_remove)
4235 {
4236 Assert(dim > 1, ExcNotImplemented());
4237 // Check the consistency of the dimensions provided.
4238 AssertDimension(repetitions.size(), dim);
4239 AssertDimension(n_cells_to_remove.size(), dim);
4240 for (unsigned int d = 0; d < dim; ++d)
4241 {
4242 Assert(std::fabs(n_cells_to_remove[d]) <= repetitions[d],
4243 ExcMessage("Attempting to cut away too many cells."));
4244 }
4245 // Create the domain to be cut
4248 repetitions,
4249 bottom_left,
4250 top_right);
4251 // compute the vertex of the cut step, we will cut according to the
4252 // location of the cartesian coordinates of the cell centers
4253 std::array<double, dim> h;
4254 Point<dim> cut_step;
4255 for (unsigned int d = 0; d < dim; ++d)
4256 {
4257 // mesh spacing in each direction in cartesian coordinates
4258 h[d] = (top_right[d] - bottom_left[d]) / repetitions[d];
4259 // left to right, bottom to top, front to back
4260 if (n_cells_to_remove[d] >= 0)
4261 {
4262 // cartesian coordinates of vertex location
4263 cut_step[d] =
4264 h[d] * std::fabs(n_cells_to_remove[d]) + bottom_left[d];
4265 }
4266 // right to left, top to bottom, back to front
4267 else
4268 {
4269 cut_step[d] = top_right[d] - h[d] * std::fabs(n_cells_to_remove[d]);
4270 }
4271 }
4272
4273
4274 // compute cells to remove
4275 std::set<typename Triangulation<dim, spacedim>::active_cell_iterator>
4276 cells_to_remove;
4277 for (const auto &cell : rectangle.active_cell_iterators())
4278 {
4279 bool remove_cell = true;
4280 for (unsigned int d = 0; d < dim && remove_cell; ++d)
4281 if ((n_cells_to_remove[d] > 0 && cell->center()[d] >= cut_step[d]) ||
4282 (n_cells_to_remove[d] < 0 && cell->center()[d] <= cut_step[d]))
4283 remove_cell = false;
4284 if (remove_cell)
4285 cells_to_remove.insert(cell);
4286 }
4287
4289 cells_to_remove,
4290 tria);
4291 }
4292
4293
4294
4295 // Implementation for 2D only
4296 template <>
4297 void
4299 const Point<2> & p,
4300 const double radius,
4301 const bool internal_manifolds)
4302 {
4303 // equilibrate cell sizes at
4304 // transition from the inner part
4305 // to the radial cells
4306 const double a = 1. / (1 + std::sqrt(2.0));
4307 const Point<2> vertices[8] = {
4308 p + Point<2>(-1, -1) * (radius / std::sqrt(2.0)),
4309 p + Point<2>(+1, -1) * (radius / std::sqrt(2.0)),
4310 p + Point<2>(-1, -1) * (radius / std::sqrt(2.0) * a),
4311 p + Point<2>(+1, -1) * (radius / std::sqrt(2.0) * a),
4312 p + Point<2>(-1, +1) * (radius / std::sqrt(2.0) * a),
4313 p + Point<2>(+1, +1) * (radius / std::sqrt(2.0) * a),
4314 p + Point<2>(-1, +1) * (radius / std::sqrt(2.0)),
4315 p + Point<2>(+1, +1) * (radius / std::sqrt(2.0))};
4316
4317 const int cell_vertices[5][4] = {
4318 {0, 1, 2, 3}, {0, 2, 6, 4}, {2, 3, 4, 5}, {1, 7, 3, 5}, {6, 4, 7, 5}};
4319
4320 std::vector<CellData<2>> cells(5, CellData<2>());
4321
4322 for (unsigned int i = 0; i < 5; ++i)
4323 {
4324 for (unsigned int j = 0; j < 4; ++j)
4325 cells[i].vertices[j] = cell_vertices[i][j];
4326 cells[i].material_id = 0;
4327 cells[i].manifold_id = i == 2 ? numbers::flat_manifold_id : 1;
4328 }
4329
4330 tria.create_triangulation(std::vector<Point<2>>(std::begin(vertices),
4331 std::end(vertices)),
4332 cells,
4333 SubCellData()); // no boundary information
4336 if (internal_manifolds)
4338 }
4339
4340
4341
4342 template <>
4343 void
4345 const Point<2> & center,
4346 const double inner_radius,
4347 const double outer_radius,
4348 const unsigned int n_cells,
4349 const bool colorize)
4350 {
4351 Assert((inner_radius > 0) && (inner_radius < outer_radius),
4352 ExcInvalidRadii());
4353
4354 const double pi = numbers::PI;
4355
4356 // determine the number of cells
4357 // for the grid. if not provided by
4358 // the user determine it such that
4359 // the length of each cell on the
4360 // median (in the middle between
4361 // the two circles) is equal to its
4362 // radial extent (which is the
4363 // difference between the two
4364 // radii)
4365 const unsigned int N =
4366 (n_cells == 0 ? static_cast<unsigned int>(
4367 std::ceil((2 * pi * (outer_radius + inner_radius) / 2) /
4368 (outer_radius - inner_radius))) :
4369 n_cells);
4370
4371 // set up N vertices on the
4372 // outer and N vertices on
4373 // the inner circle. the
4374 // first N ones are on the
4375 // outer one, and all are
4376 // numbered counter-clockwise
4377 std::vector<Point<2>> vertices(2 * N);
4378 for (unsigned int i = 0; i < N; ++i)
4379 {
4380 vertices[i] =
4381 Point<2>(std::cos(2 * pi * i / N), std::sin(2 * pi * i / N)) *
4382 outer_radius;
4383 vertices[i + N] = vertices[i] * (inner_radius / outer_radius);
4384
4385 vertices[i] += center;
4386 vertices[i + N] += center;
4387 }
4388
4389 std::vector<CellData<2>> cells(N, CellData<2>());
4390
4391 for (unsigned int i = 0; i < N; ++i)
4392 {
4393 cells[i].vertices[0] = i;
4394 cells[i].vertices[1] = (i + 1) % N;
4395 cells[i].vertices[2] = N + i;
4396 cells[i].vertices[3] = N + ((i + 1) % N);
4397
4398 cells[i].material_id = 0;
4399 }
4400
4402
4403 if (colorize)
4404 colorize_hyper_shell(tria, center, inner_radius, outer_radius);
4405
4408 }
4409
4410
4411
4412 template <int dim>
4413 void
4415 const Point<dim> & inner_center,
4416 const Point<dim> & outer_center,
4417 const double inner_radius,
4418 const double outer_radius,
4419 const unsigned int n_cells)
4420 {
4422 tria, outer_center, inner_radius, outer_radius, n_cells, true);
4423
4424 // check the consistency of the dimensions provided
4425 Assert(
4426 outer_radius - inner_radius > outer_center.distance(inner_center),
4428 "The inner radius is greater than or equal to the outer radius plus eccentricity."));
4429
4430 // shift nodes along the inner boundary according to the position of
4431 // inner_circle
4432 std::set<Point<dim> *> vertices_to_move;
4433
4434 for (const auto &face : tria.active_face_iterators())
4435 if (face->boundary_id() == 0)
4436 for (unsigned int v = 0; v < GeometryInfo<dim>::vertices_per_face; ++v)
4437 vertices_to_move.insert(&face->vertex(v));
4438
4439 const auto shift = inner_center - outer_center;
4440 for (const auto &p : vertices_to_move)
4441 (*p) += shift;
4442
4443 // the original hyper_shell function assigns the same manifold id
4444 // to all cells and faces. Set all manifolds ids to a different
4445 // value (2), then use boundary ids to assign different manifolds to
4446 // the inner (0) and outer manifolds (1). Use a transfinite manifold
4447 // for all faces and cells aside from the boundaries.
4450
4451 SphericalManifold<dim> inner_manifold(inner_center);
4452 SphericalManifold<dim> outer_manifold(outer_center);
4453
4455 transfinite.initialize(tria);
4456
4457 tria.set_manifold(0, inner_manifold);
4458 tria.set_manifold(1, outer_manifold);
4459 tria.set_manifold(2, transfinite);
4460 }
4461
4462
4463
4464 // Implementation for 2D only
4465 template <>
4466 void
4468 const double radius,
4469 const double half_length)
4470 {
4471 Point<2> p1(-half_length, -radius);
4472 Point<2> p2(half_length, radius);
4473
4474 hyper_rectangle(tria, p1, p2, true);
4475
4478 while (f != end)
4479 {
4480 switch (f->boundary_id())
4481 {
4482 case 0:
4483 f->set_boundary_id(1);
4484 break;
4485 case 1:
4486 f->set_boundary_id(2);
4487 break;
4488 default:
4489 f->set_boundary_id(0);
4490 break;
4491 }
4492 ++f;
4493 }
4494 }
4495
4496 template <>
4497 void
4499 const unsigned int,
4500 const double,
4501 const double)
4502 {
4503 Assert(false, ExcNotImplemented());
4504 }
4505
4506
4507
4508 // Implementation for 2D only
4509 template <>
4510 void
4512 const double,
4513 const double,
4514 const double,
4515 const unsigned int,
4516 const unsigned int)
4517 {
4518 Assert(false, ExcNotImplemented());
4519 }
4520
4521
4522 template <>
4523 void
4525 const Point<2> & p,
4526 const double radius)
4527 {
4528 const unsigned int dim = 2;
4529
4530 // the numbers 0.55647 and 0.42883 have been found by a search for the
4531 // best aspect ratio (defined as the maximal between the minimal singular
4532 // value of the Jacobian)
4533 const Point<dim> vertices[7] = {p + Point<dim>(0, 0) * radius,
4534 p + Point<dim>(+1, 0) * radius,
4535 p + Point<dim>(+1, 0) * (radius * 0.55647),
4536 p + Point<dim>(0, +1) * (radius * 0.55647),
4537 p + Point<dim>(+1, +1) * (radius * 0.42883),
4538 p + Point<dim>(0, +1) * radius,
4539 p + Point<dim>(+1, +1) *
4540 (radius / std::sqrt(2.0))};
4541
4542 const int cell_vertices[3][4] = {{0, 2, 3, 4}, {1, 6, 2, 4}, {5, 3, 6, 4}};
4543
4544 std::vector<CellData<dim>> cells(3, CellData<dim>());
4545
4546 for (unsigned int i = 0; i < 3; ++i)
4547 {
4548 for (unsigned int j = 0; j < 4; ++j)
4549 cells[i].vertices[j] = cell_vertices[i][j];
4550 cells[i].material_id = 0;
4551 }
4552
4553 tria.create_triangulation(std::vector<Point<dim>>(std::begin(vertices),
4554 std::end(vertices)),
4555 cells,
4556 SubCellData()); // no boundary information
4557
4560
4562
4563 while (cell != end)
4564 {
4565 for (unsigned int i : GeometryInfo<dim>::face_indices())
4566 {
4567 if (cell->face(i)->boundary_id() ==
4569 continue;
4570
4571 // If one the components is the same as the respective
4572 // component of the center, then this is part of the plane
4573 if (cell->face(i)->center()(0) < p(0) + 1.e-5 * radius ||
4574 cell->face(i)->center()(1) < p(1) + 1.e-5 * radius)
4575 {
4576 cell->face(i)->set_boundary_id(1);
4577 cell->face(i)->set_manifold_id(numbers::flat_manifold_id);
4578 }
4579 }
4580 ++cell;
4581 }
4583 }
4584
4585
4586 template <>
4587 void
4589 const Point<2> & p,
4590 const double radius)
4591 {
4592 // equilibrate cell sizes at
4593 // transition from the inner part
4594 // to the radial cells
4595 const double a = 1. / (1 + std::sqrt(2.0));
4596 const Point<2> vertices[8] = {
4597 p + Point<2>(0, -1) * radius,
4598 p + Point<2>(+1, -1) * (radius / std::sqrt(2.0)),
4599 p + Point<2>(0, -1) * (radius / std::sqrt(2.0) * a),
4600 p + Point<2>(+1, -1) * (radius / std::sqrt(2.0) * a),
4601 p + Point<2>(0, +1) * (radius / std::sqrt(2.0) * a),
4602 p + Point<2>(+1, +1) * (radius / std::sqrt(2.0) * a),
4603 p + Point<2>(0, +1) * radius,
4604 p + Point<2>(+1, +1) * (radius / std::sqrt(2.0))};
4605
4606 const int cell_vertices[5][4] = {{0, 1, 2, 3},
4607 {2, 3, 4, 5},
4608 {1, 7, 3, 5},
4609 {6, 4, 7, 5}};
4610
4611 std::vector<CellData<2>> cells(4, CellData<2>());
4612
4613 for (unsigned int i = 0; i < 4; ++i)
4614 {
4615 for (unsigned int j = 0; j < 4; ++j)
4616 cells[i].vertices[j] = cell_vertices[i][j];
4617 cells[i].material_id = 0;
4618 }
4619
4620 tria.create_triangulation(std::vector<Point<2>>(std::begin(vertices),
4621 std::end(vertices)),
4622 cells,
4623 SubCellData()); // no boundary information
4624
4627
4629
4630 while (cell != end)
4631 {
4632 for (unsigned int i : GeometryInfo<2>::face_indices())
4633 {
4634 if (cell->face(i)->boundary_id() ==
4636 continue;
4637
4638 // If x is zero, then this is part of the plane
4639 if (cell->face(i)->center()(0) < p(0) + 1.e-5 * radius)
4640 {
4641 cell->face(i)->set_boundary_id(1);
4642 cell->face(i)->set_manifold_id(numbers::flat_manifold_id);
4643 }
4644 }
4645 ++cell;
4646 }
4648 }
4649
4650
4651
4652 // Implementation for 2D only
4653 template <>
4654 void
4656 const Point<2> & center,
4657 const double inner_radius,
4658 const double outer_radius,
4659 const unsigned int n_cells,
4660 const bool colorize)
4661 {
4662 Assert((inner_radius > 0) && (inner_radius < outer_radius),
4663 ExcInvalidRadii());
4664
4665 const double pi = numbers::PI;
4666 // determine the number of cells
4667 // for the grid. if not provided by
4668 // the user determine it such that
4669 // the length of each cell on the
4670 // median (in the middle between
4671 // the two circles) is equal to its
4672 // radial extent (which is the
4673 // difference between the two
4674 // radii)
4675 const unsigned int N =
4676 (n_cells == 0 ? static_cast<unsigned int>(
4677 std::ceil((pi * (outer_radius + inner_radius) / 2) /
4678 (outer_radius - inner_radius))) :
4679 n_cells);
4680
4681 // set up N+1 vertices on the
4682 // outer and N+1 vertices on
4683 // the inner circle. the
4684 // first N+1 ones are on the
4685 // outer one, and all are
4686 // numbered counter-clockwise
4687 std::vector<Point<2>> vertices(2 * (N + 1));
4688 for (unsigned int i = 0; i <= N; ++i)
4689 {
4690 // enforce that the x-coordinates
4691 // of the first and last point of
4692 // each half-circle are exactly
4693 // zero (contrary to what we may
4694 // compute using the imprecise
4695 // value of pi)
4696 vertices[i] =
4697 Point<2>(((i == 0) || (i == N) ? 0 : std::cos(pi * i / N - pi / 2)),
4698 std::sin(pi * i / N - pi / 2)) *
4699 outer_radius;
4700 vertices[i + N + 1] = vertices[i] * (inner_radius / outer_radius);
4701
4702 vertices[i] += center;
4703 vertices[i + N + 1] += center;
4704 }
4705
4706
4707 std::vector<CellData<2>> cells(N, CellData<2>());
4708
4709 for (unsigned int i = 0; i < N; ++i)
4710 {
4711 cells[i].vertices[0] = i;
4712 cells[i].vertices[1] = (i + 1) % (N + 1);
4713 cells[i].vertices[2] = N + 1 + i;
4714 cells[i].vertices[3] = N + 1 + ((i + 1) % (N + 1));
4715
4716 cells[i].material_id = 0;
4717 }
4718
4720
4721 if (colorize)
4722 {
4724 for (; cell != tria.end(); ++cell)
4725 {
4726 cell->face(2)->set_boundary_id(1);
4727 }
4728 tria.begin()->face(0)->set_boundary_id(3);
4729
4730 tria.last()->face(1)->set_boundary_id(2);
4731 }
4734 }
4735
4736
4737 template <>
4738 void
4740 const Point<2> & center,
4741 const double inner_radius,
4742 const double outer_radius,
4743 const unsigned int n_cells,
4744 const bool colorize)
4745 {
4746 Assert((inner_radius > 0) && (inner_radius < outer_radius),
4747 ExcInvalidRadii());
4748
4749 const double pi = numbers::PI;
4750 // determine the number of cells
4751 // for the grid. if not provided by
4752 // the user determine it such that
4753 // the length of each cell on the
4754 // median (in the middle between
4755 // the two circles) is equal to its
4756 // radial extent (which is the
4757 // difference between the two
4758 // radii)
4759 const unsigned int N =
4760 (n_cells == 0 ? static_cast<unsigned int>(
4761 std::ceil((pi * (outer_radius + inner_radius) / 4) /
4762 (outer_radius - inner_radius))) :
4763 n_cells);
4764
4765 // set up N+1 vertices on the
4766 // outer and N+1 vertices on
4767 // the inner circle. the
4768 // first N+1 ones are on the
4769 // outer one, and all are
4770 // numbered counter-clockwise
4771 std::vector<Point<2>> vertices(2 * (N + 1));
4772 for (unsigned int i = 0; i <= N; ++i)
4773 {
4774 // enforce that the x-coordinates
4775 // of the last point is exactly
4776 // zero (contrary to what we may
4777 // compute using the imprecise
4778 // value of pi)
4779 vertices[i] = Point<2>(((i == N) ? 0 : std::cos(pi * i / N / 2)),
4780 std::sin(pi * i / N / 2)) *
4781 outer_radius;
4782 vertices[i + N + 1] = vertices[i] * (inner_radius / outer_radius);
4783
4784 vertices[i] += center;
4785 vertices[i + N + 1] += center;
4786 }
4787
4788
4789 std::vector<CellData<2>> cells(N, CellData<2>());
4790
4791 for (unsigned int i = 0; i < N; ++i)
4792 {
4793 cells[i].vertices[0] = i;
4794 cells[i].vertices[1] = (i + 1) % (N + 1);
4795 cells[i].vertices[2] = N + 1 + i;
4796 cells[i].vertices[3] = N + 1 + ((i + 1) % (N + 1));
4797
4798 cells[i].material_id = 0;
4799 }
4800
4802
4803 if (colorize)
4804 {
4806 for (; cell != tria.end(); ++cell)
4807 {
4808 cell->face(2)->set_boundary_id(1);
4809 }
4810 tria.begin()->face(0)->set_boundary_id(3);
4811
4812 tria.last()->face(1)->set_boundary_id(2);
4813 }
4814
4817 }
4818
4819
4820
4821 // Implementation for 3D only
4822 template <>
4823 void
4825 const double left,
4826 const double right,
4827 const bool colorize)
4828 {
4829 const double rl2 = (right + left) / 2;
4830 const double len = (right - left) / 2.;
4831
4832 const Point<3> vertices[20] = {
4833 Point<3>(left, left, -len / 2.), Point<3>(rl2, left, -len / 2.),
4834 Point<3>(rl2, rl2, -len / 2.), Point<3>(left, rl2, -len / 2.),
4835 Point<3>(right, left, -len / 2.), Point<3>(right, rl2, -len / 2.),
4836 Point<3>(rl2, right, -len / 2.), Point<3>(left, right, -len / 2.),
4837 Point<3>(right, right, -len / 2.), Point<3>(rl2, left, -len / 2.),
4838 Point<3>(left, left, len / 2.), Point<3>(rl2, left, len / 2.),
4839 Point<3>(rl2, rl2, len / 2.), Point<3>(left, rl2, len / 2.),
4840 Point<3>(right, left, len / 2.), Point<3>(right, rl2, len / 2.),
4841 Point<3>(rl2, right, len / 2.), Point<3>(left, right, len / 2.),
4842 Point<3>(right, right, len / 2.), Point<3>(rl2, left, len / 2.)};
4843 const int cell_vertices[4][8] = {{0, 1, 3, 2, 10, 11, 13, 12},
4844 {9, 4, 2, 5, 19, 14, 12, 15},
4845 {3, 2, 7, 6, 13, 12, 17, 16},
4846 {2, 5, 6, 8, 12, 15, 16, 18}};
4847 std::vector<CellData<3>> cells(4, CellData<3>());
4848 for (unsigned int i = 0; i < 4; ++i)
4849 {
4850 for (unsigned int j = 0; j < 8; ++j)
4851 cells[i].vertices[j] = cell_vertices[i][j];
4852 cells[i].material_id = 0;
4853 }
4854 tria.create_triangulation(std::vector<Point<3>>(std::begin(vertices),
4855 std::end(vertices)),
4856 cells,
4857 SubCellData()); // no boundary information
4858
4859 if (colorize)
4860 {
4862 cell->face(1)->set_boundary_id(1);
4863 ++cell;
4864 cell->face(0)->set_boundary_id(2);
4865 }
4866 }
4867
4868
4869
4870 // Implementation for 3D only
4871 template <>
4872 void
4874 const double left,
4875 const double right,
4876 const double thickness,
4877 const bool colorize)
4878 {
4879 Assert(left < right,
4880 ExcMessage("Invalid left-to-right bounds of enclosed hypercube"));
4881
4882 std::vector<Point<3>> vertices(64);
4883 double coords[4];
4884 coords[0] = left - thickness;
4885 coords[1] = left;
4886 coords[2] = right;
4887 coords[3] = right + thickness;
4888
4889 unsigned int k = 0;
4890 for (const double z : coords)
4891 for (const double y : coords)
4892 for (const double x : coords)
4893 vertices[k++] = Point<3>(x, y, z);
4894
4895 const types::material_id materials[27] = {21, 20, 22, 17, 16, 18, 25,
4896 24, 26, 5, 4, 6, 1, 0,
4897 2, 9, 8, 10, 37, 36, 38,
4898 33, 32, 34, 41, 40, 42};
4899
4900 std::vector<CellData<3>> cells(27);
4901 k = 0;
4902 for (unsigned int z = 0; z < 3; ++z)
4903 for (unsigned int y = 0; y < 3; ++y)
4904 for (unsigned int x = 0; x < 3; ++x)
4905 {
4906 cells[k].vertices[0] = x + 4 * y + 16 * z;
4907 cells[k].vertices[1] = x + 4 * y + 16 * z + 1;
4908 cells[k].vertices[2] = x + 4 * y + 16 * z + 4;
4909 cells[k].vertices[3] = x + 4 * y + 16 * z + 5;
4910 cells[k].vertices[4] = x + 4 * y + 16 * z + 16;
4911 cells[k].vertices[5] = x + 4 * y + 16 * z + 17;
4912 cells[k].vertices[6] = x + 4 * y + 16 * z + 20;
4913 cells[k].vertices[7] = x + 4 * y + 16 * z + 21;
4914 if (colorize)
4915 cells[k].material_id = materials[k];
4916 ++k;
4917 }
4919 cells,
4920 SubCellData()); // no boundary information
4921 }
4922
4923
4924
4925 template <>
4926 void
4928 const double radius_0,
4929 const double radius_1,
4930 const double half_length)
4931 {
4932 Assert(triangulation.n_cells() == 0,
4933 ExcMessage("The output triangulation object needs to be empty."));
4934 Assert(0 < radius_0, ExcMessage("The radii must be positive."));
4935 Assert(0 < radius_1, ExcMessage("The radii must be positive."));
4936 Assert(0 < half_length, ExcMessage("The half length must be positive."));
4937
4938 const auto n_slices = 1 + static_cast<unsigned int>(std::ceil(
4939 half_length / std::max(radius_0, radius_1)));
4940
4941 Triangulation<2> triangulation_2;
4942 GridGenerator::hyper_ball(triangulation_2, Point<2>(), radius_0);
4944 n_slices,
4945 2 * half_length,
4948 GridTools::shift(Tensor<1, 3>({-half_length, 0.0, 0.0}), triangulation);
4949 // At this point we have a cylinder. Multiply the y and z coordinates by a
4950 // factor that scales (with x) linearly between radius_0 and radius_1 to fix
4951 // the circle radii and interior points:
4952 auto shift_radii = [=](const Point<3> &p) {
4953 const double slope = (radius_1 / radius_0 - 1.0) / (2.0 * half_length);
4954 const double factor = slope * (p[0] - -half_length) + 1.0;
4955 return Point<3>(p[0], factor * p[1], factor * p[2]);
4956 };
4957 GridTools::transform(shift_radii, triangulation);
4958
4959 // Set boundary ids at -half_length to 1 and at half_length to 2. Set the
4960 // manifold id on hull faces (i.e., faces not on either end) to 0.
4961 for (const auto &face : triangulation.active_face_iterators())
4962 if (face->at_boundary())
4963 {
4964 if (std::abs(face->center()[0] - -half_length) < 1e-8 * half_length)
4965 face->set_boundary_id(1);
4966 else if (std::abs(face->center()[0] - half_length) <
4967 1e-8 * half_length)
4968 face->set_boundary_id(2);
4969 else
4970 face->set_all_manifold_ids(0);
4971 }
4972
4973 triangulation.set_manifold(0, CylindricalManifold<3>());
4974 }
4975
4976
4977 // Implementation for 3D only
4978 template <>
4979 void
4981 const double a,
4982 const double b,
4983 const bool colorize)
4984 {
4985 // we slice out the top back right
4986 // part of the cube
4987 const Point<3> vertices[26] = {
4988 // front face of the big cube
4989 Point<3>(a, a, a),
4990 Point<3>((a + b) / 2, a, a),
4991 Point<3>(b, a, a),
4992 Point<3>(a, a, (a + b) / 2),
4993 Point<3>((a + b) / 2, a, (a + b) / 2),
4994 Point<3>(b, a, (a + b) / 2),
4995 Point<3>(a, a, b),
4996 Point<3>((a + b) / 2, a, b),
4997 Point<3>(b, a, b),
4998 // middle face of the big cube
4999 Point<3>(a, (a + b) / 2, a),
5000 Point<3>((a + b) / 2, (a + b) / 2, a),
5001 Point<3>(b, (a + b) / 2, a),
5002 Point<3>(a, (a + b) / 2, (a + b) / 2),
5003 Point<3>((a + b) / 2, (a + b) / 2, (a + b) / 2),
5004 Point<3>(b, (a + b) / 2, (a + b) / 2),
5005 Point<3>(a, (a + b) / 2, b),
5006 Point<3>((a + b) / 2, (a + b) / 2, b),
5007 Point<3>(b, (a + b) / 2, b),
5008 // back face of the big cube
5009 // last (top right) point is missing
5010 Point<3>(a, b, a),
5011 Point<3>((a + b) / 2, b, a),
5012 Point<3>(b, b, a),
5013 Point<3>(a, b, (a + b) / 2),
5014 Point<3>((a + b) / 2, b, (a + b) / 2),
5015 Point<3>(b, b, (a + b) / 2),
5016 Point<3>(a, b, b),
5017 Point<3>((a + b) / 2, b, b)};
5018 const int cell_vertices[7][8] = {{0, 1, 9, 10, 3, 4, 12, 13},
5019 {1, 2, 10, 11, 4, 5, 13, 14},
5020 {3, 4, 12, 13, 6, 7, 15, 16},
5021 {4, 5, 13, 14, 7, 8, 16, 17},
5022 {9, 10, 18, 19, 12, 13, 21, 22},
5023 {10, 11, 19, 20, 13, 14, 22, 23},
5024 {12, 13, 21, 22, 15, 16, 24, 25}};
5025
5026 std::vector<CellData<3>> cells(7, CellData<3>());
5027
5028 for (unsigned int i = 0; i < 7; ++i)
5029 {
5030 for (unsigned int j = 0; j < 8; ++j)
5031 cells[i].vertices[j] = cell_vertices[i][j];
5032 cells[i].material_id = 0;
5033 }
5034
5035 tria.create_triangulation(std::vector<Point<3>>(std::begin(vertices),
5036 std::end(vertices)),
5037 cells,
5038 SubCellData()); // no boundary information
5039
5040 if (colorize)
5041 {
5042 Assert(false, ExcNotImplemented());
5043 }
5044 }
5045
5046
5047
5048 // Implementation for 3D only
5049 template <>
5050 void
5052 const Point<3> & p,
5053 const double radius,
5054 const bool internal_manifold)
5055 {
5056 const double a =
5057 1. / (1 + std::sqrt(3.0)); // equilibrate cell sizes at transition
5058 // from the inner part to the radial
5059 // cells
5060 const unsigned int n_vertices = 16;
5061 const Point<3> vertices[n_vertices] = {
5062 // first the vertices of the inner
5063 // cell
5064 p + Point<3>(-1, -1, -1) * (radius / std::sqrt(3.0) * a),
5065 p + Point<3>(+1, -1, -1) * (radius / std::sqrt(3.0) * a),
5066 p + Point<3>(+1, -1, +1) * (radius / std::sqrt(3.0) * a),
5067 p + Point<3>(-1, -1, +1) * (radius / std::sqrt(3.0) * a),
5068 p + Point<3>(-1, +1, -1) * (radius / std::sqrt(3.0) * a),
5069 p + Point<3>(+1, +1, -1) * (radius / std::sqrt(3.0) * a),
5070 p + Point<3>(+1, +1, +1) * (radius / std::sqrt(3.0) * a),
5071 p + Point<3>(-1, +1, +1) * (radius / std::sqrt(3.0) * a),
5072 // now the eight vertices at
5073 // the outer sphere
5074 p + Point<3>(-1, -1, -1) * (radius / std::sqrt(3.0)),
5075 p + Point<3>(+1, -1, -1) * (radius / std::sqrt(3.0)),
5076 p + Point<3>(+1, -1, +1) * (radius / std::sqrt(3.0)),
5077 p + Point<3>(-1, -1, +1) * (radius / std::sqrt(3.0)),
5078 p + Point<3>(-1, +1, -1) * (radius / std::sqrt(3.0)),
5079 p + Point<3>(+1, +1, -1) * (radius / std::sqrt(3.0)),
5080 p + Point<3>(+1, +1, +1) * (radius / std::sqrt(3.0)),
5081 p + Point<3>(-1, +1, +1) * (radius / std::sqrt(3.0)),
5082 };
5083
5084 // one needs to draw the seven cubes to
5085 // understand what's going on here
5086 const unsigned int n_cells = 7;
5087 const int cell_vertices[n_cells][8] = {
5088 {0, 1, 4, 5, 3, 2, 7, 6}, // center
5089 {8, 9, 12, 13, 0, 1, 4, 5}, // bottom
5090 {9, 13, 1, 5, 10, 14, 2, 6}, // right
5091 {11, 10, 3, 2, 15, 14, 7, 6}, // top
5092 {8, 0, 12, 4, 11, 3, 15, 7}, // left
5093 {8, 9, 0, 1, 11, 10, 3, 2}, // front
5094 {12, 4, 13, 5, 15, 7, 14, 6}}; // back
5095
5096 std::vector<CellData<3>> cells(n_cells, CellData<3>());
5097
5098 for (unsigned int i = 0; i < n_cells; ++i)
5099 {
5100 for (const unsigned int j : GeometryInfo<3>::vertex_indices())
5101 cells[i].vertices[j] = cell_vertices[i][j];
5102 cells[i].material_id = 0;
5103 cells[i].manifold_id = i == 0 ? numbers::flat_manifold_id : 1;
5104 }
5105
5106 tria.create_triangulation(std::vector<Point<3>>(std::begin(vertices),
5107 std::end(vertices)),
5108 cells,
5109 SubCellData()); // no boundary information
5112 if (internal_manifold)
5114 }
5115
5116
5117
5118 void
5120 const unsigned int n_rotate_middle_square)
5121 {
5122 AssertThrow(n_rotate_middle_square < 4,
5123 ExcMessage("The number of rotation by pi/2 of the right square "
5124 "must be in the half-open range [0,4)."))
5125
5126 constexpr unsigned int dim = 2;
5127
5128 const unsigned int n_cells = 5;
5129 std::vector<CellData<dim>> cells(n_cells);
5130
5131 // Corner points of the cube [0,1]^2
5132 const std::vector<Point<dim>> vertices = {Point<dim>(0, 0), // 0
5133 Point<dim>(1, 0), // 1
5134 Point<dim>(0, 1), // 2
5135 Point<dim>(1, 1), // 3
5136 Point<dim>(2, 0), // 4
5137 Point<dim>(2, 1), // 5
5138 Point<dim>(3, 0), // 6
5139 Point<dim>(3, 1), // 7
5140 Point<dim>(1, -1), // 8
5141 Point<dim>(2, -1), // 9
5142 Point<dim>(1, 2), // 10
5143 Point<dim>(2, 2)}; // 11
5144
5145
5146 // consistent orientation
5147 unsigned int cell_vertices[n_cells][4] = {{0, 1, 2, 3},
5148 {1, 4, 3, 5}, // rotating cube
5149 {8, 9, 1, 4},
5150 {4, 6, 5, 7},
5151 {3, 5, 10, 11}};
5152
5153 switch (n_rotate_middle_square)
5154 {
5155 case /* rotate right square */ 1:
5156 {
5157 cell_vertices[1][0] = 4;
5158 cell_vertices[1][1] = 5;
5159 cell_vertices[1][2] = 1;
5160 cell_vertices[1][3] = 3;
5161 break;
5162 }
5163
5164 case /* rotate right square */ 2:
5165 {
5166 cell_vertices[1][0] = 5;
5167 cell_vertices[1][1] = 3;
5168 cell_vertices[1][2] = 4;
5169 cell_vertices[1][3] = 1;
5170 break;
5171 }
5172
5173 case /* rotate right square */ 3:
5174 {
5175 cell_vertices[1][0] = 3;
5176 cell_vertices[1][1] = 1;
5177 cell_vertices[1][2] = 5;
5178 cell_vertices[1][3] = 4;
5179 break;
5180 }
5181
5182 default /* 0 */:
5183 break;
5184 } // switch
5185
5186 cells.resize(n_cells, CellData<dim>());
5187
5188 for (unsigned int cell_index = 0; cell_index < n_cells; ++cell_index)
5189 {
5190 for (const unsigned int vertex_index :
5192 {
5193 cells[cell_index].vertices[vertex_index] =
5194 cell_vertices[cell_index][vertex_index];
5195 cells[cell_index].material_id = 0;
5196 }
5197 }
5198
5200 }
5201
5202
5203 void
5205 const bool face_orientation,
5206 const bool face_flip,
5207 const bool face_rotation,
5208 const bool manipulate_left_cube)
5209 {
5210 constexpr unsigned int dim = 3;
5211
5212 const unsigned int n_cells = 2;
5213 std::vector<CellData<dim>> cells(n_cells);
5214
5215 // Corner points of the cube [0,1]^3
5216 const std::vector<Point<dim>> vertices = {Point<dim>(0, 0, 0), // 0
5217 Point<dim>(1, 0, 0), // 1
5218 Point<dim>(0, 1, 0), // 2
5219 Point<dim>(1, 1, 0), // 3
5220 Point<dim>(0, 0, 1), // 4
5221 Point<dim>(1, 0, 1), // 5
5222 Point<dim>(0, 1, 1), // 6
5223 Point<dim>(1, 1, 1), // 7
5224 Point<dim>(2, 0, 0), // 8
5225 Point<dim>(2, 1, 0), // 9
5226 Point<dim>(2, 0, 1), // 10
5227 Point<dim>(2, 1, 1)}; // 11
5228
5229 unsigned int cell_vertices[n_cells][8] = {
5230 {0, 1, 2, 3, 4, 5, 6, 7}, // unit cube
5231 {1, 8, 3, 9, 5, 10, 7, 11}}; // shifted cube
5232
5233 // binary to case number
5234 const unsigned int this_case = 4 * static_cast<int>(face_orientation) +
5235 2 * static_cast<int>(face_flip) +
5236 static_cast<int>(face_rotation);
5237
5238 if (manipulate_left_cube)
5239 {
5240 switch (this_case)
5241 {
5242 case 0:
5243 {
5244 cell_vertices[0][0] = 1;
5245 cell_vertices[0][1] = 0;
5246 cell_vertices[0][2] = 5;
5247 cell_vertices[0][3] = 4;
5248 cell_vertices[0][4] = 3;
5249 cell_vertices[0][5] = 2;
5250 cell_vertices[0][6] = 7;
5251 cell_vertices[0][7] = 6;
5252 break;
5253 }
5254
5255 case 1:
5256 {
5257 cell_vertices[0][0] = 5;
5258 cell_vertices[0][1] = 4;
5259 cell_vertices[0][2] = 7;
5260 cell_vertices[0][3] = 6;
5261 cell_vertices[0][4] = 1;
5262 cell_vertices[0][5] = 0;
5263 cell_vertices[0][6] = 3;
5264 cell_vertices[0][7] = 2;
5265 break;
5266 }
5267
5268 case 2:
5269 {
5270 cell_vertices[0][0] = 7;
5271 cell_vertices[0][1] = 6;
5272 cell_vertices[0][2] = 3;
5273 cell_vertices[0][3] = 2;
5274 cell_vertices[0][4] = 5;
5275 cell_vertices[0][5] = 4;
5276 cell_vertices[0][6] = 1;
5277 cell_vertices[0][7] = 0;
5278 break;
5279 }
5280 case 3:
5281 {
5282 cell_vertices[0][0] = 3;
5283 cell_vertices[0][1] = 2;
5284 cell_vertices[0][2] = 1;
5285 cell_vertices[0][3] = 0;
5286 cell_vertices[0][4] = 7;
5287 cell_vertices[0][5] = 6;
5288 cell_vertices[0][6] = 5;
5289 cell_vertices[0][7] = 4;
5290 break;
5291 }
5292
5293 case 4:
5294 {
5295 cell_vertices[0][0] = 0;
5296 cell_vertices[0][1] = 1;
5297 cell_vertices[0][2] = 2;
5298 cell_vertices[0][3] = 3;
5299 cell_vertices[0][4] = 4;
5300 cell_vertices[0][5] = 5;
5301 cell_vertices[0][6] = 6;
5302 cell_vertices[0][7] = 7;
5303 break;
5304 }
5305
5306 case 5:
5307 {
5308 cell_vertices[0][0] = 2;
5309 cell_vertices[0][1] = 3;
5310 cell_vertices[0][2] = 6;
5311 cell_vertices[0][3] = 7;
5312 cell_vertices[0][4] = 0;
5313 cell_vertices[0][5] = 1;
5314 cell_vertices[0][6] = 4;
5315 cell_vertices[0][7] = 5;
5316 break;
5317 }
5318
5319 case 6:
5320 {
5321 cell_vertices[0][0] = 6;
5322 cell_vertices[0][1] = 7;
5323 cell_vertices[0][2] = 4;
5324 cell_vertices[0][3] = 5;
5325 cell_vertices[0][4] = 2;
5326 cell_vertices[0][5] = 3;
5327 cell_vertices[0][6] = 0;
5328 cell_vertices[0][7] = 1;
5329 break;
5330 }
5331
5332 case 7:
5333 {
5334 cell_vertices[0][0] = 4;
5335 cell_vertices[0][1] = 5;
5336 cell_vertices[0][2] = 0;
5337 cell_vertices[0][3] = 1;
5338 cell_vertices[0][4] = 6;
5339 cell_vertices[0][5] = 7;
5340 cell_vertices[0][6] = 2;
5341 cell_vertices[0][7] = 3;
5342 break;
5343 }
5344 } // switch
5345 }
5346 else
5347 {
5348 switch (this_case)
5349 {
5350 case 0:
5351 {
5352 cell_vertices[1][0] = 8;
5353 cell_vertices[1][1] = 1;
5354 cell_vertices[1][2] = 10;
5355 cell_vertices[1][3] = 5;
5356 cell_vertices[1][4] = 9;
5357 cell_vertices[1][5] = 3;
5358 cell_vertices[1][6] = 11;
5359 cell_vertices[1][7] = 7;
5360 break;
5361 }
5362
5363 case 1:
5364 {
5365 cell_vertices[1][0] = 10;
5366 cell_vertices[1][1] = 5;
5367 cell_vertices[1][2] = 11;
5368 cell_vertices[1][3] = 7;
5369 cell_vertices[1][4] = 8;
5370 cell_vertices[1][5] = 1;
5371 cell_vertices[1][6] = 9;
5372 cell_vertices[1][7] = 3;
5373 break;
5374 }
5375
5376 case 2:
5377 {
5378 cell_vertices[1][0] = 11;
5379 cell_vertices[1][1] = 7;
5380 cell_vertices[1][2] = 9;
5381 cell_vertices[1][3] = 3;
5382 cell_vertices[1][4] = 10;
5383 cell_vertices[1][5] = 5;
5384 cell_vertices[1][6] = 8;
5385 cell_vertices[1][7] = 1;
5386 break;
5387 }
5388
5389 case 3:
5390 {
5391 cell_vertices[1][0] = 9;
5392 cell_vertices[1][1] = 3;
5393 cell_vertices[1][2] = 8;
5394 cell_vertices[1][3] = 1;
5395 cell_vertices[1][4] = 11;
5396 cell_vertices[1][5] = 7;
5397 cell_vertices[1][6] = 10;
5398 cell_vertices[1][7] = 5;
5399 break;
5400 }
5401
5402 case 4:
5403 {
5404 cell_vertices[1][0] = 1;
5405 cell_vertices[1][1] = 8;
5406 cell_vertices[1][2] = 3;
5407 cell_vertices[1][3] = 9;
5408 cell_vertices[1][4] = 5;
5409 cell_vertices[1][5] = 10;
5410 cell_vertices[1][6] = 7;
5411 cell_vertices[1][7] = 11;
5412 break;
5413 }
5414
5415 case 5:
5416 {
5417 cell_vertices[1][0] = 5;
5418 cell_vertices[1][1] = 10;
5419 cell_vertices[1][2] = 1;
5420 cell_vertices[1][3] = 8;
5421 cell_vertices[1][4] = 7;
5422 cell_vertices[1][5] = 11;
5423 cell_vertices[1][6] = 3;
5424 cell_vertices[1][7] = 9;
5425 break;
5426 }
5427
5428 case 6:
5429 {
5430 cell_vertices[1][0] = 7;
5431 cell_vertices[1][1] = 11;
5432 cell_vertices[1][2] = 5;
5433 cell_vertices[1][3] = 10;
5434 cell_vertices[1][4] = 3;
5435 cell_vertices[1][5] = 9;
5436 cell_vertices[1][6] = 1;
5437 cell_vertices[1][7] = 8;
5438 break;
5439 }
5440
5441 case 7:
5442 {
5443 cell_vertices[1][0] = 3;
5444 cell_vertices[1][1] = 9;
5445 cell_vertices[1][2] = 7;
5446 cell_vertices[1][3] = 11;
5447 cell_vertices[1][4] = 1;
5448 cell_vertices[1][5] = 8;
5449 cell_vertices[1][6] = 5;
5450 cell_vertices[1][7] = 10;
5451 break;
5452 }
5453 } // switch
5454 }
5455
5456 cells.resize(n_cells, CellData<dim>());
5457
5458 for (unsigned int cell_index = 0; cell_index < n_cells; ++cell_index)
5459 {
5460 for (const unsigned int vertex_index :
5462 {
5463 cells[cell_index].vertices[vertex_index] =
5464 cell_vertices[cell_index][vertex_index];
5465 cells[cell_index].material_id = 0;
5466 }
5467 }
5468
5470 }
5471
5472
5473
5474 template <int spacedim>
5475 void
5477 const Point<spacedim> & p,
5478 const double radius)
5479 {
5480 Triangulation<spacedim> volume_mesh;
5481 GridGenerator::hyper_ball(volume_mesh, p, radius);
5482 const std::set<types::boundary_id> boundary_ids = {0};
5483 GridGenerator::extract_boundary_mesh(volume_mesh, tria, boundary_ids);
5486 }
5487
5488
5489
5490 // Implementation for 3D only
5491 template <>
5492 void
5494 const unsigned int x_subdivisions,
5495 const double radius,
5496 const double half_length)
5497 {
5498 // Copy the base from hyper_ball<3>
5499 // and transform it to yz
5500 const double d = radius / std::sqrt(2.0);
5501 const double a = d / (1 + std::sqrt(2.0));
5502
5503 std::vector<Point<3>> vertices;
5504 const double initial_height = -half_length;
5505 const double height_increment = 2. * half_length / x_subdivisions;
5506
5507 for (unsigned int rep = 0; rep < (x_subdivisions + 1); ++rep)
5508 {
5509 const double height = initial_height + height_increment * rep;
5510
5511 vertices.emplace_back(Point<3>(-d, height, -d));
5512 vertices.emplace_back(Point<3>(d, height, -d));
5513 vertices.emplace_back(Point<3>(-a, height, -a));
5514 vertices.emplace_back(Point<3>(a, height, -a));
5515 vertices.emplace_back(Point<3>(-a, height, a));
5516 vertices.emplace_back(Point<3>(a, height, a));
5517 vertices.emplace_back(Point<3>(-d, height, d));
5518 vertices.emplace_back(Point<3>(d, height, d));
5519 }
5520
5521 // Turn cylinder such that y->x
5522 for (auto &vertex : vertices)
5523 {
5524 const double h = vertex(1);
5525 vertex(1) = -vertex(0);
5526 vertex(0) = h;
5527 }
5528
5529 std::vector<std::vector<int>> cell_vertices;
5530 cell_vertices.push_back({0, 1, 8, 9, 2, 3, 10, 11});
5531 cell_vertices.push_back({0, 2, 8, 10, 6, 4, 14, 12});
5532 cell_vertices.push_back({2, 3, 10, 11, 4, 5, 12, 13});
5533 cell_vertices.push_back({1, 7, 9, 15, 3, 5, 11, 13});
5534 cell_vertices.push_back({6, 4, 14, 12, 7, 5, 15, 13});
5535
5536 for (unsigned int rep = 1; rep < x_subdivisions; ++rep)
5537 {
5538 for (unsigned int i = 0; i < 5; ++i)
5539 {
5540 std::vector<int> new_cell_vertices(8);
5541 for (unsigned int j = 0; j < 8; ++j)
5542 new_cell_vertices[j] = cell_vertices[i][j] + 8 * rep;
5543 cell_vertices.push_back(new_cell_vertices);
5544 }
5545 }
5546
5547 unsigned int n_cells = x_subdivisions * 5;
5548
5549 std::vector<CellData<3>> cells(n_cells, CellData<3>());
5550
5551 for (unsigned int i = 0; i < n_cells; ++i)
5552 {
5553 for (unsigned int j = 0; j < 8; ++j)
5554 cells[i].vertices[j] = cell_vertices[i][j];
5555 cells[i].material_id = 0;
5556 }
5557
5558 tria.create_triangulation(std::vector<Point<3>>(std::begin(vertices),
5559 std::end(vertices)),
5560 cells,
5561 SubCellData()); // no boundary information
5562
5563 // set boundary indicators for the
5564 // faces at the ends to 1 and 2,
5565 // respectively. note that we also
5566 // have to deal with those lines
5567 // that are purely in the interior
5568 // of the ends. we determine whether
5569 // an edge is purely in the
5570 // interior if one of its vertices
5571 // is at coordinates '+-a' as set
5572 // above
5574
5575 // Tolerance is calculated using the minimal length defining
5576 // the cylinder
5577 const double tolerance = 1e-5 * std::min(radius, half_length);
5578
5579 for (const auto &cell : tria.cell_iterators())
5580 for (unsigned int i : GeometryInfo<3>::face_indices())
5581 if (cell->at_boundary(i))
5582 {
5583 if (cell->face(i)->center()(0) > half_length - tolerance)
5584 {
5585 cell->face(i)->set_boundary_id(2);
5586 cell->face(i)->set_manifold_id(numbers::flat_manifold_id);
5587
5588 for (unsigned int e = 0; e < GeometryInfo<3>::lines_per_face;
5589 ++e)
5590 if ((std::fabs(cell->face(i)->line(e)->vertex(0)[1]) == a) ||
5591 (std::fabs(cell->face(i)->line(e)->vertex(0)[2]) == a) ||
5592 (std::fabs(cell->face(i)->line(e)->vertex(1)[1]) == a) ||
5593 (std::fabs(cell->face(i)->line(e)->vertex(1)[2]) == a))
5594 {
5595 cell->face(i)->line(e)->set_boundary_id(2);
5596 cell->face(i)->line(e)->set_manifold_id(
5598 }
5599 }
5600 else if (cell->face(i)->center()(0) < -half_length + tolerance)
5601 {
5602 cell->face(i)->set_boundary_id(1);
5603 cell->face(i)->set_manifold_id(numbers::flat_manifold_id);
5604
5605 for (unsigned int e = 0; e < GeometryInfo<3>::lines_per_face;
5606 ++e)
5607 if ((std::fabs(cell->face(i)->line(e)->vertex(0)[1]) == a) ||
5608 (std::fabs(cell->face(i)->line(e)->vertex(0)[2]) == a) ||
5609 (std::fabs(cell->face(i)->line(e)->vertex(1)[1]) == a) ||
5610 (std::fabs(cell->face(i)->line(e)->vertex(1)[2]) == a))
5611 {
5612 cell->face(i)->line(e)->set_boundary_id(1);
5613 cell->face(i)->line(e)->set_manifold_id(
5615 }
5616 }
5617 }
5619 }
5620
5621 // Implementation for 3D only
5622 template <>
5623 void
5625 const double radius,
5626 const double half_length)
5627 {
5628 subdivided_cylinder(tria, 2, radius, half_length);
5629 }
5630
5631 template <>
5632 void
5634 const Point<3> & center,
5635 const double radius)
5636 {
5637 const unsigned int dim = 3;
5638
5639 // the parameters a (intersection on the octant lines from center), b
5640 // (intersection within the octant faces) and c (position inside the
5641 // octant) have been derived by equilibrating the minimal singular value
5642 // of the Jacobian of the four cells around the center point c and, as a
5643 // secondary measure, to minimize the aspect ratios defined as the maximal
5644 // divided by the minimal singular values throughout cells
5645 const double a = 0.528;
5646 const double b = 0.4533;
5647 const double c = 0.3752;
5648 const Point<dim> vertices[15] = {
5649 center + Point<dim>(0, 0, 0) * radius,
5650 center + Point<dim>(+1, 0, 0) * radius,
5651 center + Point<dim>(+1, 0, 0) * (radius * a),
5652 center + Point<dim>(0, +1, 0) * (radius * a),
5653 center + Point<dim>(+1, +1, 0) * (radius * b),
5654 center + Point<dim>(0, +1, 0) * radius,
5655 center + Point<dim>(+1, +1, 0) * radius / std::sqrt(2.0),
5656 center + Point<dim>(0, 0, 1) * radius * a,
5657 center + Point<dim>(+1, 0, 1) * radius / std::sqrt(2.0),
5658 center + Point<dim>(+1, 0, 1) * (radius * b),
5659 center + Point<dim>(0, +1, 1) * (radius * b),
5660 center + Point<dim>(+1, +1, 1) * (radius * c),
5661 center + Point<dim>(0, +1, 1) * radius / std::sqrt(2.0),
5662 center + Point<dim>(+1, +1, 1) * (radius / (std::sqrt(3.0))),
5663 center + Point<dim>(0, 0, 1) * radius};
5664 const int cell_vertices[4][8] = {{0, 2, 3, 4, 7, 9, 10, 11},
5665 {1, 6, 2, 4, 8, 13, 9, 11},
5666 {5, 3, 6, 4, 12, 10, 13, 11},
5667 {7, 9, 10, 11, 14, 8, 12, 13}};
5668
5669 std::vector<CellData<dim>> cells(4, CellData<dim>());
5670
5671 for (unsigned int i = 0; i < 4; ++i)
5672 {
5673 for (unsigned int j = 0; j < 8; ++j)
5674 cells[i].vertices[j] = cell_vertices[i][j];
5675 cells[i].material_id = 0;
5676 }
5677
5678 tria.create_triangulation(std::vector<Point<dim>>(std::begin(vertices),
5679 std::end(vertices)),
5680 cells,
5681 SubCellData()); // no boundary information
5682
5685
5687 while (cell != end)
5688 {
5689 for (unsigned int i : GeometryInfo<dim>::face_indices())
5690 {
5691 if (cell->face(i)->boundary_id() ==
5693 continue;
5694
5695 // If x,y or z is zero, then this is part of the plane
5696 if (cell->face(i)->center()(0) < center(0) + 1.e-5 * radius ||
5697 cell->face(i)->center()(1) < center(1) + 1.e-5 * radius ||
5698 cell->face(i)->center()(2) < center(2) + 1.e-5 * radius)
5699 {
5700 cell->face(i)->set_boundary_id(1);
5701 cell->face(i)->set_manifold_id(numbers::flat_manifold_id);
5702 // also set the boundary indicators of the bounding lines,
5703 // unless both vertices are on the perimeter
5704 for (unsigned int j = 0; j < GeometryInfo<3>::lines_per_face;
5705 ++j)
5706 {
5707 const Point<3> line_vertices[2] = {
5708 cell->face(i)->line(j)->vertex(0),
5709 cell->face(i)->line(j)->vertex(1)};
5710 if ((std::fabs(line_vertices[0].distance(center) - radius) >
5711 1e-5 * radius) ||
5712 (std::fabs(line_vertices[1].distance(center) - radius) >
5713 1e-5 * radius))
5714 {
5715 cell->face(i)->line(j)->set_boundary_id(1);
5716 cell->face(i)->line(j)->set_manifold_id(
5718 }
5719 }
5720 }
5721 }
5722 ++cell;
5723 }
5725 }
5726
5727
5728
5729 // Implementation for 3D only
5730 template <>
5731 void
5733 const Point<3> & center,
5734 const double radius)
5735 {
5736 // These are for the two lower squares
5737 const double d = radius / std::sqrt(2.0);
5738 const double a = d / (1 + std::sqrt(2.0));
5739 // These are for the two upper square
5740 const double b = a / 2.0;
5741 const double c = d / 2.0;
5742 // And so are these
5743 const double hb = radius * std::sqrt(3.0) / 4.0;
5744 const double hc = radius * std::sqrt(3.0) / 2.0;
5745
5746 Point<3> vertices[16] = {
5747 center + Point<3>(0, d, -d),
5748 center + Point<3>(0, -d, -d),
5749 center + Point<3>(0, a, -a),
5750 center + Point<3>(0, -a, -a),
5751 center + Point<3>(0, a, a),
5752 center + Point<3>(0, -a, a),
5753 center + Point<3>(0, d, d),
5754 center + Point<3>(0, -d, d),
5755
5756 center + Point<3>(hc, c, -c),
5757 center + Point<3>(hc, -c, -c),
5758 center + Point<3>(hb, b, -b),
5759 center + Point<3>(hb, -b, -b),
5760 center + Point<3>(hb, b, b),
5761 center + Point<3>(hb, -b, b),
5762 center + Point<3>(hc, c, c),
5763 center + Point<3>(hc, -c, c),
5764 };
5765
5766 int cell_vertices[6][8] = {{0, 1, 8, 9, 2, 3, 10, 11},
5767 {0, 2, 8, 10, 6, 4, 14, 12},
5768 {2, 3, 10, 11, 4, 5, 12, 13},
5769 {1, 7, 9, 15, 3, 5, 11, 13},
5770 {6, 4, 14, 12, 7, 5, 15, 13},
5771 {8, 10, 9, 11, 14, 12, 15, 13}};
5772
5773 std::vector<CellData<3>> cells(6, CellData<3>());
5774
5775 for (unsigned int i = 0; i < 6; ++i)
5776 {
5777 for (unsigned int j = 0; j < 8; ++j)
5778 cells[i].vertices[j] = cell_vertices[i][j];
5779 cells[i].material_id = 0;
5780 }
5781
5782 tria.create_triangulation(std::vector<Point<3>>(std::begin(vertices),
5783 std::end(vertices)),
5784 cells,
5785 SubCellData()); // no boundary information
5786
5789
5791
5792 // go over all faces. for the ones on the flat face, set boundary
5793 // indicator for face and edges to one; the rest will remain at
5794 // zero but we have to pay attention to those edges that are
5795 // at the perimeter of the flat face since they should not be
5796 // set to one
5797 while (cell != end)
5798 {
5799 for (unsigned int i : GeometryInfo<3>::face_indices())
5800 {
5801 if (!cell->at_boundary(i))
5802 continue;
5803
5804 // If the center is on the plane x=0, this is a planar element. set
5805 // its boundary indicator. also set the boundary indicators of the
5806 // bounding faces unless both vertices are on the perimeter
5807 if (cell->face(i)->center()(0) < center(0) + 1.e-5 * radius)
5808 {
5809 cell->face(i)->set_boundary_id(1);
5810 cell->face(i)->set_manifold_id(numbers::flat_manifold_id);
5811 for (unsigned int j = 0; j < GeometryInfo<3>::lines_per_face;
5812 ++j)
5813 {
5814 const Point<3> line_vertices[2] = {
5815 cell->face(i)->line(j)->vertex(0),
5816 cell->face(i)->line(j)->vertex(1)};
5817 if ((std::fabs(line_vertices[0].distance(center) - radius) >
5818 1e-5 * radius) ||
5819 (std::fabs(line_vertices[1].distance(center) - radius) >
5820 1e-5 * radius))
5821 {
5822 cell->face(i)->line(j)->set_boundary_id(1);
5823 cell->face(i)->line(j)->set_manifold_id(
5825 }
5826 }
5827 }
5828 }
5829 ++cell;
5830 }
5832 }
5833
5834
5835
5836 template <int dim>
5837 void
5839 const Point<dim> & p,
5840 const double radius)
5841 {
5842 // We create the ball by duplicating the information in each dimension at
5843 // a time by appropriate rotations, starting from the quarter ball. The
5844 // rotations make sure we do not generate inverted cells that would appear
5845 // if we tried the slightly simpler approach to simply mirror the cells.
5846 //
5847 // Make the rotations easy by centering at the origin now and shifting by p
5848 // later.
5849
5850 Triangulation<dim> tria_piece;
5851 GridGenerator::quarter_hyper_ball(tria_piece, Point<dim>(), radius);
5852
5853 for (unsigned int round = 0; round < dim; ++round)
5854 {
5855 Triangulation<dim> tria_copy;
5856 tria_copy.copy_triangulation(tria_piece);
5857 tria_piece.clear();
5858 std::vector<Point<dim>> new_points(tria_copy.n_vertices());
5859 if (round == 0)
5860 for (unsigned int v = 0; v < tria_copy.n_vertices(); ++v)
5861 {
5862 // rotate by 90 degrees counterclockwise
5863 new_points[v][0] = -tria_copy.get_vertices()[v][1];
5864 new_points[v][1] = tria_copy.get_vertices()[v][0];
5865 if (dim == 3)
5866 new_points[v][2] = tria_copy.get_vertices()[v][2];
5867 }
5868 else if (round == 1)
5869 {
5870 for (unsigned int v = 0; v < tria_copy.n_vertices(); ++v)
5871 {
5872 // rotate by 180 degrees along the xy plane
5873 new_points[v][0] = -tria_copy.get_vertices()[v][0];
5874 new_points[v][1] = -tria_copy.get_vertices()[v][1];
5875 if (dim == 3)
5876 new_points[v][2] = tria_copy.get_vertices()[v][2];
5877 }
5878 }
5879 else if (round == 2)
5880 for (unsigned int v = 0; v < tria_copy.n_vertices(); ++v)
5881 {
5882 // rotate by 180 degrees along the xz plane
5883 Assert(dim == 3, ExcInternalError());
5884 new_points[v][0] = -tria_copy.get_vertices()[v][0];
5885 new_points[v][1] = tria_copy.get_vertices()[v][1];
5886 new_points[v][2] = -tria_copy.get_vertices()[v][2];
5887 }
5888 else
5889 Assert(false, ExcInternalError());
5890
5891
5892 // the cell data is exactly the same as before
5893 std::vector<CellData<dim>> cells;
5894 cells.reserve(tria_copy.n_cells());
5895 for (const auto &cell : tria_copy.cell_iterators())
5896 {
5897 CellData<dim> data;
5898 for (unsigned int v : GeometryInfo<dim>::vertex_indices())
5899 data.vertices[v] = cell->vertex_index(v);
5900 data.material_id = cell->material_id();
5901 data.manifold_id = cell->manifold_id();
5902 cells.push_back(data);
5903 }
5904
5905 Triangulation<dim> rotated_tria;
5906 rotated_tria.create_triangulation(new_points, cells, SubCellData());
5907
5908 // merge the triangulations - this will make sure that the duplicate
5909 // vertices in the interior are absorbed
5910 if (round == dim - 1)
5911 merge_triangulations(tria_copy, rotated_tria, tria, 1e-12 * radius);
5912 else
5913 merge_triangulations(tria_copy,
5914 rotated_tria,
5915 tria_piece,
5916 1e-12 * radius);
5917 }
5918
5919 for (const auto &cell : tria.cell_iterators())
5920 if (cell->center().norm_square() > 0.4 * radius)
5921 cell->set_manifold_id(1);
5922 else
5923 cell->set_all_manifold_ids(numbers::flat_manifold_id);
5925
5928 }
5929
5930 // To work around an internal clang-13 error we need to split up the
5931 // individual hyper shell functions. This has the added bonus of making the
5932 // control flow easier to follow - some hyper shell functions call others.
5933 namespace internal
5934 {
5935 namespace
5936 {
5937 void
5938 hyper_shell_6(Triangulation<3> &tria,
5939 const Point<3> & p,
5940 const double inner_radius,
5941 const double outer_radius)
5942 {
5943 std::vector<Point<3>> vertices;
5944 std::vector<CellData<3>> cells;
5945
5946 const double irad = inner_radius / std::sqrt(3.0);
5947 const double orad = outer_radius / std::sqrt(3.0);
5948
5949 // Corner points of the cube [-1,1]^3
5950 static const std::array<Point<3>, 8> hexahedron = {{{-1, -1, -1}, //
5951 {+1, -1, -1}, //
5952 {-1, +1, -1}, //
5953 {+1, +1, -1}, //
5954 {-1, -1, +1}, //
5955 {+1, -1, +1}, //
5956 {-1, +1, +1}, //
5957 {+1, +1, +1}}};
5958
5959 // Start with the shell bounded by two nested cubes
5960 for (unsigned int i = 0; i < 8; ++i)
5961 vertices.push_back(p + hexahedron[i] * irad);
5962 for (unsigned int i = 0; i < 8; ++i)
5963 vertices.push_back(p + hexahedron[i] * orad);
5964
5965 const unsigned int n_cells = 6;
5966 const int cell_vertices[n_cells][8] = {
5967 {8, 9, 10, 11, 0, 1, 2, 3}, // bottom
5968 {9, 11, 1, 3, 13, 15, 5, 7}, // right
5969 {12, 13, 4, 5, 14, 15, 6, 7}, // top
5970 {8, 0, 10, 2, 12, 4, 14, 6}, // left
5971 {8, 9, 0, 1, 12, 13, 4, 5}, // front
5972 {10, 2, 11, 3, 14, 6, 15, 7}}; // back
5973
5974 cells.resize(n_cells, CellData<3>());
5975
5976 for (unsigned int i = 0; i < n_cells; ++i)
5977 {
5978 for (const unsigned int j : GeometryInfo<3>::vertex_indices())
5979 cells[i].vertices[j] = cell_vertices[i][j];
5980 cells[i].material_id = 0;
5981 }
5982
5986 }
5987
5988 void
5989 hyper_shell_12(Triangulation<3> &tria,
5990 const Point<3> & p,
5991 const double inner_radius,
5992 const double outer_radius)
5993 {
5994 std::vector<Point<3>> vertices;
5995 std::vector<CellData<3>> cells;
5996
5997 const double irad = inner_radius / std::sqrt(3.0);
5998 const double orad = outer_radius / std::sqrt(3.0);
5999
6000 // A more regular subdivision can be obtained by two nested rhombic
6001 // dodecahedra
6002 //
6003 // Octahedron inscribed in the cube [-1,1]^3
6004 static const std::array<Point<3>, 6> octahedron = {{{-1, 0, 0}, //
6005 {1, 0, 0}, //
6006 {0, -1, 0}, //
6007 {0, 1, 0}, //
6008 {0, 0, -1}, //
6009 {0, 0, 1}}};
6010
6011 // Corner points of the cube [-1,1]^3
6012 static const std::array<Point<3>, 8> hexahedron = {{{-1, -1, -1}, //
6013 {+1, -1, -1}, //
6014 {-1, +1, -1}, //
6015 {+1, +1, -1}, //
6016 {-1, -1, +1}, //
6017 {+1, -1, +1}, //
6018 {-1, +1, +1}, //
6019 {+1, +1, +1}}};
6020
6021 for (unsigned int i = 0; i < 8; ++i)
6022 vertices.push_back(p + hexahedron[i] * irad);
6023 for (unsigned int i = 0; i < 6; ++i)
6024 vertices.push_back(p + octahedron[i] * inner_radius);
6025 for (unsigned int i = 0; i < 8; ++i)
6026 vertices.push_back(p + hexahedron[i] * orad);
6027 for (unsigned int i = 0; i < 6; ++i)
6028 vertices.push_back(p + octahedron[i] * outer_radius);
6029
6030 const unsigned int n_cells = 12;
6031 const unsigned int rhombi[n_cells][4] = {{10, 4, 0, 8},
6032 {4, 13, 8, 6},
6033 {10, 5, 4, 13},
6034 {1, 9, 10, 5},
6035 {9, 7, 5, 13},
6036 {7, 11, 13, 6},
6037 {9, 3, 7, 11},
6038 {1, 12, 9, 3},
6039 {12, 2, 3, 11},
6040 {2, 8, 11, 6},
6041 {12, 0, 2, 8},
6042 {1, 10, 12, 0}};
6043
6044 cells.resize(n_cells, CellData<3>());
6045
6046 for (unsigned int i = 0; i < n_cells; ++i)
6047 {
6048 for (unsigned int j = 0; j < 4; ++j)
6049 {
6050 cells[i].vertices[j] = rhombi[i][j];
6051 cells[i].vertices[j + 4] = rhombi[i][j] + 14;
6052 }
6053 cells[i].material_id = 0;
6054 }
6055
6059 }
6060
6061 void
6062 hyper_shell_24_48(Triangulation<3> & tria,
6063 const unsigned int n,
6064 const unsigned int n_refinement_steps,
6065 const Point<3> & p,
6066 const double inner_radius,
6067 const double outer_radius)
6068 {
6069 // These two meshes are created by first creating a mesh of the
6070 // 6-cell/12-cell version, refining globally, and removing the outer
6071 // half of the cells. For 192 and more cells, we do this iteratively
6072 // several times, always refining and removing the outer half. Thus, the
6073 // outer radius for the start is larger and set as 2^n_refinement_steps
6074 // such that it exactly gives the desired radius in the end. It would
6075 // have been slightly less code to treat refinement steps recursively
6076 // for 192 cells or beyond, but unfortunately we could end up with the
6077 // 96 cell case which is not what we want. Thus, we need to implement a
6078 // loop manually here.
6079 Triangulation<3> tmp;
6080 const unsigned int outer_radius_factor = 1 << n_refinement_steps;
6081 if (n == 24)
6082 hyper_shell_6(tmp,
6083 p,
6084 inner_radius,
6085 outer_radius_factor * outer_radius -
6086 (outer_radius_factor - 1) * inner_radius);
6087 else if (n == 48)
6088 hyper_shell_12(tmp,
6089 p,
6090 inner_radius,
6091 outer_radius_factor * outer_radius -
6092 (outer_radius_factor - 1) * inner_radius);
6093 else
6094 Assert(n == 24 || n == 48, ExcInternalError());
6095 for (unsigned int r = 0; r < n_refinement_steps; ++r)
6096 {
6097 tmp.refine_global(1);
6098 std::set<Triangulation<3>::active_cell_iterator> cells_to_remove;
6099
6100 // We remove all cells which do not have exactly four vertices
6101 // at the inner radius (plus some tolerance).
6102 for (const auto &cell : tmp.active_cell_iterators())
6103 {
6104 unsigned int n_vertices_inside = 0;
6105 for (const auto v : GeometryInfo<3>::vertex_indices())
6106 if ((cell->vertex(v) - p).norm_square() <
6107 inner_radius * inner_radius * (1 + 1e-12))
6108 ++n_vertices_inside;
6109 if (n_vertices_inside < 4)
6110 cells_to_remove.insert(cell);
6111 }
6112
6113 AssertDimension(cells_to_remove.size(), tmp.n_active_cells() / 2);
6114 if (r == n_refinement_steps - 1)
6116 cells_to_remove,
6117 tria);
6118 else
6119 {
6122 cells_to_remove,
6123 copy);
6124 tmp = std::move(copy);
6125 tmp.set_all_manifold_ids(0);
6127 }
6128 }
6131 }
6132
6133 } // namespace
6134 } // namespace internal
6135
6136
6137
6138 template <>
6139 void
6141 const Point<3> & p,
6142 const double inner_radius,
6143 const double outer_radius,
6144 const unsigned int n_cells,
6145 const bool colorize)
6146 {
6147 Assert((inner_radius > 0) && (inner_radius < outer_radius),
6148 ExcInvalidRadii());
6149
6150 unsigned int n_refinement_steps = 0;
6151 unsigned int n_cells_coarsened = n_cells;
6152 if (n_cells != 96 && n_cells > 12)
6153 while (n_cells_coarsened > 12 && n_cells_coarsened % 4 == 0)
6154 {
6155 ++n_refinement_steps;
6156 n_cells_coarsened /= 4;
6157 }
6158 Assert(n_cells == 0 || n_cells == 6 || n_cells == 12 || n_cells == 96 ||
6159 (n_refinement_steps > 0 &&
6160 (n_cells_coarsened == 6 || n_cells_coarsened == 12)),
6161 ExcMessage("Invalid number of coarse mesh cells"));
6162
6163 const unsigned int n = n_refinement_steps > 0 ?
6164 4 * n_cells_coarsened :
6165 ((n_cells == 0) ? 6 : n_cells);
6166
6167 switch (n)
6168 {
6169 case 6:
6170 internal::hyper_shell_6(tria, p, inner_radius, outer_radius);
6171 break;
6172 case 12:
6173 internal::hyper_shell_12(tria, p, inner_radius, outer_radius);
6174 break;
6175 case 24:
6176 case 48:
6177 internal::hyper_shell_24_48(
6178 tria, n, n_refinement_steps, p, inner_radius, outer_radius);
6179 break;
6180 case 96:
6181 {
6182 // create a triangulation based on the 12-cell version. This
6183 // function was needed before SphericalManifold was written: it
6184 // manually adjusted the interior vertices to lie along concentric
6185 // spheres. Nowadays we can just refine globally:
6186 Triangulation<3> tmp;
6187 internal::hyper_shell_12(tmp, p, inner_radius, outer_radius);
6188 tmp.refine_global(1);
6192 break;
6193 }
6194 default:
6195 {
6196 Assert(false, ExcMessage("Invalid number of coarse mesh cells."));
6197 }
6198 }
6199
6200 if (n_cells > 0)
6202
6203 if (colorize)
6204 colorize_hyper_shell(tria, p, inner_radius, outer_radius);
6205 }
6206
6207
6208
6209 // Implementation for 3D only
6210 template <>
6211 void
6213 const Point<3> & center,
6214 const double inner_radius,
6215 const double outer_radius,
6216 const unsigned int /*n_cells*/,
6217 const bool colorize)
6218 {
6219 Assert((inner_radius > 0) && (inner_radius < outer_radius),
6220 ExcInvalidRadii());
6221
6222 // These are for the two lower squares
6223 const double d = outer_radius / std::sqrt(2.0);
6224 const double a = inner_radius / std::sqrt(2.0);
6225 // These are for the two upper square
6226 const double b = a / 2.0;
6227 const double c = d / 2.0;
6228 // And so are these
6229 const double hb = inner_radius * std::sqrt(3.0) / 2.0;
6230 const double hc = outer_radius * std::sqrt(3.0) / 2.0;
6231
6232 Point<3> vertices[16] = {
6233 center + Point<3>(0, d, -d),
6234 center + Point<3>(0, -d, -d),
6235 center + Point<3>(0, a, -a),
6236 center + Point<3>(0, -a, -a),
6237 center + Point<3>(0, a, a),
6238 center + Point<3>(0, -a, a),
6239 center + Point<3>(0, d, d),
6240 center + Point<3>(0, -d, d),
6241
6242 center + Point<3>(hc, c, -c),
6243 center + Point<3>(hc, -c, -c),
6244 center + Point<3>(hb, b, -b),
6245 center + Point<3>(hb, -b, -b),
6246 center + Point<3>(hb, b, b),
6247 center + Point<3>(hb, -b, b),
6248 center + Point<3>(hc, c, c),
6249 center + Point<3>(hc, -c, c),
6250 };
6251
6252 int cell_vertices[5][8] = {{0, 1, 8, 9, 2, 3, 10, 11},
6253 {0, 2, 8, 10, 6, 4, 14, 12},
6254 {1, 7, 9, 15, 3, 5, 11, 13},
6255 {6, 4, 14, 12, 7, 5, 15, 13},
6256 {8, 10, 9, 11, 14, 12, 15, 13}};
6257
6258 std::vector<CellData<3>> cells(5, CellData<3>());
6259
6260 for (unsigned int i = 0; i < 5; ++i)
6261 {
6262 for (unsigned int j = 0; j < 8; ++j)
6263 cells[i].vertices[j] = cell_vertices[i][j];
6264 cells[i].material_id = 0;
6265 }
6266
6267 tria.create_triangulation(std::vector<Point<3>>(std::begin(vertices),
6268 std::end(vertices)),
6269 cells,
6270 SubCellData()); // no boundary information
6271
6272 if (colorize)
6273 {
6274 // We want to use a standard boundary description where
6275 // the boundary is not curved. Hence set boundary id 2 to
6276 // to all faces in a first step.
6278 for (; cell != tria.end(); ++cell)
6279 for (unsigned int i : GeometryInfo<3>::face_indices())
6280 if (cell->at_boundary(i))
6281 cell->face(i)->set_all_boundary_ids(2);
6282
6283 // Next look for the curved boundaries. If the x value of the
6284 // center of the face is not equal to center(0), we're on a curved
6285 // boundary. Then decide whether the center is nearer to the inner
6286 // or outer boundary to set the correct boundary id.
6287 for (cell = tria.begin(); cell != tria.end(); ++cell)
6288 for (unsigned int i : GeometryInfo<3>::face_indices())
6289 if (cell->at_boundary(i))
6290 {
6291 const Triangulation<3>::face_iterator face = cell->face(i);
6292
6293 const Point<3> face_center(face->center());
6294 if (std::abs(face_center(0) - center(0)) >
6295 1.e-6 * face_center.norm())
6296 {
6297 if (std::abs((face_center - center).norm() - inner_radius) <
6298 std::abs((face_center - center).norm() - outer_radius))
6299 face->set_all_boundary_ids(0);
6300 else
6301 face->set_all_boundary_ids(1);
6302 }
6303 }
6304 }
6307 }
6308
6309
6310 // Implementation for 3D only
6311 template <>
6312 void
6314 const Point<3> & center,
6315 const double inner_radius,
6316 const double outer_radius,
6317 const unsigned int n,
6318 const bool colorize)
6319 {
6320 Assert((inner_radius > 0) && (inner_radius < outer_radius),
6321 ExcInvalidRadii());
6322 if (n == 0 || n == 3)
6323 {
6324 const double a = inner_radius * std::sqrt(2.0) / 2e0;
6325 const double b = outer_radius * std::sqrt(2.0) / 2e0;
6326 const double c = a * std::sqrt(3.0) / 2e0;
6327 const double d = b * std::sqrt(3.0) / 2e0;
6328 const double e = outer_radius / 2e0;
6329 const double h = inner_radius / 2e0;
6330
6331 std::vector<Point<3>> vertices;
6332
6333 vertices.push_back(center + Point<3>(0, inner_radius, 0)); // 0
6334 vertices.push_back(center + Point<3>(a, a, 0)); // 1
6335 vertices.push_back(center + Point<3>(b, b, 0)); // 2
6336 vertices.push_back(center + Point<3>(0, outer_radius, 0)); // 3
6337 vertices.push_back(center + Point<3>(0, a, a)); // 4
6338 vertices.push_back(center + Point<3>(c, c, h)); // 5
6339 vertices.push_back(center + Point<3>(d, d, e)); // 6
6340 vertices.push_back(center + Point<3>(0, b, b)); // 7
6341 vertices.push_back(center + Point<3>(inner_radius, 0, 0)); // 8
6342 vertices.push_back(center + Point<3>(outer_radius, 0, 0)); // 9
6343 vertices.push_back(center + Point<3>(a, 0, a)); // 10
6344 vertices.push_back(center + Point<3>(b, 0, b)); // 11
6345 vertices.push_back(center + Point<3>(0, 0, inner_radius)); // 12
6346 vertices.push_back(center + Point<3>(0, 0, outer_radius)); // 13
6347
6348 const int cell_vertices[3][8] = {
6349 {0, 1, 3, 2, 4, 5, 7, 6},
6350 {1, 8, 2, 9, 5, 10, 6, 11},
6351 {4, 5, 7, 6, 12, 10, 13, 11},
6352 };
6353 std::vector<CellData<3>> cells(3);
6354
6355 for (unsigned int i = 0; i < 3; ++i)
6356 {
6357 for (unsigned int j = 0; j < 8; ++j)
6358 cells[i].vertices[j] = cell_vertices[i][j];
6359 cells[i].material_id = 0;
6360 }
6361
6363 cells,
6364 SubCellData()); // no boundary information
6365 }
6366 else
6367 {
6369 }
6370
6371 if (colorize)
6372 colorize_quarter_hyper_shell(tria, center, inner_radius, outer_radius);
6373
6376 }
6377
6378
6379 // Implementation for 3D only
6380 template <>
6381 void
6383 const double length,
6384 const double inner_radius,
6385 const double outer_radius,
6386 const unsigned int n_radial_cells,
6387 const unsigned int n_axial_cells)
6388 {
6389 Assert((inner_radius > 0) && (inner_radius < outer_radius),
6390 ExcInvalidRadii());
6391
6392 const double pi = numbers::PI;
6393
6394 // determine the number of cells
6395 // for the grid. if not provided by
6396 // the user determine it such that
6397 // the length of each cell on the
6398 // median (in the middle between
6399 // the two circles) is equal to its
6400 // radial extent (which is the
6401 // difference between the two
6402 // radii)
6403 const unsigned int N_r =
6404 (n_radial_cells == 0 ? static_cast<unsigned int>(std::ceil(
6405 (2 * pi * (outer_radius + inner_radius) / 2) /
6406 (outer_radius - inner_radius))) :
6407 n_radial_cells);
6408 const unsigned int N_z =
6409 (n_axial_cells == 0 ?
6410 static_cast<unsigned int>(std::ceil(
6411 length / (2 * pi * (outer_radius + inner_radius) / 2 / N_r))) :
6412 n_axial_cells);
6413
6414 // set up N vertices on the
6415 // outer and N vertices on
6416 // the inner circle. the
6417 // first N ones are on the
6418 // outer one, and all are
6419 // numbered counter-clockwise
6420 std::vector<Point<2>> vertices_2d(2 * N_r);
6421 for (unsigned int i = 0; i < N_r; ++i)
6422 {
6423 vertices_2d[i] =
6424 Point<2>(std::cos(2 * pi * i / N_r), std::sin(2 * pi * i / N_r)) *
6425 outer_radius;
6426 vertices_2d[i + N_r] = vertices_2d[i] * (inner_radius / outer_radius);
6427 }
6428
6429 std::vector<Point<3>> vertices_3d;
6430 vertices_3d.reserve(2 * N_r * (N_z + 1));
6431 for (unsigned int j = 0; j <= N_z; ++j)
6432 for (unsigned int i = 0; i < 2 * N_r; ++i)
6433 {
6434 const Point<3> v(vertices_2d[i][0],
6435 vertices_2d[i][1],
6436 j * length / N_z);
6437 vertices_3d.push_back(v);
6438 }
6439
6440 std::vector<CellData<3>> cells(N_r * N_z, CellData<3>());
6441
6442 for (unsigned int j = 0; j < N_z; ++j)
6443 for (unsigned int i = 0; i < N_r; ++i)
6444 {
6445 cells[i + j * N_r].vertices[0] = i + (j + 1) * 2 * N_r;
6446 cells[i + j * N_r].vertices[1] = (i + 1) % N_r + (j + 1) * 2 * N_r;
6447 cells[i + j * N_r].vertices[2] = i + j * 2 * N_r;
6448 cells[i + j * N_r].vertices[3] = (i + 1) % N_r + j * 2 * N_r;
6449
6450 cells[i + j * N_r].vertices[4] = N_r + i + (j + 1) * 2 * N_r;
6451 cells[i + j * N_r].vertices[5] =
6452 N_r + ((i + 1) % N_r) + (j + 1) * 2 * N_r;
6453 cells[i + j * N_r].vertices[6] = N_r + i + j * 2 * N_r;
6454 cells[i + j * N_r].vertices[7] = N_r + ((i + 1) % N_r) + j * 2 * N_r;
6455
6456 cells[i + j * N_r].material_id = 0;
6457 }
6458
6459 tria.create_triangulation(vertices_3d, cells, SubCellData());
6462 }
6463
6464
6465
6466 template <int dim, int spacedim>
6467 void
6469 const std::vector<const Triangulation<dim, spacedim> *> &triangulations,
6471 const double duplicated_vertex_tolerance,
6472 const bool copy_manifold_ids)
6473 {
6474 std::vector<Point<spacedim>> vertices;
6475 std::vector<CellData<dim>> cells;
6476 SubCellData subcell_data;
6477
6478 unsigned int n_accumulated_vertices = 0;
6479 for (const auto triangulation : triangulations)
6480 {
6481 Assert(triangulation->n_levels() == 1,
6482 ExcMessage("The input triangulations must be non-empty "
6483 "and must not be refined."));
6484
6485 std::vector<Point<spacedim>> tria_vertices;
6486 std::vector<CellData<dim>> tria_cells;
6487 SubCellData tria_subcell_data;
6488 std::tie(tria_vertices, tria_cells, tria_subcell_data) =
6490
6491 vertices.insert(vertices.end(),
6492 tria_vertices.begin(),
6493 tria_vertices.end());
6494 for (CellData<dim> &cell_data : tria_cells)
6495 {
6496 for (unsigned int &vertex_n : cell_data.vertices)
6497 vertex_n += n_accumulated_vertices;
6498 cells.push_back(cell_data);
6499 }
6500
6501 // Skip copying lines with no manifold information.
6502 if (copy_manifold_ids)
6503 {
6504 for (CellData<1> &line_data : tria_subcell_data.boundary_lines)
6505 {
6506 if (line_data.manifold_id == numbers::flat_manifold_id)
6507 continue;
6508 for (unsigned int &vertex_n : line_data.vertices)
6509 vertex_n += n_accumulated_vertices;
6510 line_data.boundary_id =
6512 subcell_data.boundary_lines.push_back(line_data);
6513 }
6514
6515 for (CellData<2> &quad_data : tria_subcell_data.boundary_quads)
6516 {
6517 if (quad_data.manifold_id == numbers::flat_manifold_id)
6518 continue;
6519 for (unsigned int &vertex_n : quad_data.vertices)
6520 vertex_n += n_accumulated_vertices;
6521 quad_data.boundary_id =
6523 subcell_data.boundary_quads.push_back(quad_data);
6524 }
6525 }
6526
6527 n_accumulated_vertices += triangulation->n_vertices();
6528 }
6529
6530 // throw out duplicated vertices
6531 std::vector<unsigned int> considered_vertices;
6533 cells,
6534 subcell_data,
6535 considered_vertices,
6536 duplicated_vertex_tolerance);
6537
6538 // reorder the cells to ensure that they satisfy the convention for
6539 // edge and face directions
6540 if (std::all_of(cells.begin(), cells.end(), [](const auto &cell) {
6541 return cell.vertices.size() ==
6542 ReferenceCells::get_hypercube<dim>().n_vertices();
6543 }))
6545 result.clear();
6546 result.create_triangulation(vertices, cells, subcell_data);
6547 }
6548
6549
6550
6551 template <int dim, int spacedim>
6552 void
6554 const Triangulation<dim, spacedim> &triangulation_2,
6556 const double duplicated_vertex_tolerance,
6557 const bool copy_manifold_ids)
6558 {
6559 // if either Triangulation is empty then merging is just a copy.
6560 if (triangulation_1.n_cells() == 0)
6561 {
6562 result.copy_triangulation(triangulation_2);
6563 return;
6564 }
6565 if (triangulation_2.n_cells() == 0)
6566 {
6567 result.copy_triangulation(triangulation_1);
6568 return;
6569 }
6570 merge_triangulations({&triangulation_1, &triangulation_2},
6571 result,
6572 duplicated_vertex_tolerance,
6573 copy_manifold_ids);
6574 }
6575
6576
6577
6578 namespace
6579 {
6601 template <int structdim>
6602 void
6603 delete_duplicated_objects(std::vector<CellData<structdim>> &subcell_data)
6604 {
6605 static_assert(structdim == 1 || structdim == 2,
6606 "This function is only implemented for lines and "
6607 "quadrilaterals.");
6608 // start by making sure that all objects representing the same vertices
6609 // are numbered in the same way by canonicalizing the numberings. This
6610 // makes it possible to detect duplicates.
6611 for (CellData<structdim> &cell_data : subcell_data)
6612 {
6613 if (structdim == 1)
6614 std::sort(std::begin(cell_data.vertices),
6615 std::end(cell_data.vertices));
6616 else if (structdim == 2)
6617 {
6618 // rotate the vertex numbers so that the lowest one is first
6619 std::array<unsigned int, 4> renumbering;
6620 std::copy(std::begin(cell_data.vertices),
6621 std::end(cell_data.vertices),
6622 renumbering.begin());
6623
6624 // convert to old style vertex numbering. This makes the
6625 // permutations easy since the valid configurations are
6626 //
6627 // 3 2 2 1 1 0 0 3
6628 // 0 1 3 0 2 3 1 2
6629 // (0123) (3012) (2310) (1230)
6630 //
6631 // rather than the lexical ordering which is harder to permute
6632 // by rotation.
6633 std::swap(renumbering[2], renumbering[3]);
6634 std::rotate(renumbering.begin(),
6635 std::min_element(renumbering.begin(),
6636 renumbering.end()),
6637 renumbering.end());
6638 // convert to new style
6639 std::swap(renumbering[2], renumbering[3]);
6640 // deal with cases where we might have
6641 //
6642 // 3 2 1 2
6643 // 0 1 0 3
6644 //
6645 // by forcing the second vertex (in lexical ordering) to be
6646 // smaller than the third
6647 if (renumbering[1] > renumbering[2])
6648 std::swap(renumbering[1], renumbering[2]);
6649 std::copy(renumbering.begin(),
6650 renumbering.end(),
6651 std::begin(cell_data.vertices));
6652 }
6653 }
6654
6655 // Now that all cell objects have been canonicalized they can be sorted:
6656 auto compare = [](const CellData<structdim> &a,
6657 const CellData<structdim> &b) {
6658 return std::lexicographical_compare(std::begin(a.vertices),
6659 std::end(a.vertices),
6660 std::begin(b.vertices),
6661 std::end(b.vertices));
6662 };
6663 std::sort(subcell_data.begin(), subcell_data.end(), compare);
6664
6665 // Finally, determine which objects are duplicates. Duplicates are
6666 // assumed to be interior objects, so delete all but one and change the
6667 // boundary id:
6668 auto left = subcell_data.begin();
6669 while (left != subcell_data.end())
6670 {
6671 const auto right =
6672 std::upper_bound(left, subcell_data.end(), *left, compare);
6673 // if the range has more than one item, then there are duplicates -
6674 // set all boundary ids in the range to the internal boundary id
6675 if (left + 1 != right)
6676 for (auto it = left; it != right; ++it)
6677 {
6678 it->boundary_id = numbers::internal_face_boundary_id;
6679 Assert(it->manifold_id == left->manifold_id,
6680 ExcMessage(
6681 "In the process of grid generation a single "
6682 "line or quadrilateral has been assigned two "
6683 "different manifold ids. This can happen when "
6684 "a Triangulation is copied, e.g., via "
6685 "GridGenerator::replicate_triangulation() and "
6686 "not all external boundary faces have the same "
6687 "manifold id. Double check that all faces "
6688 "which you expect to be merged together have "
6689 "the same manifold id."));
6690 }
6691 left = right;
6692 }
6693
6694 subcell_data.erase(std::unique(subcell_data.begin(), subcell_data.end()),
6695 subcell_data.end());
6696 }
6697 } // namespace
6698
6699
6700
6701 template <int dim, int spacedim>
6702 void
6704 const std::vector<unsigned int> & extents,
6706 {
6707 AssertDimension(dim, extents.size());
6708# ifdef DEBUG
6709 for (const auto &extent : extents)
6710 Assert(0 < extent,
6711 ExcMessage("The Triangulation must be copied at least one time in "
6712 "each coordinate dimension."));
6713# endif
6714 const BoundingBox<spacedim> bbox(input.get_vertices());
6715 const auto & min = bbox.get_boundary_points().first;
6716 const auto & max = bbox.get_boundary_points().second;
6717
6718 std::array<Tensor<1, spacedim>, dim> offsets;
6719 for (unsigned int d = 0; d < dim; ++d)
6720 offsets[d][d] = max[d] - min[d];
6721
6722 Triangulation<dim, spacedim> tria_to_replicate;
6723 tria_to_replicate.copy_triangulation(input);
6724 for (unsigned int d = 0; d < dim; ++d)
6725 {
6726 std::vector<Point<spacedim>> input_vertices;
6727 std::vector<CellData<dim>> input_cell_data;
6728 SubCellData input_subcell_data;
6729 std::tie(input_vertices, input_cell_data, input_subcell_data) =
6730 GridTools::get_coarse_mesh_description(tria_to_replicate);
6731 std::vector<Point<spacedim>> output_vertices = input_vertices;
6732 std::vector<CellData<dim>> output_cell_data = input_cell_data;
6733 SubCellData output_subcell_data = input_subcell_data;
6734
6735 for (unsigned int k = 1; k < extents[d]; ++k)
6736 {
6737 const std::size_t vertex_offset = k * input_vertices.size();
6738 // vertices
6739 for (const Point<spacedim> &point : input_vertices)
6740 output_vertices.push_back(point + double(k) * offsets[d]);
6741 // cell data
6742 for (const CellData<dim> &cell_data : input_cell_data)
6743 {
6744 output_cell_data.push_back(cell_data);
6745 for (unsigned int &vertex : output_cell_data.back().vertices)
6746 vertex += vertex_offset;
6747 }
6748 // subcell data
6749 for (const CellData<1> &boundary_line :
6750 input_subcell_data.boundary_lines)
6751 {
6752 output_subcell_data.boundary_lines.push_back(boundary_line);
6753 for (unsigned int &vertex :
6754 output_subcell_data.boundary_lines.back().vertices)
6755 vertex += vertex_offset;
6756 }
6757 for (const CellData<2> &boundary_quad :
6758 input_subcell_data.boundary_quads)
6759 {
6760 output_subcell_data.boundary_quads.push_back(boundary_quad);
6761 for (unsigned int &vertex :
6762 output_subcell_data.boundary_quads.back().vertices)
6763 vertex += vertex_offset;
6764 }
6765 }
6766 // check all vertices: since the grid is coarse, most will be on the
6767 // boundary anyway
6768 std::vector<unsigned int> boundary_vertices;
6770 output_vertices,
6771 output_cell_data,
6772 output_subcell_data,
6773 boundary_vertices,
6774 1e-6 * input.begin_active()->diameter());
6775 // delete_duplicated_vertices also deletes any unused vertices
6776 // deal with any reordering issues created by delete_duplicated_vertices
6777 GridTools::consistently_order_cells(output_cell_data);
6778 // clean up the boundary ids of the boundary objects: note that we
6779 // have to do this after delete_duplicated_vertices so that boundary
6780 // objects are actually duplicated at this point
6781 if (dim == 2)
6782 delete_duplicated_objects(output_subcell_data.boundary_lines);
6783 else if (dim == 3)
6784 {
6785 delete_duplicated_objects(output_subcell_data.boundary_quads);
6786 for (CellData<1> &boundary_line :
6787 output_subcell_data.boundary_lines)
6788 // set boundary lines to the default value - let
6789 // create_triangulation figure out the rest.
6791 }
6792
6793 tria_to_replicate.clear();
6794 tria_to_replicate.create_triangulation(output_vertices,
6795 output_cell_data,
6796 output_subcell_data);
6797 }
6798
6799 result.copy_triangulation(tria_to_replicate);
6800 }
6801
6802
6803
6804 template <int dim, int spacedim>
6805 void
6807 const Triangulation<dim, spacedim> &triangulation_1,
6808 const Triangulation<dim, spacedim> &triangulation_2,
6810 {
6811 Assert(GridTools::have_same_coarse_mesh(triangulation_1, triangulation_2),
6812 ExcMessage("The two input triangulations are not derived from "
6813 "the same coarse mesh as required."));
6814 Assert((dynamic_cast<
6816 &triangulation_1) == nullptr) &&
6817 (dynamic_cast<
6819 &triangulation_2) == nullptr),
6820 ExcMessage("The source triangulations for this function must both "
6821 "be available entirely locally, and not be distributed "
6822 "triangulations."));
6823
6824 // first copy triangulation_1, and
6825 // then do as many iterations as
6826 // there are levels in
6827 // triangulation_2 to refine
6828 // additional cells. since this is
6829 // the maximum number of
6830 // refinements to get from the
6831 // coarse grid to triangulation_2,
6832 // it is clear that this is also
6833 // the maximum number of
6834 // refinements to get from any cell
6835 // on triangulation_1 to
6836 // triangulation_2
6837 result.clear();
6838 result.copy_triangulation(triangulation_1);
6839 for (unsigned int iteration = 0; iteration < triangulation_2.n_levels();
6840 ++iteration)
6841 {
6843 intergrid_map.make_mapping(result, triangulation_2);
6844
6845 bool any_cell_flagged = false;
6846 for (const auto &result_cell : result.active_cell_iterators())
6847 if (intergrid_map[result_cell]->has_children())
6848 {
6849 any_cell_flagged = true;
6850 result_cell->set_refine_flag();
6851 }
6852
6853 if (any_cell_flagged == false)
6854 break;
6855 else
6857 }
6858 }
6859
6860
6861
6862 template <int dim, int spacedim>
6863 void
6865 const Triangulation<dim, spacedim> &input_triangulation,
6867 & cells_to_remove,
6869 {
6870 // simply copy the vertices; we will later strip those
6871 // that turn out to be unused
6872 std::vector<Point<spacedim>> vertices = input_triangulation.get_vertices();
6873
6874 // the loop through the cells and copy stuff, excluding
6875 // the ones we are to remove
6876 std::vector<CellData<dim>> cells;
6877 for (const auto &cell : input_triangulation.active_cell_iterators())
6878 if (cells_to_remove.find(cell) == cells_to_remove.end())
6879 {
6880 Assert(static_cast<unsigned int>(cell->level()) ==
6881 input_triangulation.n_levels() - 1,
6882 ExcMessage(
6883 "Your input triangulation appears to have "
6884 "adaptively refined cells. This is not allowed. You can "
6885 "only call this function on a triangulation in which "
6886 "all cells are on the same refinement level."));
6887
6888 CellData<dim> this_cell;
6889 for (const unsigned int v : GeometryInfo<dim>::vertex_indices())
6890 this_cell.vertices[v] = cell->vertex_index(v);
6891 this_cell.material_id = cell->material_id();
6892 cells.push_back(this_cell);
6893 }
6894
6895 // throw out duplicated vertices from the two meshes, reorder vertices as
6896 // necessary and create the triangulation
6897 SubCellData subcell_data;
6898 std::vector<unsigned int> considered_vertices;
6900 cells,
6901 subcell_data,
6902 considered_vertices);
6903
6904 // then clear the old triangulation and create the new one
6905 result.clear();
6906 result.create_triangulation(vertices, cells, subcell_data);
6907 }
6908
6909
6910
6911 void
6913 const Triangulation<2, 2> & input,
6914 const unsigned int n_slices,
6915 const double height,
6916 Triangulation<3, 3> & result,
6917 const bool copy_manifold_ids,
6918 const std::vector<types::manifold_id> &manifold_priorities)
6919 {
6920 Assert(input.n_levels() == 1,
6921 ExcMessage(
6922 "The input triangulation must be a coarse mesh, i.e., it must "
6923 "not have been refined."));
6924 Assert(result.n_cells() == 0,
6925 ExcMessage("The output triangulation object needs to be empty."));
6926 Assert(height > 0,
6927 ExcMessage("The given height for extrusion must be positive."));
6928 Assert(n_slices >= 2,
6929 ExcMessage(
6930 "The number of slices for extrusion must be at least 2."));
6931
6932 const double delta_h = height / (n_slices - 1);
6933 std::vector<double> slices_z_values;
6934 for (unsigned int i = 0; i < n_slices; ++i)
6935 slices_z_values.push_back(i * delta_h);
6937 input, slices_z_values, result, copy_manifold_ids, manifold_priorities);
6938 }
6939
6940
6941
6942 void
6944 const Triangulation<2, 2> & input,
6945 const unsigned int n_slices,
6946 const double height,
6947 Triangulation<2, 2> & result,
6948 const bool copy_manifold_ids,
6949 const std::vector<types::manifold_id> &manifold_priorities)
6950 {
6951 (void)input;
6952 (void)n_slices;
6953 (void)height;
6954 (void)result;
6955 (void)copy_manifold_ids;
6956 (void)manifold_priorities;
6957
6958 AssertThrow(false,
6959 ExcMessage(
6960 "GridTools::extrude_triangulation() is only available "
6961 "for Triangulation<3, 3> as output triangulation."));
6962 }
6963
6964
6965
6966 void
6968 const Triangulation<2, 2> & input,
6969 const std::vector<double> & slice_coordinates,
6970 Triangulation<3, 3> & result,
6971 const bool copy_manifold_ids,
6972 const std::vector<types::manifold_id> &manifold_priorities)
6973 {
6974 Assert(input.n_levels() == 1,
6975 ExcMessage(
6976 "The input triangulation must be a coarse mesh, i.e., it must "
6977 "not have been refined."));
6978 Assert(result.n_cells() == 0,
6979 ExcMessage("The output triangulation object needs to be empty."));
6980 Assert(slice_coordinates.size() >= 2,
6981 ExcMessage(
6982 "The number of slices for extrusion must be at least 2."));
6983 Assert(std::is_sorted(slice_coordinates.begin(), slice_coordinates.end()),
6984 ExcMessage("Slice z-coordinates should be in ascending order"));
6985
6986 const auto priorities = [&]() -> std::vector<types::manifold_id> {
6987 // if a non-empty (i.e., not the default) vector is given for
6988 // manifold_priorities then use it (but check its validity in debug
6989 // mode)
6990 if (0 < manifold_priorities.size())
6991 {
6992# ifdef DEBUG
6993 // check that the provided manifold_priorities is valid
6994 std::vector<types::manifold_id> sorted_manifold_priorities =
6995 manifold_priorities;
6996 std::sort(sorted_manifold_priorities.begin(),
6997 sorted_manifold_priorities.end());
6998 Assert(std::unique(sorted_manifold_priorities.begin(),
6999 sorted_manifold_priorities.end()) ==
7000 sorted_manifold_priorities.end(),
7001 ExcMessage(
7002 "The given vector of manifold ids may not contain any "
7003 "duplicated entries."));
7004 std::vector<types::manifold_id> sorted_manifold_ids =
7005 input.get_manifold_ids();
7006 std::sort(sorted_manifold_ids.begin(), sorted_manifold_ids.end());
7007 if (sorted_manifold_priorities != sorted_manifold_ids)
7008 {
7009 std::ostringstream message;
7010 message << "The given triangulation has manifold ids {";
7011 for (const types::manifold_id manifold_id : sorted_manifold_ids)
7012 if (manifold_id != sorted_manifold_ids.back())
7013 message << manifold_id << ", ";
7014 message << sorted_manifold_ids.back() << "}, but \n"
7015 << " the given vector of manifold ids is {";
7016 for (const types::manifold_id manifold_id : manifold_priorities)
7017 if (manifold_id != manifold_priorities.back())
7018 message << manifold_id << ", ";
7019 message
7020 << manifold_priorities.back() << "}.\n"
7021 << " These vectors should contain the same elements.\n";
7022 const std::string m = message.str();
7023 Assert(false, ExcMessage(m));
7024 }
7025# endif
7026 return manifold_priorities;
7027 }
7028 // otherwise use the default ranking: ascending order, but TFI manifolds
7029 // are at the end.
7030 std::vector<types::manifold_id> default_priorities =
7031 input.get_manifold_ids();
7032 const auto first_tfi_it = std::partition(
7033 default_priorities.begin(),
7034 default_priorities.end(),
7035 [&input](const types::manifold_id &id) {
7036 return dynamic_cast<const TransfiniteInterpolationManifold<2, 2> *>(
7037 &input.get_manifold(id)) == nullptr;
7038 });
7039 std::sort(default_priorities.begin(), first_tfi_it);
7040 std::sort(first_tfi_it, default_priorities.end());
7041
7042 return default_priorities;
7043 }();
7044
7045 const std::size_t n_slices = slice_coordinates.size();
7046 std::vector<Point<3>> points(n_slices * input.n_vertices());
7047 std::vector<CellData<3>> cells;
7048 cells.reserve((n_slices - 1) * input.n_active_cells());
7049
7050 // copy the array of points as many times as there will be slices,
7051 // one slice at a time. The z-axis value are defined in slices_coordinates
7052 for (std::size_t slice_n = 0; slice_n < n_slices; ++slice_n)
7053 {
7054 for (std::size_t vertex_n = 0; vertex_n < input.n_vertices();
7055 ++vertex_n)
7056 {
7057 const Point<2> vertex = input.get_vertices()[vertex_n];
7058 points[slice_n * input.n_vertices() + vertex_n] =
7059 Point<3>(vertex[0], vertex[1], slice_coordinates[slice_n]);
7060 }
7061 }
7062
7063 // then create the cells of each of the slices, one stack at a
7064 // time
7065 for (const auto &cell : input.active_cell_iterators())
7066 {
7067 for (std::size_t slice_n = 0; slice_n < n_slices - 1; ++slice_n)
7068 {
7069 CellData<3> this_cell;
7070 for (const unsigned int vertex_n :
7072 {
7073 this_cell.vertices[vertex_n] =
7074 cell->vertex_index(vertex_n) + slice_n * input.n_vertices();
7075 this_cell
7077 cell->vertex_index(vertex_n) +
7078 (slice_n + 1) * input.n_vertices();
7079 }
7080
7081 this_cell.material_id = cell->material_id();
7082 if (copy_manifold_ids)
7083 this_cell.manifold_id = cell->manifold_id();
7084 cells.push_back(this_cell);
7085 }
7086 }
7087
7088 // Next, create face data for all faces that are orthogonal to the x-y
7089 // plane
7090 SubCellData subcell_data;
7091 std::vector<CellData<2>> &quads = subcell_data.boundary_quads;
7092 types::boundary_id max_boundary_id = 0;
7093 quads.reserve(input.n_active_lines() * (n_slices - 1) +
7094 input.n_active_cells() * 2);
7095 for (const auto &face : input.active_face_iterators())
7096 {
7097 CellData<2> quad;
7098 quad.boundary_id = face->boundary_id();
7099 if (face->at_boundary())
7100 max_boundary_id = std::max(max_boundary_id, quad.boundary_id);
7101 if (copy_manifold_ids)
7102 quad.manifold_id = face->manifold_id();
7103 for (std::size_t slice_n = 0; slice_n < n_slices - 1; ++slice_n)
7104 {
7105 quad.vertices[0] =
7106 face->vertex_index(0) + slice_n * input.n_vertices();
7107 quad.vertices[1] =
7108 face->vertex_index(1) + slice_n * input.n_vertices();
7109 quad.vertices[2] =
7110 face->vertex_index(0) + (slice_n + 1) * input.n_vertices();
7111 quad.vertices[3] =
7112 face->vertex_index(1) + (slice_n + 1) * input.n_vertices();
7113 quads.push_back(quad);
7114 }
7115 }
7116
7117 // if necessary, create face data for faces parallel to the x-y
7118 // plane. This is only necessary if we need to set manifolds.
7119 if (copy_manifold_ids)
7120 for (const auto &cell : input.active_cell_iterators())
7121 {
7122 CellData<2> quad;
7124 quad.manifold_id = cell->manifold_id(); // check is outside loop
7125 for (std::size_t slice_n = 1; slice_n < n_slices - 1; ++slice_n)
7126 {
7127 quad.vertices[0] =
7128 cell->vertex_index(0) + slice_n * input.n_vertices();
7129 quad.vertices[1] =
7130 cell->vertex_index(1) + slice_n * input.n_vertices();
7131 quad.vertices[2] =
7132 cell->vertex_index(2) + slice_n * input.n_vertices();
7133 quad.vertices[3] =
7134 cell->vertex_index(3) + slice_n * input.n_vertices();
7135 quads.push_back(quad);
7136 }
7137 }
7138
7139 // then mark the bottom and top boundaries of the extruded mesh
7140 // with max_boundary_id+1 and max_boundary_id+2. check that this
7141 // remains valid
7142 Assert((max_boundary_id != numbers::invalid_boundary_id) &&
7143 (max_boundary_id + 1 != numbers::invalid_boundary_id) &&
7144 (max_boundary_id + 2 != numbers::invalid_boundary_id),
7145 ExcMessage(
7146 "The input triangulation to this function is using boundary "
7147 "indicators in a range that do not allow using "
7148 "max_boundary_id+1 and max_boundary_id+2 as boundary "
7149 "indicators for the bottom and top faces of the "
7150 "extruded triangulation."));
7151 const types::boundary_id bottom_boundary_id = max_boundary_id + 1;
7152 const types::boundary_id top_boundary_id = max_boundary_id + 2;
7153 for (const auto &cell : input.active_cell_iterators())
7154 {
7155 CellData<2> quad;
7156 quad.boundary_id = bottom_boundary_id;
7157 quad.vertices[0] = cell->vertex_index(0);
7158 quad.vertices[1] = cell->vertex_index(1);
7159 quad.vertices[2] = cell->vertex_index(2);
7160 quad.vertices[3] = cell->vertex_index(3);
7161 if (copy_manifold_ids)
7162 quad.manifold_id = cell->manifold_id();
7163 quads.push_back(quad);
7164
7165 quad.boundary_id = top_boundary_id;
7166 for (unsigned int &vertex : quad.vertices)
7167 vertex += (n_slices - 1) * input.n_vertices();
7168 if (copy_manifold_ids)
7169 quad.manifold_id = cell->manifold_id();
7170 quads.push_back(quad);
7171 }
7172
7173 // use all of this to finally create the extruded 3d
7174 // triangulation. it is not necessary to call
7175 // GridTools::consistently_order_cells() because the cells we have
7176 // constructed above are automatically correctly oriented. this is
7177 // because the 2d base mesh is always correctly oriented, and
7178 // extruding it automatically yields a correctly oriented 3d mesh,
7179 // as discussed in the edge orientation paper mentioned in the
7180 // introduction to the @ref reordering "reordering module".
7181 result.create_triangulation(points, cells, subcell_data);
7182
7183 for (auto manifold_id_it = priorities.rbegin();
7184 manifold_id_it != priorities.rend();
7185 ++manifold_id_it)
7186 for (const auto &face : result.active_face_iterators())
7187 if (face->manifold_id() == *manifold_id_it)
7188 for (unsigned int line_n = 0;
7189 line_n < GeometryInfo<3>::lines_per_face;
7190 ++line_n)
7191 face->line(line_n)->set_manifold_id(*manifold_id_it);
7192 }
7193
7194
7195
7196 void
7198 const Triangulation<2, 2> & input,
7199 const std::vector<double> & slice_coordinates,
7200 Triangulation<2, 2> & result,
7201 const bool copy_manifold_ids,
7202 const std::vector<types::manifold_id> &manifold_priorities)
7203 {
7204 (void)input;
7205 (void)slice_coordinates;
7206 (void)result;
7207 (void)copy_manifold_ids;
7208 (void)manifold_priorities;
7209
7210 AssertThrow(false,
7211 ExcMessage(
7212 "GridTools::extrude_triangulation() is only available "
7213 "for Triangulation<3, 3> as output triangulation."));
7214 }
7215
7216
7217
7218 template <>
7219 void
7221 const double,
7222 const double,
7223 const double,
7224 const unsigned int,
7225 const bool)
7226 {
7227 Assert(false, ExcNotImplemented());
7228 }
7229
7230
7231
7232 template <>
7233 void
7235 const double inner_radius,
7236 const double outer_radius,
7237 const double, // width,
7238 const unsigned int, // width_repetition,
7239 const bool colorize)
7240 {
7241 const int dim = 2;
7242
7243 Assert(inner_radius < outer_radius,
7244 ExcMessage("outer_radius has to be bigger than inner_radius."));
7245
7247 // We create an hyper_shell in two dimensions, and then we modify it.
7248 hyper_shell(triangulation, center, inner_radius, outer_radius, 8);
7249 triangulation.set_all_manifold_ids(numbers::flat_manifold_id);
7251 triangulation.begin_active(),
7252 endc = triangulation.end();
7253 std::vector<bool> treated_vertices(triangulation.n_vertices(), false);
7254 for (; cell != endc; ++cell)
7255 {
7256 for (auto f : GeometryInfo<dim>::face_indices())
7257 if (cell->face(f)->at_boundary())
7258 {
7259 for (unsigned int v = 0; v < GeometryInfo<dim>::vertices_per_face;
7260 ++v)
7261 {
7262 unsigned int vv = cell->face(f)->vertex_index(v);
7263 if (treated_vertices[vv] == false)
7264 {
7265 treated_vertices[vv] = true;
7266 switch (vv)
7267 {
7268 case 1:
7269 cell->face(f)->vertex(v) =
7270 center + Point<dim>(outer_radius, outer_radius);
7271 break;
7272 case 3:
7273 cell->face(f)->vertex(v) =
7274 center + Point<dim>(-outer_radius, outer_radius);
7275 break;
7276 case 5:
7277 cell->face(f)->vertex(v) =
7278 center + Point<dim>(-outer_radius, -outer_radius);
7279 break;
7280 case 7:
7281 cell->face(f)->vertex(v) =
7282 center + Point<dim>(outer_radius, -outer_radius);
7283 break;
7284 default:
7285 break;
7286 }
7287 }
7288 }
7289 }
7290 }
7291 double eps = 1e-3 * outer_radius;
7292 cell = triangulation.begin_active();
7293 for (; cell != endc; ++cell)
7294 {
7295 for (auto f : GeometryInfo<dim>::face_indices())
7296 if (cell->face(f)->at_boundary())
7297 {
7298 double dx = cell->face(f)->center()(0) - center(0);
7299 double dy = cell->face(f)->center()(1) - center(1);
7300 if (colorize)
7301 {
7302 if (std::abs(dx + outer_radius) < eps)
7303 cell->face(f)->set_boundary_id(0);
7304 else if (std::abs(dx - outer_radius) < eps)
7305 cell->face(f)->set_boundary_id(1);
7306 else if (std::abs(dy + outer_radius) < eps)
7307 cell->face(f)->set_boundary_id(2);
7308 else if (std::abs(dy - outer_radius) < eps)
7309 cell->face(f)->set_boundary_id(3);
7310 else
7311 {
7312 cell->face(f)->set_boundary_id(4);
7313 cell->face(f)->set_manifold_id(0);
7314 }
7315 }
7316 else
7317 {
7318 double d = (cell->face(f)->center() - center).norm();
7319 if (d - inner_radius < 0)
7320 {
7321 cell->face(f)->set_boundary_id(1);
7322 cell->face(f)->set_manifold_id(0);
7323 }
7324 else
7325 cell->face(f)->set_boundary_id(0);
7326 }
7327 }
7328 }
7329 triangulation.set_manifold(0, PolarManifold<2>(center));
7330 }
7331
7332
7333
7334 template <int dim>
7335 void
7337 const Point<dim> & center,
7338 const double inner_radius,
7339 const double outer_radius,
7340 const unsigned int n_shells,
7341 const double skewness,
7342 const unsigned int n_cells,
7343 const bool colorize)
7344 {
7345 Assert(dim == 2 || dim == 3, ExcNotImplemented());
7346 (void)colorize;
7347 (void)n_cells;
7348 Assert(inner_radius < outer_radius,
7349 ExcMessage("outer_radius has to be bigger than inner_radius."));
7350 if (n_shells == 0)
7351 return; // empty Triangulation
7352
7353 std::vector<double> radii;
7354 radii.push_back(inner_radius);
7355 for (unsigned int shell_n = 1; shell_n < n_shells; ++shell_n)
7356 if (skewness == 0.0)
7357 // same as below, but works in the limiting case of zero skewness
7358 radii.push_back(inner_radius +
7359 (outer_radius - inner_radius) *
7360 (1.0 - (1.0 - double(shell_n) / n_shells)));
7361 else
7362 radii.push_back(
7363 inner_radius +
7364 (outer_radius - inner_radius) *
7365 (1.0 - std::tanh(skewness * (1.0 - double(shell_n) / n_shells)) /
7366 std::tanh(skewness)));
7367 radii.push_back(outer_radius);
7368
7369 double grid_vertex_tolerance = 0.0;
7370 for (unsigned int shell_n = 0; shell_n < radii.size() - 1; ++shell_n)
7371 {
7372 Triangulation<dim> current_shell;
7373 GridGenerator::hyper_shell(current_shell,
7374 center,
7375 radii[shell_n],
7376 radii[shell_n + 1],
7377 n_cells == 0 ? (dim == 2 ? 8 : 12) :
7378 n_cells);
7379
7380 // The innermost shell has the smallest cells: use that to set the
7381 // vertex merging tolerance
7382 if (grid_vertex_tolerance == 0.0)
7383 grid_vertex_tolerance =
7384 0.5 * internal::minimal_vertex_distance(current_shell);
7385
7386 Triangulation<dim> temp(std::move(triangulation));
7387 triangulation.clear();
7389 temp,
7391 grid_vertex_tolerance);
7392 }
7393
7395 triangulation.set_all_manifold_ids(manifold_id);
7396 if (dim == 2)
7397 triangulation.set_manifold(manifold_id, PolarManifold<dim>(center));
7398 else if (dim == 3)
7399 triangulation.set_manifold(manifold_id, SphericalManifold<dim>(center));
7400
7401 // We use boundary vertex positions to see if things are on the inner or
7402 // outer boundary.
7403 constexpr double radial_vertex_tolerance =
7404 100.0 * std::numeric_limits<double>::epsilon();
7405 auto assert_vertex_distance_within_tolerance =
7406 [center, radial_vertex_tolerance](
7407 const TriaIterator<TriaAccessor<dim - 1, dim, dim>> face,
7408 const double radius) {
7409 (void)center;
7410 (void)radial_vertex_tolerance;
7411 (void)face;
7412 (void)radius;
7413 for (unsigned int vertex_n = 0;
7414 vertex_n < GeometryInfo<dim>::vertices_per_face;
7415 ++vertex_n)
7416 {
7417 Assert(std::abs((face->vertex(vertex_n) - center).norm() - radius) <
7418 (center.norm() + radius) * radial_vertex_tolerance,
7420 }
7421 };
7422 if (colorize)
7423 for (const auto &cell : triangulation.active_cell_iterators())
7424 for (const unsigned int face_n : GeometryInfo<dim>::face_indices())
7425 {
7426 auto face = cell->face(face_n);
7427 if (face->at_boundary())
7428 {
7429 if (((face->vertex(0) - center).norm() - inner_radius) <
7430 (center.norm() + inner_radius) * radial_vertex_tolerance)
7431 {
7432 // we must be at an inner face, but check
7433 assert_vertex_distance_within_tolerance(face, inner_radius);
7434 face->set_all_boundary_ids(0);
7435 }
7436 else
7437 {
7438 // we must be at an outer face, but check
7439 assert_vertex_distance_within_tolerance(face, outer_radius);
7440 face->set_all_boundary_ids(1);
7441 }
7442 }
7443 }
7444 }
7445
7446
7447
7448 template <>
7449 void
7451 const double inner_radius,
7452 const double outer_radius,
7453 const double L,
7454 const unsigned int Nz,
7455 const bool colorize)
7456 {
7457 const int dim = 3;
7458
7459 Assert(inner_radius < outer_radius,
7460 ExcMessage("outer_radius has to be bigger than inner_radius."));
7461 Assert(L > 0, ExcMessage("Must give positive extension L"));
7462 Assert(Nz >= 1, ExcLowerRange(1, Nz));
7463
7464 cylinder_shell(triangulation, L, inner_radius, outer_radius, 8, Nz);
7465 triangulation.set_all_manifold_ids(numbers::flat_manifold_id);
7466
7468 triangulation.begin_active(),
7469 endc = triangulation.end();
7470 std::vector<bool> treated_vertices(triangulation.n_vertices(), false);
7471 for (; cell != endc; ++cell)
7472 {
7473 for (auto f : GeometryInfo<dim>::face_indices())
7474 if (cell->face(f)->at_boundary())
7475 {
7476 for (unsigned int v = 0; v < GeometryInfo<dim>::vertices_per_face;
7477 ++v)
7478 {
7479 unsigned int vv = cell->face(f)->vertex_index(v);
7480 if (treated_vertices[vv] == false)
7481 {
7482 treated_vertices[vv] = true;
7483 for (unsigned int i = 0; i <= Nz; ++i)
7484 {
7485 double d = i * L / Nz;
7486 switch (vv - i * 16)
7487 {
7488 case 1:
7489 cell->face(f)->vertex(v) =
7490 Point<dim>(outer_radius, outer_radius, d);
7491 break;
7492 case 3:
7493 cell->face(f)->vertex(v) =
7494 Point<dim>(-outer_radius, outer_radius, d);
7495 break;
7496 case 5:
7497 cell->face(f)->vertex(v) =
7498 Point<dim>(-outer_radius, -outer_radius, d);
7499 break;
7500 case 7:
7501 cell->face(f)->vertex(v) =
7502 Point<dim>(outer_radius, -outer_radius, d);
7503 break;
7504 default:
7505 break;
7506 }
7507 }
7508 }
7509 }
7510 }
7511 }
7512 double eps = 1e-3 * outer_radius;
7513 cell = triangulation.begin_active();
7514 for (; cell != endc; ++cell)
7515 {
7516 for (auto f : GeometryInfo<dim>::face_indices())
7517 if (cell->face(f)->at_boundary())
7518 {
7519 double dx = cell->face(f)->center()(0);
7520 double dy = cell->face(f)->center()(1);
7521 double dz = cell->face(f)->center()(2);
7522
7523 if (colorize)
7524 {
7525 if (std::abs(dx + outer_radius) < eps)
7526 cell->face(f)->set_boundary_id(0);
7527
7528 else if (std::abs(dx - outer_radius) < eps)
7529 cell->face(f)->set_boundary_id(1);
7530
7531 else if (std::abs(dy + outer_radius) < eps)
7532 cell->face(f)->set_boundary_id(2);
7533
7534 else if (std::abs(dy - outer_radius) < eps)
7535 cell->face(f)->set_boundary_id(3);
7536
7537 else if (std::abs(dz) < eps)
7538 cell->face(f)->set_boundary_id(4);
7539
7540 else if (std::abs(dz - L) < eps)
7541 cell->face(f)->set_boundary_id(5);
7542
7543 else
7544 {
7545 cell->face(f)->set_all_boundary_ids(6);
7546 cell->face(f)->set_all_manifold_ids(0);
7547 }
7548 }
7549 else
7550 {
7551 Point<dim> c = cell->face(f)->center();
7552 c(2) = 0;
7553 double d = c.norm();
7554 if (d - inner_radius < 0)
7555 {
7556 cell->face(f)->set_all_boundary_ids(1);
7557 cell->face(f)->set_all_manifold_ids(0);
7558 }
7559 else
7560 cell->face(f)->set_boundary_id(0);
7561 }
7562 }
7563 }
7564 triangulation.set_manifold(0, CylindricalManifold<3>(2));
7565 }
7566
7567
7568
7569 template <int dim, int spacedim1, int spacedim2>
7570 void
7573 {
7574 Assert((dynamic_cast<
7576 &in_tria) == nullptr),
7577 ExcMessage(
7578 "This function cannot be used on "
7579 "parallel::distributed::Triangulation objects as inputs."));
7580 Assert(in_tria.has_hanging_nodes() == false,
7581 ExcMessage("This function does not work for meshes that have "
7582 "hanging nodes."));
7583
7584
7585 const unsigned int spacedim = std::min(spacedim1, spacedim2);
7586 const std::vector<Point<spacedim1>> &in_vertices = in_tria.get_vertices();
7587
7588 // Create an array of vertices, with components either truncated
7589 // or extended by zeroes.
7590 std::vector<Point<spacedim2>> v(in_vertices.size());
7591 for (unsigned int i = 0; i < in_vertices.size(); ++i)
7592 for (unsigned int d = 0; d < spacedim; ++d)
7593 v[i][d] = in_vertices[i][d];
7594
7595 std::vector<CellData<dim>> cells(in_tria.n_active_cells());
7596 for (const auto &cell : in_tria.active_cell_iterators())
7597 {
7598 const unsigned int id = cell->active_cell_index();
7599
7600 cells[id].vertices.resize(cell->n_vertices());
7601 for (const auto i : cell->vertex_indices())
7602 cells[id].vertices[i] = cell->vertex_index(i);
7603 cells[id].material_id = cell->material_id();
7604 cells[id].manifold_id = cell->manifold_id();
7605 }
7606
7607 SubCellData subcelldata;
7608 switch (dim)
7609 {
7610 case 1:
7611 {
7612 // Nothing to do in 1d
7613 break;
7614 }
7615
7616 case 2:
7617 {
7618 std::vector<bool> user_flags_line;
7619 in_tria.save_user_flags_line(user_flags_line);
7620 const_cast<Triangulation<dim, spacedim1> &>(in_tria)
7621 .clear_user_flags_line();
7622
7623 // Loop over all the faces of the triangulation and create
7624 // objects that describe their boundary and manifold ids.
7625 for (const auto &face : in_tria.active_face_iterators())
7626 {
7627 if (face->at_boundary())
7628 {
7629 CellData<1> boundary_line;
7630
7631 boundary_line.vertices.resize(face->n_vertices());
7632 for (const auto i : face->vertex_indices())
7633 boundary_line.vertices[i] = face->vertex_index(i);
7634 boundary_line.boundary_id = face->boundary_id();
7635 boundary_line.manifold_id = face->manifold_id();
7636
7637 subcelldata.boundary_lines.emplace_back(
7638 std::move(boundary_line));
7639 }
7640 else
7641 // The face is not at the boundary. We won't have to set
7642 // boundary_ids (that is not possible for interior faces), but
7643 // we need to do something if the manifold-id is not the
7644 // default.
7645 //
7646 // We keep track via the user flags whether we have already
7647 // dealt with a face or not. (We need to do that here because
7648 // we will return to interior faces twice, once for each
7649 // neighbor, whereas we only touch each of the boundary faces
7650 // above once.)
7651 if ((face->user_flag_set() == false) &&
7652 (face->manifold_id() != numbers::flat_manifold_id))
7653 {
7654 CellData<1> boundary_line;
7655
7656 boundary_line.vertices.resize(face->n_vertices());
7657 for (const auto i : face->vertex_indices())
7658 boundary_line.vertices[i] = face->vertex_index(i);
7659 boundary_line.boundary_id =
7661 boundary_line.manifold_id = face->manifold_id();
7662
7663 subcelldata.boundary_lines.emplace_back(
7664 std::move(boundary_line));
7665
7666 face->set_user_flag();
7667 }
7668 }
7669
7670 // Reset the user flags to their previous values:
7671 const_cast<Triangulation<dim, spacedim1> &>(in_tria)
7672 .load_user_flags_line(user_flags_line);
7673
7674 break;
7675 }
7676
7677 case 3:
7678 {
7679 std::vector<bool> user_flags_line;
7680 in_tria.save_user_flags_line(user_flags_line);
7681 const_cast<Triangulation<dim, spacedim1> &>(in_tria)
7682 .clear_user_flags_line();
7683
7684 std::vector<bool> user_flags_quad;
7685 in_tria.save_user_flags_quad(user_flags_quad);
7686 const_cast<Triangulation<dim, spacedim1> &>(in_tria)
7687 .clear_user_flags_quad();
7688
7689 // Loop over all the faces of the triangulation and create
7690 // objects that describe their boundary and manifold ids.
7691 for (const auto &face : in_tria.active_face_iterators())
7692 {
7693 if (face->at_boundary())
7694 {
7695 CellData<2> boundary_face;
7696
7697 boundary_face.vertices.resize(face->n_vertices());
7698 for (const auto i : face->vertex_indices())
7699 boundary_face.vertices[i] = face->vertex_index(i);
7700 boundary_face.boundary_id = face->boundary_id();
7701 boundary_face.manifold_id = face->manifold_id();
7702
7703 subcelldata.boundary_quads.emplace_back(
7704 std::move(boundary_face));
7705
7706 // Then also loop over the edges and do the same. We would
7707 // accidentally create duplicates for edges that are part of
7708 // two boundary faces. To avoid this, use the user_flag on
7709 // edges to mark those that we have already visited. (Note
7710 // how we save and restore those above and below.)
7711 for (unsigned int e = 0; e < face->n_lines(); ++e)
7712 if (face->line(e)->user_flag_set() == false)
7713 {
7714 const typename Triangulation<dim,
7715 spacedim1>::line_iterator
7716 edge = face->line(e);
7717 CellData<1> boundary_edge;
7718
7719 boundary_edge.vertices.resize(edge->n_vertices());
7720 for (const auto i : edge->vertex_indices())
7721 boundary_edge.vertices[i] = edge->vertex_index(i);
7722 boundary_edge.boundary_id = edge->boundary_id();
7723 boundary_edge.manifold_id = edge->manifold_id();
7724
7725 subcelldata.boundary_lines.emplace_back(
7726 std::move(boundary_edge));
7727
7728 edge->set_user_flag();
7729 }
7730 }
7731 else
7732 // The face is not at the boundary. We won't have to set
7733 // boundary_ids (that is not possible for interior faces), but
7734 // we need to do something if the manifold-id is not the
7735 // default.
7736 //
7737 // We keep track via the user flags whether we have already
7738 // dealt with a face or not. (We need to do that here because
7739 // we will return to interior faces twice, once for each
7740 // neighbor, whereas we only touch each of the boundary faces
7741 // above once.)
7742 //
7743 // Note that if we have already dealt with a face, then we
7744 // have also already dealt with the edges and don't have
7745 // to worry about that any more separately.
7746 if (face->user_flag_set() == false)
7747 {
7748 if (face->manifold_id() != numbers::flat_manifold_id)
7749 {
7750 CellData<2> boundary_face;
7751
7752 boundary_face.vertices.resize(face->n_vertices());
7753 for (const auto i : face->vertex_indices())
7754 boundary_face.vertices[i] = face->vertex_index(i);
7755 boundary_face.boundary_id =
7757 boundary_face.manifold_id = face->manifold_id();
7758
7759 subcelldata.boundary_quads.emplace_back(
7760 std::move(boundary_face));
7761
7762 face->set_user_flag();
7763 }
7764
7765 // Then also loop over the edges of this face. Because every
7766 // boundary edge must also be a part of a boundary face, we
7767 // can ignore these. But it is possible that we have already
7768 // encountered an interior edge through a previous face, and
7769 // in that case we have to just ignore it
7770 for (unsigned int e = 0; e < face->n_lines(); ++e)
7771 if (face->line(e)->at_boundary() == false)
7772 if (face->line(e)->user_flag_set() == false)
7773 {
7774 const typename Triangulation<dim, spacedim1>::
7775 line_iterator edge = face->line(e);
7776 CellData<1> boundary_edge;
7777
7778 boundary_edge.vertices.resize(edge->n_vertices());
7779 for (const auto i : edge->vertex_indices())
7780 boundary_edge.vertices[i] = edge->vertex_index(i);
7781 boundary_edge.boundary_id =
7783 boundary_edge.manifold_id = edge->manifold_id();
7784
7785 subcelldata.boundary_lines.emplace_back(
7786 std::move(boundary_edge));
7787
7788 edge->set_user_flag();
7789 }
7790 }
7791 }
7792
7793 // Reset the user flags to their previous values:
7794 const_cast<Triangulation<dim, spacedim1> &>(in_tria)
7795 .load_user_flags_line(user_flags_line);
7796 const_cast<Triangulation<dim, spacedim1> &>(in_tria)
7797 .load_user_flags_quad(user_flags_quad);
7798
7799 break;
7800 }
7801 default:
7802 Assert(false, ExcInternalError());
7803 }
7804
7805 out_tria.create_triangulation(v, cells, subcelldata);
7806 }
7807
7808
7809
7810 template <int dim, int spacedim>
7811 void
7814 {
7815 Assert(dim > 1, ExcNotImplemented());
7816
7818 if (in_tria.n_global_levels() > 1)
7819 {
7821 flatten_triangulation(in_tria, temp_tria);
7822 }
7823 const Triangulation<dim, spacedim> &ref_tria =
7824 in_tria.n_global_levels() > 1 ? temp_tria : in_tria;
7825
7826 /* static tables with the definitions of cells, faces and edges by its
7827 * vertices for 2D and 3D. For the inheritance of the manifold_id,
7828 * definitions of inner-faces and boundary-faces are required. In case of
7829 * 3D, also inner-edges and boundary-edges need to be defined.
7830 */
7831
7832 /* Cell definition 2D:
7833 * A quadrilateral element is converted to 8 simplices elements. Each
7834 * triangle is defined by 3 vertices.
7835 */
7836 static const ndarray<unsigned int, 8, 3> table_2D_cell = {{{{0, 6, 4}},
7837 {{8, 4, 6}},
7838 {{8, 6, 5}},
7839 {{1, 5, 6}},
7840 {{2, 4, 7}},
7841 {{8, 7, 4}},
7842 {{8, 5, 7}},
7843 {{3, 7, 5}}}};
7844
7845 /* Cell definition 3D:
7846 * A hexahedron element is converted to 24 tetrahedron elements. Each
7847 * tetrahedron is defined by 4 vertices.
7848 */
7849 static const ndarray<unsigned int, 24, 4> vertex_ids_for_cells_3d = {
7850 {{{0, 1, 12, 10}}, {{2, 3, 11, 12}}, {{7, 6, 11, 13}},
7851 {{5, 4, 13, 10}}, {{0, 2, 8, 12}}, {{4, 6, 13, 8}},
7852 {{5, 13, 7, 9}}, {{1, 9, 3, 12}}, {{0, 8, 4, 10}},
7853 {{1, 5, 9, 10}}, {{3, 7, 11, 9}}, {{2, 6, 8, 11}},
7854 {{12, 13, 10, 9}}, {{12, 13, 9, 11}}, {{12, 13, 11, 8}},
7855 {{12, 13, 8, 10}}, {{13, 8, 10, 4}}, {{13, 10, 9, 5}},
7856 {{13, 9, 11, 7}}, {{13, 11, 8, 6}}, {{10, 12, 9, 1}},
7857 {{9, 12, 11, 3}}, {{11, 12, 8, 2}}, {{8, 12, 10, 0}}}};
7858
7859 /* Boundary-faces 2D:
7860 * After converting, each of the 4 quadrilateral faces is defined by faces
7861 * of 2 different triangles, i.e., lines. Note that lines are defined by 2
7862 * vertices.
7863 */
7865 vertex_ids_for_boundary_faces_2d = {{{{{{0, 4}}, {{4, 2}}}},
7866 {{{{1, 5}}, {{5, 3}}}},
7867 {{{{0, 6}}, {{6, 1}}}},
7868 {{{{2, 7}}, {{7, 3}}}}}};
7869
7870 /* Boundary-faces 3D:
7871 * After converting, each of the 6 hexahedron faces corresponds to faces of
7872 * 4 different tetrahedron faces, i.e., triangles. Note that a triangle is
7873 * defined by 3 vertices.
7874 */
7876 vertex_ids_for_boundary_faces_3d = {
7877 {{{{{0, 4, 8}}, {{4, 8, 6}}, {{8, 6, 2}}, {{0, 2, 8}}}},
7878 {{{{1, 3, 9}}, {{3, 9, 7}}, {{9, 7, 5}}, {{1, 9, 5}}}},
7879 {{{{0, 1, 10}}, {{1, 10, 5}}, {{10, 5, 4}}, {{0, 10, 4}}}},
7880 {{{{2, 3, 11}}, {{3, 11, 7}}, {{11, 7, 6}}, {{2, 11, 6}}}},
7881 {{{{0, 1, 12}}, {{1, 12, 3}}, {{12, 3, 2}}, {{0, 12, 2}}}},
7882 {{{{4, 5, 13}}, {{5, 13, 7}}, {{13, 7, 6}}, {{4, 13, 6}}}}}};
7883
7884 /* Inner-faces 2D:
7885 * The converted triangulation based on simplices has 8 faces that do not
7886 * form the boundary, i.e. inner-faces, each defined by 2 vertices.
7887 */
7888 static const ndarray<unsigned int, 8, 2> vertex_ids_for_inner_faces_2d = {
7889 {{{6, 4}},
7890 {{6, 8}},
7891 {{6, 5}},
7892 {{4, 8}},
7893 {{8, 5}},
7894 {{7, 4}},
7895 {{7, 8}},
7896 {{7, 5}}}};
7897
7898 /* Inner-faces 3D:
7899 * The converted triangulation based on simplices has 72 faces that do not
7900 * form the boundary, i.e. inner-faces, each defined by 3 vertices.
7901 */
7902 static const ndarray<unsigned int, 72, 3> vertex_ids_for_inner_faces_3d = {
7903 {{{0, 12, 10}}, {{12, 1, 10}}, {{12, 1, 9}}, {{12, 3, 9}},
7904 {{12, 2, 11}}, {{12, 3, 11}}, {{12, 0, 8}}, {{12, 2, 8}},
7905 {{9, 13, 5}}, {{13, 7, 9}}, {{11, 7, 13}}, {{11, 6, 13}},
7906 {{4, 8, 13}}, {{6, 8, 13}}, {{4, 13, 10}}, {{13, 5, 10}},
7907 {{10, 9, 5}}, {{10, 9, 1}}, {{11, 9, 7}}, {{11, 9, 3}},
7908 {{8, 11, 2}}, {{8, 11, 6}}, {{8, 10, 0}}, {{8, 10, 4}},
7909 {{12, 3, 9}}, {{12, 9, 11}}, {{12, 3, 11}}, {{3, 9, 11}},
7910 {{2, 12, 8}}, {{2, 12, 11}}, {{2, 11, 8}}, {{8, 12, 11}},
7911 {{0, 12, 10}}, {{0, 12, 8}}, {{0, 8, 10}}, {{8, 10, 12}},
7912 {{12, 1, 10}}, {{12, 1, 9}}, {{1, 10, 9}}, {{10, 9, 12}},
7913 {{10, 8, 4}}, {{10, 8, 13}}, {{4, 13, 8}}, {{4, 13, 10}},
7914 {{10, 9, 13}}, {{10, 9, 5}}, {{13, 5, 10}}, {{13, 5, 9}},
7915 {{13, 7, 9}}, {{13, 7, 11}}, {{9, 11, 13}}, {{9, 11, 7}},
7916 {{8, 11, 13}}, {{8, 11, 6}}, {{6, 13, 8}}, {{6, 13, 11}},
7917 {{12, 13, 10}}, {{12, 13, 8}}, {{8, 10, 13}}, {{8, 10, 12}},
7918 {{12, 13, 10}}, {{12, 13, 9}}, {{10, 9, 13}}, {{10, 9, 12}},
7919 {{12, 13, 9}}, {{12, 13, 11}}, {{9, 11, 13}}, {{9, 11, 12}},
7920 {{12, 13, 11}}, {{12, 13, 8}}, {{8, 11, 13}}, {{8, 11, 12}}}};
7921
7922 /* Inner-edges 3D:
7923 * The converted triangulation based on simplices has 60 edges that do not
7924 * coincide with the boundary, i.e. inner-edges, each defined by 2 vertices.
7925 */
7926 static const ndarray<unsigned int, 60, 2> vertex_ids_for_inner_edges_3d = {
7927 {{{12, 10}}, {{12, 9}}, {{12, 11}}, {{12, 8}}, {{9, 13}}, {{11, 13}},
7928 {{8, 13}}, {{10, 13}}, {{10, 9}}, {{9, 11}}, {{11, 8}}, {{8, 10}},
7929 {{12, 9}}, {{12, 11}}, {{11, 9}}, {{12, 8}}, {{12, 11}}, {{11, 8}},
7930 {{12, 8}}, {{12, 10}}, {{10, 8}}, {{12, 10}}, {{12, 9}}, {{9, 10}},
7931 {{13, 10}}, {{13, 8}}, {{8, 10}}, {{13, 10}}, {{13, 9}}, {{9, 10}},
7932 {{13, 11}}, {{13, 9}}, {{11, 9}}, {{13, 11}}, {{13, 8}}, {{11, 8}},
7933 {{12, 13}}, {{8, 10}}, {{8, 13}}, {{10, 13}}, {{8, 12}}, {{10, 12}},
7934 {{12, 13}}, {{10, 9}}, {{10, 13}}, {{9, 13}}, {{10, 12}}, {{9, 12}},
7935 {{12, 13}}, {{9, 11}}, {{9, 13}}, {{11, 13}}, {{9, 12}}, {{11, 12}},
7936 {{12, 13}}, {{11, 8}}, {{11, 13}}, {{8, 13}}, {{11, 12}}, {{8, 12}}}};
7937
7938 /* Boundary-edges 3D:
7939 * For each of the 6 boundary-faces of the hexahedron, there are 8 edges (of
7940 * different tetrahedrons) that coincide with the boundary, i.e.
7941 * boundary-edges. Each boundary-edge is defined by 2 vertices. 4 of these
7942 * edges are new (they are placed in the middle of a presently existing
7943 * face); the other 4 coincide with edges present in the hexahedral
7944 * triangulation. The new 4 edges inherit the manifold id of the relevant
7945 * face, but the other 4 need to be copied from the input and thus do not
7946 * require a lookup table.
7947 */
7949 vertex_ids_for_new_boundary_edges_3d = {
7950 {{{{{4, 8}}, {{6, 8}}, {{0, 8}}, {{2, 8}}}},
7951 {{{{5, 9}}, {{7, 9}}, {{1, 9}}, {{3, 9}}}},
7952 {{{{4, 10}}, {{5, 10}}, {{0, 10}}, {{1, 10}}}},
7953 {{{{6, 11}}, {{7, 11}}, {{2, 11}}, {{3, 11}}}},
7954 {{{{2, 12}}, {{3, 12}}, {{0, 12}}, {{1, 12}}}},
7955 {{{{6, 13}}, {{7, 13}}, {{4, 13}}, {{5, 13}}}}}};
7956
7957 std::vector<Point<spacedim>> vertices;
7958 std::vector<CellData<dim>> cells;
7959 SubCellData subcell_data;
7960
7961 // store for each vertex and face the assigned index so that we only
7962 // assign them a value once
7963 std::vector<unsigned int> old_to_new_vertex_indices(
7965 std::vector<unsigned int> face_to_new_vertex_indices(
7967
7968 // We first have to create all of the new vertices. To do this, we loop over
7969 // all cells and on each cell
7970 // (i) copy the existing vertex locations (and record their new indices in
7971 // the 'old_to_new_vertex_indices' vector),
7972 // (ii) create new midpoint vertex locations for each face (and record their
7973 // new indices in the 'face_to_new_vertex_indices' vector),
7974 // (iii) create new midpoint vertex locations for each cell (dim = 2 only)
7975 for (const auto &cell : ref_tria)
7976 {
7977 // temporary array storing the global indices of each cell entity in the
7978 // sequence: vertices, edges/faces, cell
7979 std::array<unsigned int, dim == 2 ? 9 : 14> local_vertex_indices;
7980
7981 // (i) copy the existing vertex locations
7982 for (const auto v : cell.vertex_indices())
7983 {
7984 const auto v_global = cell.vertex_index(v);
7985
7986 if (old_to_new_vertex_indices[v_global] ==
7988 {
7989 old_to_new_vertex_indices[v_global] = vertices.size();
7990 vertices.push_back(cell.vertex(v));
7991 }
7992
7993 AssertIndexRange(v, local_vertex_indices.size());
7994 local_vertex_indices[v] = old_to_new_vertex_indices[v_global];
7995 }
7996
7997 // (ii) create new midpoint vertex locations for each face
7998 for (const auto f : cell.face_indices())
7999 {
8000 const auto f_global = cell.face_index(f);
8001
8002 if (face_to_new_vertex_indices[f_global] ==
8004 {
8005 face_to_new_vertex_indices[f_global] = vertices.size();
8006 vertices.push_back(
8007 cell.face(f)->center(/*respect_manifold*/ true));
8008 }
8009
8010 AssertIndexRange(cell.n_vertices() + f,
8011 local_vertex_indices.size());
8012 local_vertex_indices[cell.n_vertices() + f] =
8013 face_to_new_vertex_indices[f_global];
8014 }
8015
8016 // (iii) create new midpoint vertex locations for each cell
8017 if (dim == 2)
8018 {
8019 AssertIndexRange(cell.n_vertices() + cell.n_faces(),
8020 local_vertex_indices.size());
8021 local_vertex_indices[cell.n_vertices() + cell.n_faces()] =
8022 vertices.size();
8023 vertices.push_back(cell.center(/*respect_manifold*/ true));
8024 }
8025
8026 // helper function for creating cells and subcells
8027 const auto add_cell = [&](const unsigned int struct_dim,
8028 const auto & index_vertices,
8029 const unsigned int material_or_boundary_id,
8030 const unsigned int manifold_id = 0) {
8031 // sub-cell data only has to be stored if the information differs
8032 // from the default
8033 if (struct_dim < dim &&
8034 (material_or_boundary_id == numbers::internal_face_boundary_id &&
8035 manifold_id == numbers::flat_manifold_id))
8036 return;
8037
8038 if (struct_dim == dim) // cells
8039 {
8040 if (dim == 2)
8041 {
8042 AssertDimension(index_vertices.size(), 3);
8043 }
8044 else if (dim == 3)
8045 {
8046 AssertDimension(index_vertices.size(), 4);
8047 }
8048
8049 CellData<dim> cell_data(index_vertices.size());
8050 for (unsigned int i = 0; i < index_vertices.size(); ++i)
8051 {
8052 AssertIndexRange(index_vertices[i],
8053 local_vertex_indices.size());
8054 cell_data.vertices[i] =
8055 local_vertex_indices[index_vertices[i]];
8056 cell_data.material_id =
8057 material_or_boundary_id; // inherit material id
8058 cell_data.manifold_id =
8059 manifold_id; // inherit cell-manifold id
8060 }
8061 cells.push_back(cell_data);
8062 }
8063 else if (dim == 2 && struct_dim == 1) // an edge of a simplex
8064 {
8065 Assert(index_vertices.size() == 2, ExcInternalError());
8066 CellData<1> boundary_line(2);
8067 boundary_line.boundary_id = material_or_boundary_id;
8068 boundary_line.manifold_id = manifold_id;
8069 for (unsigned int i = 0; i < index_vertices.size(); ++i)
8070 {
8071 AssertIndexRange(index_vertices[i],
8072 local_vertex_indices.size());
8073 boundary_line.vertices[i] =
8074 local_vertex_indices[index_vertices[i]];
8075 }
8076 subcell_data.boundary_lines.push_back(boundary_line);
8077 }
8078 else if (dim == 3 && struct_dim == 2) // a face of a tetrahedron
8079 {
8080 Assert(index_vertices.size() == 3, ExcInternalError());
8081 CellData<2> boundary_quad(3);
8082 boundary_quad.material_id = material_or_boundary_id;
8083 boundary_quad.manifold_id = manifold_id;
8084 for (unsigned int i = 0; i < index_vertices.size(); ++i)
8085 {
8086 AssertIndexRange(index_vertices[i],
8087 local_vertex_indices.size());
8088 boundary_quad.vertices[i] =
8089 local_vertex_indices[index_vertices[i]];
8090 }
8091 subcell_data.boundary_quads.push_back(boundary_quad);
8092 }
8093 else if (dim == 3 && struct_dim == 1) // an edge of a tetrahedron
8094 {
8095 Assert(index_vertices.size() == 2, ExcInternalError());
8096 CellData<1> boundary_line(2);
8097 boundary_line.boundary_id = material_or_boundary_id;
8098 boundary_line.manifold_id = manifold_id;
8099 for (unsigned int i = 0; i < index_vertices.size(); ++i)
8100 {
8101 AssertIndexRange(index_vertices[i],
8102 local_vertex_indices.size());
8103 boundary_line.vertices[i] =
8104 local_vertex_indices[index_vertices[i]];
8105 }
8106 subcell_data.boundary_lines.push_back(boundary_line);
8107 }
8108 else
8109 {
8110 Assert(false, ExcNotImplemented());
8111 }
8112 };
8113
8114 const auto material_id_cell = cell.material_id();
8115
8116 // create cells one by one
8117 if (dim == 2)
8118 {
8119 // get cell-manifold id from current quad cell
8120 const auto manifold_id_cell = cell.manifold_id();
8121 // inherit cell manifold
8122 for (const auto &cell_vertices : table_2D_cell)
8123 add_cell(dim, cell_vertices, material_id_cell, manifold_id_cell);
8124
8125 // inherit inner manifold (faces)
8126 for (const auto &face_vertices : vertex_ids_for_inner_faces_2d)
8127 // set inner tri-faces according to cell-manifold of quad
8128 // element, set invalid b_id, since we do not want to modify
8129 // b_id inside
8130 add_cell(1,
8131 face_vertices,
8133 manifold_id_cell);
8134 }
8135 else if (dim == 3)
8136 {
8137 // get cell-manifold id from current quad cell
8138 const auto manifold_id_cell = cell.manifold_id();
8139 // inherit cell manifold
8140 for (const auto &cell_vertices : vertex_ids_for_cells_3d)
8141 add_cell(dim, cell_vertices, material_id_cell, manifold_id_cell);
8142
8143 // set manifold of inner FACES of tets according to
8144 // hex-cell-manifold
8145 for (const auto &face_vertices : vertex_ids_for_inner_faces_3d)
8146 add_cell(2,
8147 face_vertices,
8149 manifold_id_cell);
8150
8151 // set manifold of inner EDGES of tets according to
8152 // hex-cell-manifold
8153 for (const auto &edge_vertices : vertex_ids_for_inner_edges_3d)
8154 add_cell(1,
8155 edge_vertices,
8157 manifold_id_cell);
8158 }
8159 else
8160 Assert(false, ExcNotImplemented());
8161
8162 // Set up sub-cell data.
8163 for (const auto f : cell.face_indices())
8164 {
8165 const auto bid = cell.face(f)->boundary_id();
8166 const auto mid = cell.face(f)->manifold_id();
8167
8168 // process boundary-faces: set boundary and manifold ids
8169 if (dim == 2) // 2D boundary-faces
8170 {
8171 for (const auto &face_vertices :
8172 vertex_ids_for_boundary_faces_2d[f])
8173 add_cell(1, face_vertices, bid, mid);
8174 }
8175 else if (dim == 3) // 3D boundary-faces
8176 {
8177 // set manifold ids of tet-boundary-faces according to
8178 // hex-boundary-faces
8179 for (const auto &face_vertices :
8180 vertex_ids_for_boundary_faces_3d[f])
8181 add_cell(2, face_vertices, bid, mid);
8182 // set manifold ids of new tet-boundary-edges according to
8183 // hex-boundary-faces
8184 for (const auto &edge_vertices :
8185 vertex_ids_for_new_boundary_edges_3d[f])
8186 add_cell(1, edge_vertices, bid, mid);
8187 }
8188 else
8189 Assert(false, ExcNotImplemented());
8190 }
8191
8192 // set manifold ids of edges that were already present in the
8193 // triangulation.
8194 if (dim == 3)
8195 {
8196 for (const auto e : cell.line_indices())
8197 {
8198 auto edge = cell.line(e);
8199 // Rather than use add_cell(), which does additional index
8200 // translation, just add edges directly into subcell_data since
8201 // we already know the correct global vertex indices.
8202 CellData<1> edge_data;
8203 edge_data.vertices[0] =
8204 old_to_new_vertex_indices[edge->vertex_index(0)];
8205 edge_data.vertices[1] =
8206 old_to_new_vertex_indices[edge->vertex_index(1)];
8207 edge_data.boundary_id = edge->boundary_id();
8208 edge_data.manifold_id = edge->manifold_id();
8209
8210 subcell_data.boundary_lines.push_back(std::move(edge_data));
8211 }
8212 }
8213 }
8214
8215 out_tria.create_triangulation(vertices, cells, subcell_data);
8216 }
8217
8218
8219
8220 template <int spacedim>
8221 void
8223 Triangulation<1, spacedim> & out_tria)
8224 {
8225 out_tria.copy_triangulation(in_tria);
8226 return;
8227 }
8228
8229
8230
8231 template <template <int, int> class MeshType, int dim, int spacedim>
8232# ifndef _MSC_VER
8233 std::map<typename MeshType<dim - 1, spacedim>::cell_iterator,
8234 typename MeshType<dim, spacedim>::face_iterator>
8235# else
8236 typename ExtractBoundaryMesh<MeshType, dim, spacedim>::return_type
8237# endif
8238 extract_boundary_mesh(const MeshType<dim, spacedim> & volume_mesh,
8239 MeshType<dim - 1, spacedim> & surface_mesh,
8240 const std::set<types::boundary_id> &boundary_ids)
8241 {
8242 Assert((dynamic_cast<
8244 &volume_mesh.get_triangulation()) == nullptr),
8246
8247 // This function works using the following assumption:
8248 // Triangulation::create_triangulation(...) will create cells that
8249 // preserve the order of cells passed in using the CellData argument;
8250 // also, that it will not reorder the vertices.
8251
8252 // dimension of the boundary mesh
8253 const unsigned int boundary_dim = dim - 1;
8254
8255 // temporary map for level==0
8256 // iterator to face is stored along with face number
8257 // (this is required by the algorithm to adjust the normals of the
8258 // cells of the boundary mesh)
8259 std::vector<
8260 std::pair<typename MeshType<dim, spacedim>::face_iterator, unsigned int>>
8261 temporary_mapping_level0;
8262
8263 // vector indicating whether a vertex of the volume mesh has
8264 // already been visited (necessary to avoid duplicate vertices in
8265 // boundary mesh)
8266 std::vector<bool> touched(volume_mesh.get_triangulation().n_vertices(),
8267 false);
8268
8269 // data structures required for creation of boundary mesh
8270 std::vector<CellData<boundary_dim>> cells;
8271 SubCellData subcell_data;
8272 std::vector<Point<spacedim>> vertices;
8273
8274 // volume vertex indices to surf ones
8275 std::map<unsigned int, unsigned int> map_vert_index;
8276
8277 // define swapping of vertices to get proper normal orientation of boundary
8278 // mesh;
8279 // the entry (i,j) of swap_matrix stores the index of the vertex of
8280 // the boundary cell corresponding to the j-th vertex on the i-th face
8281 // of the underlying volume cell
8282 // if e.g. face 3 of a volume cell is considered and vertices 1 and 2 of the
8283 // corresponding boundary cell are swapped to get
8284 // proper normal orientation, swap_matrix[3]=( 0, 2, 1, 3 )
8285 Table<2, unsigned int> swap_matrix(
8288 for (unsigned int i1 = 0; i1 < GeometryInfo<spacedim>::faces_per_cell; ++i1)
8289 {
8290 for (unsigned int i2 = 0; i2 < GeometryInfo<dim - 1>::vertices_per_cell;
8291 i2++)
8292 swap_matrix[i1][i2] = i2;
8293 }
8294 // vertex swapping such that normals on the surface mesh point out of the
8295 // underlying volume
8296 if (dim == 3)
8297 {
8298 std::swap(swap_matrix[0][1], swap_matrix[0][2]);
8299 std::swap(swap_matrix[2][1], swap_matrix[2][2]);
8300 std::swap(swap_matrix[4][1], swap_matrix[4][2]);
8301 }
8302 else if (dim == 2)
8303 {
8304 std::swap(swap_matrix[1][0], swap_matrix[1][1]);
8305 std::swap(swap_matrix[2][0], swap_matrix[2][1]);
8306 }
8307
8308 // Create boundary mesh and mapping
8309 // from only level(0) cells of volume_mesh
8310 for (typename MeshType<dim, spacedim>::cell_iterator cell =
8311 volume_mesh.begin(0);
8312 cell != volume_mesh.end(0);
8313 ++cell)
8314 for (unsigned int i : GeometryInfo<dim>::face_indices())
8315 {
8316 const typename MeshType<dim, spacedim>::face_iterator face =
8317 cell->face(i);
8318
8319 if (face->at_boundary() &&
8320 (boundary_ids.empty() ||
8321 (boundary_ids.find(face->boundary_id()) != boundary_ids.end())))
8322 {
8324
8325 for (const unsigned int j :
8327 {
8328 const unsigned int v_index = face->vertex_index(j);
8329
8330 if (!touched[v_index])
8331 {
8332 vertices.push_back(face->vertex(j));
8333 map_vert_index[v_index] = vertices.size() - 1;
8334 touched[v_index] = true;
8335 }
8336
8337 c_data.vertices[swap_matrix[i][j]] = map_vert_index[v_index];
8338 }
8339 c_data.material_id =
8340 static_cast<types::material_id>(face->boundary_id());
8341 c_data.manifold_id = face->manifold_id();
8342
8343
8344 // in 3d, we need to make sure we copy the manifold
8345 // indicators from the edges of the volume mesh to the
8346 // edges of the surface mesh
8347 //
8348 // we set default boundary ids for boundary lines
8349 // and numbers::internal_face_boundary_id for internal lines
8350 if (dim == 3)
8351 for (unsigned int e = 0; e < 4; ++e)
8352 {
8353 // see if we already saw this edge from a
8354 // neighboring face, either in this or the reverse
8355 // orientation. if so, skip it.
8356 {
8357 bool edge_found = false;
8358 for (auto &boundary_line : subcell_data.boundary_lines)
8359 if (((boundary_line.vertices[0] ==
8360 map_vert_index[face->line(e)->vertex_index(0)]) &&
8361 (boundary_line.vertices[1] ==
8362 map_vert_index[face->line(e)->vertex_index(
8363 1)])) ||
8364 ((boundary_line.vertices[0] ==
8365 map_vert_index[face->line(e)->vertex_index(1)]) &&
8366 (boundary_line.vertices[1] ==
8367 map_vert_index[face->line(e)->vertex_index(0)])))
8368 {
8369 boundary_line.boundary_id =
8371 edge_found = true;
8372 break;
8373 }
8374 if (edge_found == true)
8375 // try next edge of current face
8376 continue;
8377 }
8378
8379 CellData<1> edge;
8380 edge.vertices[0] =
8381 map_vert_index[face->line(e)->vertex_index(0)];
8382 edge.vertices[1] =
8383 map_vert_index[face->line(e)->vertex_index(1)];
8384 edge.boundary_id = 0;
8385 edge.manifold_id = face->line(e)->manifold_id();
8386
8387 subcell_data.boundary_lines.push_back(edge);
8388 }
8389
8390 cells.push_back(c_data);
8391 temporary_mapping_level0.push_back(std::make_pair(face, i));
8392 }
8393 }
8394
8395 // create level 0 surface triangulation
8396 Assert(cells.size() > 0, ExcMessage("No boundary faces selected"));
8397 const_cast<Triangulation<dim - 1, spacedim> &>(
8398 surface_mesh.get_triangulation())
8399 .create_triangulation(vertices, cells, subcell_data);
8400
8401 // in 2d: set default boundary ids for "boundary vertices"
8402 if (dim == 2)
8403 {
8404 for (const auto &cell : surface_mesh.active_cell_iterators())
8405 for (unsigned int vertex = 0; vertex < 2; ++vertex)
8406 if (cell->face(vertex)->at_boundary())
8407 cell->face(vertex)->set_boundary_id(0);
8408 }
8409
8410 // Make mapping for level 0
8411
8412 // temporary map between cells on the boundary and corresponding faces of
8413 // domain mesh (each face is characterized by an iterator to the face and
8414 // the face number within the underlying cell)
8415 std::vector<std::pair<
8416 const typename MeshType<dim - 1, spacedim>::cell_iterator,
8417 std::pair<typename MeshType<dim, spacedim>::face_iterator, unsigned int>>>
8418 temporary_map_boundary_cell_face;
8419 for (const auto &cell : surface_mesh.active_cell_iterators())
8420 temporary_map_boundary_cell_face.push_back(
8421 std::make_pair(cell, temporary_mapping_level0.at(cell->index())));
8422
8423
8424 // refine the boundary mesh according to the refinement of the underlying
8425 // volume mesh,
8426 // algorithm:
8427 // (1) check which cells on refinement level i need to be refined
8428 // (2) do refinement (yields cells on level i+1)
8429 // (3) repeat for the next level (i+1->i) until refinement is completed
8430
8431 // stores the index into temporary_map_boundary_cell_face at which
8432 // presently deepest refinement level of boundary mesh begins
8433 unsigned int index_cells_deepest_level = 0;
8434 do
8435 {
8436 bool changed = false;
8437
8438 // vector storing cells which have been marked for
8439 // refinement
8440 std::vector<unsigned int> cells_refined;
8441
8442 // loop over cells of presently deepest level of boundary triangulation
8443 for (unsigned int cell_n = index_cells_deepest_level;
8444 cell_n < temporary_map_boundary_cell_face.size();
8445 cell_n++)
8446 {
8447 // mark boundary cell for refinement if underlying volume face has
8448 // children
8449 if (temporary_map_boundary_cell_face[cell_n]
8450 .second.first->has_children())
8451 {
8452 // algorithm only works for
8453 // isotropic refinement!
8454 Assert(temporary_map_boundary_cell_face[cell_n]
8455 .second.first->refinement_case() ==
8458 temporary_map_boundary_cell_face[cell_n]
8459 .first->set_refine_flag();
8460 cells_refined.push_back(cell_n);
8461 changed = true;
8462 }
8463 }
8464
8465 // if cells have been marked for refinement (i.e., presently deepest
8466 // level is not the deepest level of the volume mesh)
8467 if (changed)
8468 {
8469 // do actual refinement
8470 const_cast<Triangulation<dim - 1, spacedim> &>(
8471 surface_mesh.get_triangulation())
8472 .execute_coarsening_and_refinement();
8473
8474 // add new level of cells to temporary_map_boundary_cell_face
8475 index_cells_deepest_level = temporary_map_boundary_cell_face.size();
8476 for (const auto &refined_cell_n : cells_refined)
8477 {
8478 const typename MeshType<dim - 1, spacedim>::cell_iterator
8479 refined_cell =
8480 temporary_map_boundary_cell_face[refined_cell_n].first;
8481 const typename MeshType<dim,
8482 spacedim>::face_iterator refined_face =
8483 temporary_map_boundary_cell_face[refined_cell_n].second.first;
8484 const unsigned int refined_face_number =
8485 temporary_map_boundary_cell_face[refined_cell_n]
8486 .second.second;
8487 for (unsigned int child_n = 0;
8488 child_n < refined_cell->n_children();
8489 ++child_n)
8490 // at this point, the swapping of vertices done earlier must
8491 // be taken into account to get the right association between
8492 // volume faces and boundary cells!
8493 temporary_map_boundary_cell_face.push_back(
8494 std::make_pair(refined_cell->child(
8495 swap_matrix[refined_face_number][child_n]),
8496 std::make_pair(refined_face->child(child_n),
8497 refined_face_number)));
8498 }
8499 }
8500 // we are at the deepest level of refinement of the volume mesh
8501 else
8502 break;
8503 }
8504 while (true);
8505
8506 // generate the final mapping from the temporary mapping
8507 std::map<typename MeshType<dim - 1, spacedim>::cell_iterator,
8508 typename MeshType<dim, spacedim>::face_iterator>
8509 surface_to_volume_mapping;
8510 for (unsigned int i = 0; i < temporary_map_boundary_cell_face.size(); ++i)
8511 surface_to_volume_mapping[temporary_map_boundary_cell_face[i].first] =
8512 temporary_map_boundary_cell_face[i].second.first;
8513
8514 return surface_to_volume_mapping;
8515 }
8516
8517
8518
8519 template <int dim, int spacedim>
8520 void
8521 subdivided_hyper_rectangle_with_simplices(
8523 const std::vector<unsigned int> &repetitions,
8524 const Point<dim> & p1,
8525 const Point<dim> & p2,
8526 const bool colorize)
8527 {
8528 AssertDimension(dim, spacedim);
8529
8531
8532 std::vector<Point<spacedim>> vertices;
8533 std::vector<CellData<dim>> cells;
8534
8535 if (dim == 2)
8536 {
8537 // determine cell sizes
8538 const Point<dim> dx((p2[0] - p1[0]) / repetitions[0],
8539 (p2[1] - p1[1]) / repetitions[1]);
8540
8541 // create vertices
8542 for (unsigned int j = 0; j <= repetitions[1]; ++j)
8543 for (unsigned int i = 0; i <= repetitions[0]; ++i)
8544 vertices.push_back(
8545 Point<spacedim>(p1[0] + dx[0] * i, p1[1] + dx[1] * j));
8546
8547 // create cells
8548 for (unsigned int j = 0; j < repetitions[1]; ++j)
8549 for (unsigned int i = 0; i < repetitions[0]; ++i)
8550 {
8551 // create reference QUAD cell
8552 std::array<unsigned int, 4> quad{{
8553 (j + 0) * (repetitions[0] + 1) + i + 0, //
8554 (j + 0) * (repetitions[0] + 1) + i + 1, //
8555 (j + 1) * (repetitions[0] + 1) + i + 0, //
8556 (j + 1) * (repetitions[0] + 1) + i + 1 //
8557 }}; //
8558
8559 // TRI cell 0
8560 {
8561 CellData<dim> tri;
8562 tri.vertices = {quad[0], quad[1], quad[2]};
8563 cells.push_back(tri);
8564 }
8565
8566 // TRI cell 1
8567 {
8568 CellData<dim> tri;
8569 tri.vertices = {quad[3], quad[2], quad[1]};
8570 cells.push_back(tri);
8571 }
8572 }
8573 }
8574 else if (dim == 3)
8575 {
8576 // determine cell sizes
8577 const Point<dim> dx((p2[0] - p1[0]) / repetitions[0],
8578 (p2[1] - p1[1]) / repetitions[1],
8579 (p2[2] - p1[2]) / repetitions[2]);
8580
8581 // create vertices
8582 for (unsigned int k = 0; k <= repetitions[2]; ++k)
8583 for (unsigned int j = 0; j <= repetitions[1]; ++j)
8584 for (unsigned int i = 0; i <= repetitions[0]; ++i)
8585 vertices.push_back(Point<spacedim>(p1[0] + dx[0] * i,
8586 p1[1] + dx[1] * j,
8587 p1[2] + dx[2] * k));
8588
8589 // create cells
8590 for (unsigned int k = 0; k < repetitions[2]; ++k)
8591 for (unsigned int j = 0; j < repetitions[1]; ++j)
8592 for (unsigned int i = 0; i < repetitions[0]; ++i)
8593 {
8594 // create reference HEX cell
8595 std::array<unsigned int, 8> quad{
8596 {(k + 0) * (repetitions[0] + 1) * (repetitions[1] + 1) +
8597 (j + 0) * (repetitions[0] + 1) + i + 0,
8598 (k + 0) * (repetitions[0] + 1) * (repetitions[1] + 1) +
8599 (j + 0) * (repetitions[0] + 1) + i + 1,
8600 (k + 0) * (repetitions[0] + 1) * (repetitions[1] + 1) +
8601 (j + 1) * (repetitions[0] + 1) + i + 0,
8602 (k + 0) * (repetitions[0] + 1) * (repetitions[1] + 1) +
8603 (j + 1) * (repetitions[0] + 1) + i + 1,
8604 (k + 1) * (repetitions[0] + 1) * (repetitions[1] + 1) +
8605 (j + 0) * (repetitions[0] + 1) + i + 0,
8606 (k + 1) * (repetitions[0] + 1) * (repetitions[1] + 1) +
8607 (j + 0) * (repetitions[0] + 1) + i + 1,
8608 (k + 1) * (repetitions[0] + 1) * (repetitions[1] + 1) +
8609 (j + 1) * (repetitions[0] + 1) + i + 0,
8610 (k + 1) * (repetitions[0] + 1) * (repetitions[1] + 1) +
8611 (j + 1) * (repetitions[0] + 1) + i + 1}};
8612
8613 // TET cell 0
8614 {
8615 CellData<dim> cell;
8616 if (((i % 2) + (j % 2) + (k % 2)) % 2 == 0)
8617 cell.vertices = {{quad[0], quad[1], quad[2], quad[4]}};
8618 else
8619 cell.vertices = {{quad[0], quad[1], quad[3], quad[5]}};
8620
8621 cells.push_back(cell);
8622 }
8623
8624 // TET cell 1
8625 {
8626 CellData<dim> cell;
8627 if (((i % 2) + (j % 2) + (k % 2)) % 2 == 0)
8628 cell.vertices = {{quad[2], quad[1], quad[3], quad[7]}};
8629 else
8630 cell.vertices = {{quad[0], quad[3], quad[2], quad[6]}};
8631 cells.push_back(cell);
8632 }
8633
8634 // TET cell 2
8635 {
8636 CellData<dim> cell;
8637 if (((i % 2) + (j % 2) + (k % 2)) % 2 == 0)
8638 cell.vertices = {{quad[1], quad[4], quad[5], quad[7]}};
8639 else
8640 cell.vertices = {{quad[0], quad[4], quad[5], quad[6]}};
8641 cells.push_back(cell);
8642 }
8643
8644 // TET cell 3
8645 {
8646 CellData<dim> cell;
8647 if (((i % 2) + (j % 2) + (k % 2)) % 2 == 0)
8648 cell.vertices = {{quad[2], quad[4], quad[7], quad[6]}};
8649 else
8650 cell.vertices = {{quad[3], quad[5], quad[7], quad[6]}};
8651 cells.push_back(cell);
8652 }
8653
8654 // TET cell 4
8655 {
8656 CellData<dim> cell;
8657 if (((i % 2) + (j % 2) + (k % 2)) % 2 == 0)
8658 cell.vertices = {{quad[1], quad[2], quad[4], quad[7]}};
8659 else
8660 cell.vertices = {{quad[0], quad[3], quad[6], quad[5]}};
8661 cells.push_back(cell);
8662 }
8663 }
8664 }
8665 else
8666 {
8668 }
8669
8670 // actually create triangulation
8672 }
8673
8674
8675
8676 template <int dim, int spacedim>
8677 void
8678 subdivided_hyper_cube_with_simplices(Triangulation<dim, spacedim> &tria,
8679 const unsigned int repetitions,
8680 const double p1,
8681 const double p2,
8682 const bool colorize)
8683 {
8684 if (dim == 2)
8685 {
8686 subdivided_hyper_rectangle_with_simplices(
8687 tria, {{repetitions, repetitions}}, {p1, p1}, {p2, p2}, colorize);
8688 }
8689 else if (dim == 3)
8690 {
8691 subdivided_hyper_rectangle_with_simplices(
8692 tria,
8693 {{repetitions, repetitions, repetitions}},
8694 {p1, p1, p1},
8695 {p2, p2, p2},
8696 colorize);
8697 }
8698 else
8699 {
8701 }
8702 }
8703} // namespace GridGenerator
8704
8705// explicit instantiations
8706# include "grid_generator.inst"
8707
8708#endif // DOXYGEN
8709
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)
Definition: point.h:111
numbers::NumberTraits< Number >::real_type distance(const Point< dim, Number > &p) const
const Point< spacedim > center
Definition: manifold_lib.h:131
Definition: tensor.h:503
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
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:442
#define DEAL_II_NAMESPACE_CLOSE
Definition: config.h:443
Point< 3 > center
Point< 3 > vertices[4]
DerivativeForm< 1, spacedim, dim, Number > transpose(const DerivativeForm< 1, dim, spacedim, Number > &DF)
Point< 2 > second
Definition: grid_out.cc:4604
bool colorize
Definition: grid_out.cc:4605
Point< 2 > first
Definition: grid_out.cc:4603
unsigned int cell_index
Definition: grid_tools.cc:1129
IteratorRange< active_face_iterator > active_face_iterators() const
IteratorRange< active_cell_iterator > active_cell_iterators() const
IteratorRange< cell_iterator > cell_iterators() const
static ::ExceptionBase & ExcNotImplemented()
#define Assert(cond, exc)
Definition: exceptions.h:1473
#define AssertDimension(dim1, dim2)
Definition: exceptions.h:1667
static ::ExceptionBase & ExcLowerRange(int arg1, int arg2)
#define AssertIndexRange(index, range)
Definition: exceptions.h:1732
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)
Definition: exceptions.h:1583
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)
Definition: grid_tools.cc:4925
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)
const Mapping< dim, spacedim > & get_default_linear_mapping(const Triangulation< dim, spacedim > &triangulation)
Definition: mapping.cc:260
void interpolate(const DoFHandler< dim, spacedim > &dof1, const InVector &u1, const DoFHandler< dim, spacedim > &dof2, OutVector &u2)
void create_triangulation(Triangulation< dim, dim > &tria, const AdditionalData &additional_data=AdditionalData())
void parallelepiped(Triangulation< dim > &tria, const Point< dim >(&corners)[dim], const bool colorize=false)
void convert_hypercube_to_simplex_mesh(const Triangulation< 1, spacedim > &in_tria, Triangulation< 1, spacedim > &out_tria)
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_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_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 hyper_ball(Triangulation< dim > &tria, const Point< dim > &center=Point< dim >(), const double radius=1., const bool attach_spherical_manifold_on_boundary_cells=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 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)
void cheese(Triangulation< dim, spacedim > &tria, const std::vector< unsigned int > &holes)
Rectangular domain with rectangular pattern of holes.
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)
void create_union_triangulation(const Triangulation< dim, spacedim > &triangulation_1, const Triangulation< dim, spacedim > &triangulation_2, Triangulation< dim, spacedim > &result)
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 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 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 hyper_shell(Triangulation< dim > &tria, const Point< dim > &center, const double inner_radius, const double outer_radius, const unsigned int n_cells=0, 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 hyper_cube_with_cylindrical_hole(Triangulation< dim > &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 truncated_cone(Triangulation< dim > &tria, const double radius_0=1.0, const double radius_1=0.5, const double half_length=1.0)
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 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)
Definition: grid_tools.cc:636
void scale(const double scaling_factor, Triangulation< dim, spacedim > &triangulation)
Definition: grid_tools.cc:2084
void transform(const Transformation &transformation, Triangulation< dim, spacedim > &triangulation)
void consistently_order_cells(std::vector< CellData< dim > > &cells)
Definition: grid_tools.cc:1959
void shift(const Tensor< 1, spacedim > &shift_vector, Triangulation< dim, spacedim > &triangulation)
Definition: grid_tools.cc:2050
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)
Definition: grid_tools.cc:741
void rotate(const double angle, Triangulation< dim > &triangulation)
double volume(const Triangulation< dim, spacedim > &tria, const Mapping< dim, spacedim > &mapping=(ReferenceCells::get_hypercube< dim >() .template get_default_linear_mapping< dim, spacedim >()))
Definition: grid_tools.cc:139
bool have_same_coarse_mesh(const Triangulation< dim, spacedim > &mesh_1, const Triangulation< dim, spacedim > &mesh_2)
void invert_all_negative_measure_cells(const std::vector< Point< spacedim > > &all_vertices, std::vector< CellData< dim > > &cells)
Definition: grid_tools.cc:936
std::tuple< std::vector< Point< spacedim > >, std::vector< CellData< dim > >, SubCellData > get_coarse_mesh_description(const Triangulation< dim, spacedim > &tria)
Definition: grid_tools.cc:535
double norm(const FEValuesBase< dim > &fe, const ArrayView< const std::vector< Tensor< 1, dim > > > &Du)
Definition: divergence.h:472
Point< spacedim > point(const gp_Pnt &p, const double tolerance=1e-10)
Definition: utilities.cc:190
SymmetricTensor< 2, dim, Number > C(const Tensor< 2, dim, Number > &F)
SymmetricTensor< 2, dim, Number > E(const Tensor< 2, dim, Number > &F)
SymmetricTensor< 2, dim, Number > e(const Tensor< 2, dim, Number > &F)
SymmetricTensor< 2, dim, Number > b(const Tensor< 2, dim, Number > &F)
SymmetricTensor< 2, dim, Number > d(const Tensor< 2, dim, Number > &F, const Tensor< 2, dim, Number > &dF_dt)
SymmetricTensor< 2, dim, Number > epsilon(const Tensor< 2, dim, Number > &Grad_u)
Tensor< 2, dim, Number > F(const Tensor< 2, dim, Number > &Grad_u)
Tensor< 2, 2, Number > rotation_matrix_2d(const Number &angle)
VectorType::value_type * end(VectorType &V)
VectorType::value_type * begin(VectorType &V)
std::string int_to_string(const unsigned int value, const unsigned int digits=numbers::invalid_unsigned_int)
Definition: utilities.cc:473
long double gamma(const unsigned int n)
unsigned int n_cells(const internal::TriangulationImplementation::NumberCache< 1 > &c)
Definition: tria.cc:13734
void copy(const T *begin, const T *end, U *dest)
const types::material_id invalid_material_id
Definition: types.h:233
static constexpr double PI_2
Definition: numbers.h:238
const types::boundary_id invalid_boundary_id
Definition: types.h:244
static constexpr double PI
Definition: numbers.h:233
const types::boundary_id internal_face_boundary_id
Definition: types.h:260
static const unsigned int invalid_unsigned_int
Definition: types.h:201
const types::manifold_id flat_manifold_id
Definition: types.h:269
STL namespace.
::VectorizedArray< Number, width > tan(const ::VectorizedArray< Number, width > &)
::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)
::VectorizedArray< Number, width > abs(const ::VectorizedArray< Number, width > &)
unsigned int manifold_id
Definition: types.h:141
unsigned int material_id
Definition: types.h:152
typename internal::ndarray::HelperArray< T, Ns... >::type ndarray
Definition: ndarray.h:108
const ::parallel::distributed::Triangulation< dim, spacedim > * triangulation
std::vector< unsigned int > vertices
types::manifold_id manifold_id
types::material_id material_id
types::boundary_id boundary_id
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
std::vector< CellData< 1 > > boundary_lines
constexpr Number determinant(const SymmetricTensor< 2, dim, Number > &)
constexpr Tensor< 1, dim, typename ProductType< Number1, Number2 >::type > cross_product_3d(const Tensor< 1, dim, Number1 > &src1, const Tensor< 1, dim, Number2 > &src2)
Definition: tensor.h:2660
const ::Triangulation< dim, spacedim > & tria