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