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