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