deal.II version GIT relicensing-6809-ge913b9bb34 2026-09-25 17:20:01+00:00
\(\newcommand{\dealvcentcolon}{\mathrel{\mathop{:}}}\) \(\newcommand{\dealcoloneq}{\dealvcentcolon\mathrel{\mkern-1.2mu}=}\) \(\newcommand{\jump}[1]{\left[\!\left[ #1 \right]\!\right]}\) \(\newcommand{\average}[1]{\left\{\!\left\{ #1 \right\}\!\right\}}\)
Loading...
Searching...
No Matches
reference_cell.cc
Go to the documentation of this file.
1// -----------------------------------------------------------------------------
2//
3// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception OR LGPL-2.1-or-later
4// Copyright (C) 2020 - 2026 by the deal.II authors
5//
6// This file is part of the deal.II library.
7//
8// Detailed license information governing the source code and contributions
9// can be found in LICENSE.md and CONTRIBUTING.md at the top level directory.
10//
11// -----------------------------------------------------------------------------
12
17
26
29#include <deal.II/grid/tria.h>
30
31#include <algorithm>
32#include <iostream>
33#include <memory>
34
36
37namespace
38{
39 namespace VTKCellType
40 {
41 // Define VTK constants for linear, quadratic and
42 // high-order Lagrange geometrices
43 enum : unsigned int
44 {
45 VTK_VERTEX = 1,
46 // Linear cells
47 VTK_LINE = 3,
48 VTK_TRIANGLE = 5,
49 VTK_QUAD = 9,
50 VTK_TETRA = 10,
51 VTK_HEXAHEDRON = 12,
52 VTK_WEDGE = 13,
53 VTK_PYRAMID = 14,
54 // Quadratic cells
55 VTK_QUADRATIC_EDGE = 21,
56 VTK_QUADRATIC_TRIANGLE = 22,
57 VTK_QUADRATIC_QUAD = 23,
58 VTK_QUADRATIC_TETRA = 24,
59 VTK_QUADRATIC_HEXAHEDRON = 25,
60 VTK_QUADRATIC_WEDGE = 26,
61 VTK_QUADRATIC_PYRAMID = 27,
62 // Lagrange cells
63 VTK_LAGRANGE_CURVE = 68,
64 VTK_LAGRANGE_TRIANGLE = 69,
65 VTK_LAGRANGE_QUADRILATERAL = 70,
66 VTK_LAGRANGE_TETRAHEDRON = 71,
67 VTK_LAGRANGE_HEXAHEDRON = 72,
68 VTK_LAGRANGE_WEDGE = 73,
69 VTK_LAGRANGE_PYRAMID = 74,
70 // Invalid code
72 };
73
74 } // namespace VTKCellType
75
76} // namespace
77
78
79
80template <int dim>
81std::string
83{
84 if constexpr (dim == 0)
85 {
86 switch (this->kind)
87 {
89 return "Vertex";
90 case ReferenceCells::Invalid<dim>:
91 return "Invalid";
92 default:
94 }
95 }
96 else if constexpr (dim == 1)
97 {
98 switch (this->kind)
99 {
101 return "Line";
102 case ReferenceCells::Invalid<dim>:
103 return "Invalid";
104 default:
106 }
107 }
108 else if constexpr (dim == 2)
109 {
110 switch (this->kind)
111 {
113 return "Tri";
115 return "Quad";
116 case ReferenceCells::Invalid<dim>:
117 return "Invalid";
118 default:
120 }
121 }
122 else if constexpr (dim == 3)
123 {
124 switch (this->kind)
125 {
127 return "Tet";
129 return "Pyramid";
131 return "Wedge";
133 return "Hex";
134 case ReferenceCells::Invalid<dim>:
135 return "Invalid";
136 default:
138 }
139 }
140 else
142
143 return "Invalid";
144}
145
146
147
148namespace
149{
150 // Return vertex_no-th vertex of the child_no-ith child cell.
151 //
152 // @note This function is not yet implemented in 3d since it is only called on
153 // faces of cells.
154 template <int dim>
156 child_vertex(const ReferenceCell<dim> reference_cell,
157 const unsigned int child_no,
158 const unsigned int vertex_no,
159 const RefinementCase<dim> refinement_case)
160 {
161 if constexpr (dim > 1)
162 AssertIndexRange(child_no, reference_cell.n_children(refinement_case));
163 AssertIndexRange(vertex_no, reference_cell.n_vertices());
164
165 constexpr Point<dim> V0;
166 // V isn't used for dim == 0
167 [[maybe_unused]] const auto V = [](const unsigned int d) {
168 return Point<dim>::unit_vector(d);
169 };
170
171 switch (reference_cell)
172 {
174 return V0;
176 {
179 if constexpr (dim == 1)
180 {
181 static constexpr ndarray<Point<1>, 2, 2>
182 isotropic_child_vertices = {{
183 {{V0, 0.5 * V(0)}},
184 {{0.5 * V(0), V(0)}},
185 }};
186 return isotropic_child_vertices[child_no][vertex_no];
187 }
188 else
190 }
192 {
195 if constexpr (dim == 2)
196 {
197 static constexpr ndarray<Point<2>, 4, 3>
198 isotropic_child_vertices = {{
199 {{V0, 0.5 * V(0), 0.5 * V(1)}},
200 {{0.5 * V(0), V(0), 0.5 * (V(0) + V(1))}},
201 {{0.5 * V(1), 0.5 * V(0) + 0.5 * V(1), V(1)}},
202 {{0.5 * V(0), 0.5 * V(0) + 0.5 * V(1), 0.5 * V(1)}},
203 }};
204 return isotropic_child_vertices[child_no][vertex_no];
205 }
206 else
208 }
210 {
211 if constexpr (dim == 2)
212 {
213 static constexpr Point<2> M = 0.5 * (V(0) + V(1));
214
215 static constexpr ndarray<Point<2>, 2, 4> cut_x_child_vertices =
216 {{
217 {{V0, 0.5 * V(0), V(1), V(1) + 0.5 * V(0)}},
218 {{0.5 * V(0), V(0), 0.5 * V(0) + V(1), V(0) + V(1)}},
219 }};
220
221 static constexpr ndarray<Point<2>, 2, 4> cut_y_child_vertices =
222 {{
223 {{V0, V(0), 0.5 * V(1), V(0) + 0.5 * V(1)}},
224 {{0.5 * V(1), V(0) + 0.5 * V(1), V(1), V(0) + V(1)}},
225 }};
226
227 static constexpr ndarray<Point<2>, 4, 4>
228 isotropic_child_vertices = {{
229 {{V0, 0.5 * V(0), 0.5 * V(1), M}},
230 {{0.5 * V(0), V(0), M, V(0) + 0.5 * V(1)}},
231 {{0.5 * V(1), M, V(1), V(1) + 0.5 * V(0)}},
232 {{M, V(0) + 0.5 * V(1), V(1) + 0.5 * V(0), V(0) + V(1)}},
233 }};
234
235 switch (refinement_case)
236 {
238 return cut_x_child_vertices[child_no][vertex_no];
240 return cut_y_child_vertices[child_no][vertex_no];
242 return isotropic_child_vertices[child_no][vertex_no];
243 default:
245 }
246 }
247 else
255 return {};
256 default:
258 return {};
259 }
260 }
261} // namespace
262
263
264
265template <int dim>
268 const unsigned int face_no,
269 const unsigned int subface_no,
270 const unsigned int subface_vertex_no,
271 const RefinementCase<dim - 1> face_refinement_case) const
272{
273 AssertIndexRange(face_no, n_faces());
274 if constexpr (dim > 1)
275 {
276 AssertIndexRange(subface_no,
277 face_reference_cell(face_no).n_children(
278 face_refinement_case));
279 Assert(face_refinement_case != RefinementCase<dim - 1>::no_refinement,
280 ExcMessage("This function may only be called for subfaces."));
281 }
282 AssertIndexRange(subface_vertex_no,
283 face_reference_cell(face_no).n_vertices());
284
285 Point<dim> p;
286 for (const unsigned int vertex_no :
287 face_reference_cell(face_no).vertex_indices())
288 p += face_vertex_location(face_no, vertex_no) *
289 face_reference_cell(face_no).d_linear_shape_function(
290 child_vertex(face_reference_cell(face_no),
291 subface_no,
292 subface_vertex_no,
293 face_refinement_case),
294 vertex_no);
295
296 return p;
297}
298
299
300
301template <int dim>
302std::pair<unsigned int, RefinementCase<dim - 1>>
304 const types::geometric_orientation combined_face_orientation,
305 const internal::SubfaceCase<dim> subface_case,
306 const unsigned int subface_no) const
307{
308 // 1. in 1d subfaces don't exist, but we still support some subface code
309 // (such as QProjector's functions) to enable dimension-independent
310 // programming. To match the convention used by 3d this will always return
311 // (0, isotropic_refinement).
312 //
313 // 2. historically we have permitted subface calculations in 2d for unrefined
314 // faces. In that case we don't actually need the value of subface_case
315 // since there is only one possible refinement - the returned value has to
316 // be isotropic_refinement.
317 //
318 // 3. Similarly, in 3d we treat case_none as case_isotropic.
319 if constexpr (dim == 1)
320 {
321 (void)subface_case;
322 AssertIndexRange(combined_face_orientation, n_face_orientations(0));
323 AssertIndexRange(subface_no, 1);
324 return std::make_pair(0, RefinementCase<dim - 1>::isotropic_refinement);
325 }
326
327 if constexpr (dim == 2)
328 {
329 (void)subface_case;
330 AssertIndexRange(combined_face_orientation, n_face_orientations(0));
331 AssertIndexRange(subface_no,
332 face_reference_cell(0).n_isotropic_children());
333 return std::make_pair(combined_face_orientation ==
335 1 - subface_no :
336 subface_no,
338 }
339 else if constexpr (dim == 3)
340 {
342
343 static const RefinementCase<dim - 1>
344 equivalent_refine_case[internal::SubfaceCase<dim>::case_isotropic + 1]
346 // case_none. there should be only
347 // invalid values here. However, as
348 // this function is also called (in
349 // tests) for cells which have no
350 // refined faces, use isotropic
351 // refinement instead
352 {RefinementCase<dim - 1>::cut_xy,
353 RefinementCase<dim - 1>::cut_xy,
354 RefinementCase<dim - 1>::cut_xy,
355 RefinementCase<dim - 1>::cut_xy},
356 // case_x
357 {RefinementCase<dim - 1>::cut_x,
358 RefinementCase<dim - 1>::cut_x,
359 RefinementCase<dim - 1>::no_refinement,
360 RefinementCase<dim - 1>::no_refinement},
361 // case_x1y
362 {RefinementCase<dim - 1>::cut_xy,
363 RefinementCase<dim - 1>::cut_xy,
364 RefinementCase<dim - 1>::cut_x,
365 RefinementCase<dim - 1>::no_refinement},
366 // case_x2y
367 {RefinementCase<dim - 1>::cut_x,
368 RefinementCase<dim - 1>::cut_xy,
369 RefinementCase<dim - 1>::cut_xy,
370 RefinementCase<dim - 1>::no_refinement},
371 // case_x1y2y
372 {RefinementCase<dim - 1>::cut_xy,
373 RefinementCase<dim - 1>::cut_xy,
374 RefinementCase<dim - 1>::cut_xy,
375 RefinementCase<dim - 1>::cut_xy},
376 // case_y
377 {RefinementCase<dim - 1>::cut_y,
378 RefinementCase<dim - 1>::cut_y,
379 RefinementCase<dim - 1>::no_refinement,
380 RefinementCase<dim - 1>::no_refinement},
381 // case_y1x
382 {RefinementCase<dim - 1>::cut_xy,
383 RefinementCase<dim - 1>::cut_xy,
384 RefinementCase<dim - 1>::cut_y,
385 RefinementCase<dim - 1>::no_refinement},
386 // case_y2x
387 {RefinementCase<dim - 1>::cut_y,
388 RefinementCase<dim - 1>::cut_xy,
389 RefinementCase<dim - 1>::cut_xy,
390 RefinementCase<dim - 1>::no_refinement},
391 // case_y1x2x
392 {RefinementCase<dim - 1>::cut_xy,
393 RefinementCase<dim - 1>::cut_xy,
394 RefinementCase<dim - 1>::cut_xy,
395 RefinementCase<dim - 1>::cut_xy},
396 // case_xy (case_isotropic)
397 {RefinementCase<dim - 1>::cut_xy,
398 RefinementCase<dim - 1>::cut_xy,
399 RefinementCase<dim - 1>::cut_xy,
400 RefinementCase<dim - 1>::cut_xy}};
401
402 constexpr unsigned int X = numbers::invalid_unsigned_int;
403 static const unsigned int
404 equivalent_subface_number[internal::SubfaceCase<dim>::case_isotropic +
406 // case_none, see above
407 {0, 1, 2, 3},
408 // case_x
409 {0, 1, X, X},
410 // case_x1y
411 {0, 2, 1, X},
412 // case_x2y
413 {0, 1, 3, X},
414 // case_x1y2y
415 {0, 2, 1, 3},
416 // case_y
417 {0, 1, X, X},
418 // case_y1x
419 {0, 1, 1, X},
420 // case_y2x
421 {0, 2, 3, X},
422 // case_y1x2x
423 {0, 1, 2, 3},
424 // case_xy (case_isotropic)
425 {0, 1, 2, 3}};
426
427 static const RefinementCase<dim - 1> rotated_refinement_case[4] = {
428 RefinementCase<dim - 1>::no_refinement,
429 RefinementCase<dim - 1>::cut_y,
430 RefinementCase<dim - 1>::cut_x,
431 RefinementCase<dim - 1>::cut_xy};
432 const auto [face_orientation, face_rotation, face_flip] =
433 internal::split_face_orientation(combined_face_orientation);
434
435 const auto equivalent_refinement_case =
436 equivalent_refine_case[subface_case][subface_no];
437 const unsigned int equivalent_subface_no =
438 equivalent_subface_number[subface_case][subface_no];
439 // make sure, that we got a valid subface and RefineCase
440 Assert(equivalent_refinement_case != RefinementCase<dim>::no_refinement,
442 Assert(equivalent_subface_no != X, ExcInternalError());
443 // now, finally respect non-standard faces
444 const RefinementCase<dim - 1> final_refinement_case =
445 (face_orientation == face_rotation ?
446 rotated_refinement_case[equivalent_refinement_case] :
447 equivalent_refinement_case);
448
449 const unsigned int final_subface_no =
451 final_refinement_case),
452 /*face_no = */ 4,
453 equivalent_subface_no,
454 face_orientation,
455 face_flip,
456 face_rotation,
457 equivalent_refinement_case);
458
459 return std::make_pair(final_subface_no, final_refinement_case);
460 }
461 else
462 {
463 (void)combined_face_orientation;
464 (void)subface_case;
465 (void)subface_no;
466
468 return {};
469 }
470}
471
472
473
474template <int dim>
475template <int spacedim>
476std::unique_ptr<Mapping<dim, spacedim>>
477ReferenceCell<dim>::get_default_mapping(const unsigned int degree) const
478{
479 if (is_hyper_cube())
480 return std::make_unique<MappingQ<dim, spacedim>>(degree);
481 else if (is_simplex())
482 if (degree == 1)
483 return std::make_unique<MappingP1<dim, spacedim>>();
484 else
485 return std::make_unique<MappingFE<dim, spacedim>>(
487 else if constexpr (dim == 3)
488 {
489 // Handle the remaining two cases:
490 if (*this == ReferenceCells::Pyramid)
491 return std::make_unique<MappingFE<dim, spacedim>>(
493 else if (*this == ReferenceCells::Wedge)
494 return std::make_unique<MappingFE<dim, spacedim>>(
496 }
497
499 return {};
500}
501
502
503
504template <int dim>
505template <int spacedim>
508{
509 if (is_hyper_cube())
511 else if (is_simplex())
512 {
513 static const MappingP1<dim, spacedim> mapping;
514 return mapping;
515 }
516 else if constexpr (dim == 3)
517 {
518 // Handle the remaining two cases:
519 if (*this == ReferenceCells::Pyramid)
520 {
521 static const MappingFE<dim, spacedim> mapping(
523 return mapping;
524 }
525 else if (*this == ReferenceCells::Wedge)
526 {
527 static const MappingFE<dim, spacedim> mapping(
529 return mapping;
530 }
531 }
532
535}
536
537
538
539template <int dim>
541ReferenceCell<dim>::get_gauss_type_quadrature(const unsigned n_points_1d) const
542{
543 if (is_hyper_cube())
544 return QGauss<dim>(n_points_1d);
545 else if (is_simplex())
546 return QGaussSimplex<dim>(n_points_1d);
547 else if constexpr (dim == 3)
548 {
549 // Handle the remaining two cases:
550 if (*this == ReferenceCells::Pyramid)
551 return QGaussPyramid<dim>(n_points_1d);
552 else if (*this == ReferenceCells::Wedge)
553 return QGaussWedge<dim>(n_points_1d);
554 }
555
557 return {};
558}
559
560
561
562template <int dim>
563const Quadrature<dim> &
565{
566 // A function that is used to fill a quadrature object of the
567 // desired type the first time we encounter a particular
568 // reference cell
569 const auto create_quadrature = [](const ReferenceCell<dim> &reference_cell) {
570 std::vector<Point<dim>> vertices(reference_cell.n_vertices());
571 for (const unsigned int v : reference_cell.vertex_indices())
572 vertices[v] = reference_cell.vertex(v);
573
574 return Quadrature<dim>(vertices);
575 };
576
577 if (is_hyper_cube())
578 {
579 static const Quadrature<dim> quadrature = create_quadrature(*this);
580 return quadrature;
581 }
582 else if (is_simplex())
583 {
584 static const Quadrature<dim> quadrature = create_quadrature(*this);
585 return quadrature;
586 }
587 else if constexpr (dim == 3)
588 {
589 // Handle the remaining two cases:
590 if (*this == ReferenceCells::Pyramid)
591 {
592 static const Quadrature<dim> quadrature = create_quadrature(*this);
593 return quadrature;
594 }
595 else if (*this == ReferenceCells::Wedge)
596 {
597 static const Quadrature<dim> quadrature = create_quadrature(*this);
598 return quadrature;
599 }
600 }
601
602
604 static const Quadrature<dim> dummy;
605 return dummy;
606}
607
608
609
610template <int dim>
611unsigned int
613 const unsigned int vertex_n) const
614{
615 AssertIndexRange(vertex_n, n_vertices());
616
617 if constexpr (dim == 0)
619 else if constexpr (dim == 1)
620 return vertex_n;
621 else if constexpr (dim == 2)
622 {
623 switch (this->kind)
624 {
626 return vertex_n;
628 {
629 constexpr std::array<unsigned int, 4> exodus_to_deal{
630 {0, 1, 3, 2}};
631 return exodus_to_deal[vertex_n];
632 }
633 }
634 }
635 else if constexpr (dim == 3)
636 {
637 switch (this->kind)
638 {
640 return vertex_n;
643 constexpr std::array<unsigned int, 8> exodus_to_deal{
644 {0, 1, 3, 2, 4, 5, 7, 6}};
645 return exodus_to_deal[vertex_n];
646 }
648 {
649 constexpr std::array<unsigned int, 6> exodus_to_deal{
650 {2, 1, 0, 5, 4, 3}};
651 return exodus_to_deal[vertex_n];
652 }
654 {
655 constexpr std::array<unsigned int, 5> exodus_to_deal{
656 {0, 1, 3, 2, 4}};
657 return exodus_to_deal[vertex_n];
658 }
659 }
660 }
661
662
665}
666
667
668
669template <int dim>
670unsigned int
672{
673 AssertIndexRange(face_n, n_faces());
674
675 if constexpr (dim == 0)
676 return 0;
677 else if constexpr (dim == 1)
678 return face_n;
679 else if constexpr (dim == 2)
680 {
681 switch (this->kind)
682 {
684 return face_n;
686 {
687 constexpr std::array<unsigned int, 4> exodus_to_deal{
688 {2, 1, 3, 0}};
689 return exodus_to_deal[face_n];
690 }
691 }
692 }
693 else if constexpr (dim == 3)
694 {
695 switch (this->kind)
696 {
698 {
699 constexpr std::array<unsigned int, 4> exodus_to_deal{
700 {1, 3, 2, 0}};
701 return exodus_to_deal[face_n];
702 }
704 {
705 constexpr std::array<unsigned int, 6> exodus_to_deal{
706 {2, 1, 3, 0, 4, 5}};
707 return exodus_to_deal[face_n];
708 }
710 {
711 constexpr std::array<unsigned int, 6> exodus_to_deal{
712 {3, 4, 2, 0, 1}};
713 return exodus_to_deal[face_n];
714 }
716 {
717 constexpr std::array<unsigned int, 5> exodus_to_deal{
718 {3, 2, 4, 1, 0}};
719 return exodus_to_deal[face_n];
720 }
721 }
722 }
723
726}
727
728
729
730template <int dim>
731unsigned int
732ReferenceCell<dim>::ucd_vertex_to_deal_vertex(const unsigned int vertex_n) const
733{
734 AssertIndexRange(vertex_n, n_vertices());
735 // Information on this file format can be found here
736 //
737 // https://lanl.github.io/LaGriT/pages/docs/read_avs.html
738 //
739 // http://www.hnware.de/rismo/dokumente/anwenderdoku/formate/avs_ucd.html
740
741 if constexpr (dim == 1)
742 {
743 return GeometryInfo<1>::ucd_to_deal[vertex_n];
744 }
745 else if constexpr (dim == 2)
746 {
747 switch (this->kind)
748 {
750 return vertex_n;
752 return GeometryInfo<2>::ucd_to_deal[vertex_n];
753 }
754 }
755 else if constexpr (dim == 3)
756 {
757 switch (this->kind)
758 {
760 {
761 constexpr std::array<unsigned int, 4> ucd_to_deal_tet{
762 {0, 3, 1, 2}};
763 return ucd_to_deal_tet[vertex_n];
764 }
766 {
767 constexpr std::array<unsigned int, 5> ucd_to_deal_pyr{
768 {4, 0, 1, 3, 2}};
769 return ucd_to_deal_pyr[vertex_n];
770 }
772 {
773 constexpr std::array<unsigned int, 6> ucd_to_deal_wedge{
774 {1, 2, 0, 4, 5, 3}};
775 return ucd_to_deal_wedge[vertex_n];
776 }
778 {
779 return GeometryInfo<3>::ucd_to_deal[vertex_n];
780 }
781 }
782 }
783 // All of the other cases not listed above:
786}
787
788
789
790template <int dim>
791unsigned int
792ReferenceCell<dim>::unv_vertex_to_deal_vertex(const unsigned int vertex_n) const
793{
794 AssertIndexRange(vertex_n, n_vertices());
795 // Information on this file format isn't easy to find - the documents here
796 //
797 // https://www.ceas3.uc.edu/sdrluff/
798 //
799 // doesn't actually explain anything about the sections we care about (2412)
800 // in any detail. For node numbering I worked backwards from what is actually
801 // in our test files (since that's supposed to work), which all use some
802 // non-standard clockwise numbering scheme which starts at the bottom right
803 // vertex.
804 if constexpr (dim == 1)
805 return vertex_n;
806 else if constexpr (dim == 2)
807 {
809 {
810 constexpr std::array<unsigned int, 4> unv_to_deal{{1, 0, 2, 3}};
811 return unv_to_deal[vertex_n];
812 }
813 }
814 else if constexpr (dim == 3)
815 {
816 if (*this == ReferenceCells::Hexahedron)
817 {
818 constexpr std::array<unsigned int, 8> unv_to_deal{
819 {6, 7, 5, 4, 2, 3, 1, 0}};
820 return unv_to_deal[vertex_n];
821 }
822 }
823
824 // All of the other cases not listed above:
827}
828
829
830
831template <int dim>
832unsigned int
834{
835 if constexpr (dim == 0)
836 return VTKCellType::VTK_VERTEX;
837 else if constexpr (dim == 1)
838 return VTKCellType::VTK_LINE;
839 else if constexpr (dim == 2)
840 {
841 switch (this->kind)
842 {
844 return VTKCellType::VTK_TRIANGLE;
846 return VTKCellType::VTK_QUAD;
847 }
848 }
849 else if constexpr (dim == 3)
850 {
851 switch (this->kind)
852 {
854 return VTKCellType::VTK_TETRA;
856 return VTKCellType::VTK_PYRAMID;
858 return VTKCellType::VTK_WEDGE;
860 return VTKCellType::VTK_HEXAHEDRON;
861 }
862 }
863
865 return VTKCellType::VTK_INVALID;
866}
867
868
869
870template <int dim>
871unsigned int
873{
874 if constexpr (dim == 0)
875 return VTKCellType::VTK_VERTEX;
876 else if constexpr (dim == 1)
877 return VTKCellType::VTK_QUADRATIC_EDGE;
878 else if constexpr (dim == 2)
879 {
880 switch (this->kind)
881 {
883 return VTKCellType::VTK_QUADRATIC_TRIANGLE;
885 return VTKCellType::VTK_QUADRATIC_QUAD;
886 }
887 }
888 else if constexpr (dim == 3)
889 {
890 switch (this->kind)
891 {
893 return VTKCellType::VTK_QUADRATIC_TETRA;
895 return VTKCellType::VTK_QUADRATIC_PYRAMID;
897 return VTKCellType::VTK_QUADRATIC_WEDGE;
899 return VTKCellType::VTK_QUADRATIC_HEXAHEDRON;
900 }
901 }
902
904 return VTKCellType::VTK_INVALID;
905}
906
907
908
909template <int dim>
910unsigned int
912{
913 if constexpr (dim == 0)
914 return VTKCellType::VTK_VERTEX;
915 else if constexpr (dim == 1)
916 return VTKCellType::VTK_LAGRANGE_CURVE;
917 else if constexpr (dim == 2)
918 {
919 switch (this->kind)
920 {
922 return VTKCellType::VTK_LAGRANGE_TRIANGLE;
924 return VTKCellType::VTK_LAGRANGE_QUADRILATERAL;
925 }
926 }
927 else if constexpr (dim == 3)
928 {
929 switch (this->kind)
930 {
932 return VTKCellType::VTK_LAGRANGE_TETRAHEDRON;
934 return VTKCellType::VTK_LAGRANGE_PYRAMID;
936 return VTKCellType::VTK_LAGRANGE_WEDGE;
938 return VTKCellType::VTK_LAGRANGE_HEXAHEDRON;
939 }
940 }
941
943 return VTKCellType::VTK_INVALID;
944}
945
946
947
948template <int dim>
949unsigned int
951 const std::array<unsigned, dim> &node_indices,
952 const std::array<unsigned, dim> &nodes_per_direction,
953 const bool legacy_format) const
954{
955 (void)legacy_format;
956
957 if constexpr (dim == 0)
958 {
960 return 0;
961 }
962 else if constexpr (dim == 1)
963 {
968 const unsigned int i = node_indices[0];
969
970 const bool ibdy = (i == 0 || i == nodes_per_direction[0]);
971 // How many boundaries do we lie on at once?
972 const int nbdy = (ibdy ? 1 : 0);
973
974 if (nbdy == 1) // Vertex DOF
975 { // ijk is a corner node. Return the proper index (somewhere in [0,7]):
976 return i ? 1 : 0;
977 }
978
979 const int offset = 2;
980 return (i - 1) + offset;
981 }
982 else if constexpr (dim == 2)
983 {
989
990 const unsigned int i = node_indices[0];
991 const unsigned int j = node_indices[1];
992
993 const bool ibdy = (i == 0 || i == nodes_per_direction[0]);
994 const bool jbdy = (j == 0 || j == nodes_per_direction[1]);
995 // How many boundaries do we lie on at once?
996 const int nbdy = (ibdy ? 1 : 0) + (jbdy ? 1 : 0);
998 if (nbdy == 2) // Vertex DOF
999 { // ijk is a corner node. Return the proper index (somewhere in [0,3]):
1000 return (i != 0u ? (j != 0u ? 2 : 1) : (j != 0u ? 3 : 0));
1001 }
1002
1003 int offset = 4;
1004 if (nbdy == 1) // Edge DOF
1005 {
1006 if (!ibdy)
1007 { // On i axis
1008 return (i - 1) +
1009 (j != 0u ? nodes_per_direction[0] - 1 +
1010 nodes_per_direction[1] - 1 :
1011 0) +
1012 offset;
1013 }
1014
1015 if (!jbdy)
1016 { // On j axis
1017 return (j - 1) +
1018 (i != 0u ? nodes_per_direction[0] - 1 :
1019 2 * (nodes_per_direction[0] - 1) +
1020 nodes_per_direction[1] - 1) +
1021 offset;
1023 }
1024
1025 offset += 2 * (nodes_per_direction[0] - 1 + nodes_per_direction[1] - 1);
1026 // nbdy == 0: Face DOF
1027 return offset + (i - 1) + (nodes_per_direction[0] - 1) * ((j - 1));
1028 }
1029 else if constexpr (dim == 3)
1030 {
1042
1043 const unsigned int i = node_indices[0];
1044 const unsigned int j = node_indices[1];
1045 const unsigned int k = node_indices[2];
1046
1047 const bool ibdy = (i == 0 || i == nodes_per_direction[0]);
1048 const bool jbdy = (j == 0 || j == nodes_per_direction[1]);
1049 const bool kbdy = (k == 0 || k == nodes_per_direction[2]);
1050 // How many boundaries do we lie on at once?
1051 const int nbdy = (ibdy ? 1 : 0) + (jbdy ? 1 : 0) + (kbdy ? 1 : 0);
1052
1053 if (nbdy == 3) // Vertex DOF
1054 { // ijk is a corner node. Return the proper index (somewhere in [0,7]):
1055 return (i != 0u ? (j != 0u ? 2 : 1) : (j != 0u ? 3 : 0)) +
1056 (k != 0u ? 4 : 0);
1057 }
1058
1059 int offset = 8;
1060 if (nbdy == 2) // Edge DOF
1061 {
1062 if (!ibdy)
1063 { // On i axis
1064 return (i - 1) +
1065 (j != 0u ? nodes_per_direction[0] - 1 +
1066 nodes_per_direction[1] - 1 :
1067 0) +
1068 (k != 0u ? 2 * (nodes_per_direction[0] - 1 +
1069 nodes_per_direction[1] - 1) :
1070 0) +
1071 offset;
1072 }
1073 if (!jbdy)
1074 { // On j axis
1075 return (j - 1) +
1076 (i != 0u ? nodes_per_direction[0] - 1 :
1077 2 * (nodes_per_direction[0] - 1) +
1078 nodes_per_direction[1] - 1) +
1079 (k != 0u ? 2 * (nodes_per_direction[0] - 1 +
1080 nodes_per_direction[1] - 1) :
1081 0) +
1082 offset;
1083 }
1084 // !kbdy, On k axis
1085 offset +=
1086 4 * (nodes_per_direction[0] - 1) + 4 * (nodes_per_direction[1] - 1);
1087 if (legacy_format)
1088 return (k - 1) +
1089 (nodes_per_direction[2] - 1) *
1090 (i != 0u ? (j != 0u ? 3 : 1) : (j != 0u ? 2 : 0)) +
1091 offset;
1092 else
1093 return (k - 1) +
1094 (nodes_per_direction[2] - 1) *
1095 (i != 0u ? (j != 0u ? 2 : 1) : (j != 0u ? 3 : 0)) +
1096 offset;
1097 }
1098
1099 offset += 4 * (nodes_per_direction[0] - 1 + nodes_per_direction[1] - 1 +
1100 nodes_per_direction[2] - 1);
1101 if (nbdy == 1) // Face DOF
1102 {
1103 if (ibdy) // On i-normal face
1104 return (j - 1) + ((nodes_per_direction[1] - 1) * (k - 1)) +
1105 (i != 0u ? (nodes_per_direction[1] - 1) *
1106 (nodes_per_direction[2] - 1) :
1107 0) +
1108 offset;
1109 offset +=
1110 2 * (nodes_per_direction[1] - 1) * (nodes_per_direction[2] - 1);
1111 if (jbdy) // On j-normal face
1112 return (i - 1) + ((nodes_per_direction[0] - 1) * (k - 1)) +
1113 (j != 0u ? (nodes_per_direction[2] - 1) *
1114 (nodes_per_direction[0] - 1) :
1115 0) +
1116 offset;
1117 offset +=
1118 2 * (nodes_per_direction[2] - 1) * (nodes_per_direction[0] - 1);
1119 // kbdy, On k-normal face
1120 return (i - 1) + ((nodes_per_direction[0] - 1) * (j - 1)) +
1121 (k != 0u ? (nodes_per_direction[0] - 1) *
1122 (nodes_per_direction[1] - 1) :
1123 0) +
1124 offset;
1125 }
1126
1127 // nbdy == 0: Body DOF
1128 offset +=
1129 2 * ((nodes_per_direction[1] - 1) * (nodes_per_direction[2] - 1) +
1130 (nodes_per_direction[2] - 1) * (nodes_per_direction[0] - 1) +
1131 (nodes_per_direction[0] - 1) * (nodes_per_direction[1] - 1));
1132 return offset + (i - 1) +
1133 (nodes_per_direction[0] - 1) *
1134 ((j - 1) + (nodes_per_direction[1] - 1) * ((k - 1)));
1135 }
1136
1138 return 0;
1139}
1140
1141
1142
1143template <int dim>
1144unsigned int
1146 const unsigned int vertex_index) const
1147{
1148 AssertIndexRange(vertex_index, n_vertices());
1149
1150 // For some of the following, deal.II uses the same ordering as VTK
1151 // and in that case, we only need to return 'vertex_index' (i.e.,
1152 // use the identity mapping). For some others, we need to translate.
1153 //
1154 // For the ordering, see the VTK manual (for example at
1155 // http://www.princeton.edu/~efeibush/viscourse/vtk.pdf, page 9).
1156 if constexpr (dim == 0)
1157 return vertex_index;
1158 else if constexpr (dim == 1)
1159 return vertex_index;
1160 else if constexpr (dim == 2)
1161 {
1162 switch (this->kind)
1163 {
1165 return vertex_index;
1167 {
1168 static constexpr std::array<unsigned int, 4>
1169 index_translation_table = {{0, 1, 3, 2}};
1170 return index_translation_table[vertex_index];
1171 }
1172 }
1173 }
1174 else if constexpr (dim == 3)
1175 {
1176 switch (this->kind)
1177 {
1179 return vertex_index;
1181 {
1182 static constexpr std::array<unsigned int, 5>
1183 index_translation_table = {{0, 1, 3, 2, 4}};
1184 return index_translation_table[vertex_index];
1185 }
1187 return vertex_index;
1189 {
1190 static constexpr std::array<unsigned int, 8>
1191 index_translation_table = {{0, 1, 3, 2, 4, 5, 7, 6}};
1192 return index_translation_table[vertex_index];
1193 }
1194 }
1195 }
1196
1199}
1200
1201
1202
1203template <int dim>
1204unsigned int
1206{
1207 /*
1208 From the GMSH documentation:
1209
1210 elm-type
1211 defines the geometrical type of the n-th element:
1212
1213 1
1214 Line (2 nodes).
1215
1216 2
1217 Triangle (3 nodes).
1218
1219 3
1220 Quadrangle (4 nodes).
1221
1222 4
1223 Tetrahedron (4 nodes).
1224
1225 5
1226 Hexahedron (8 nodes).
1227
1228 6
1229 Prism (6 nodes).
1230
1231 7
1232 Pyramid (5 nodes).
1233
1234 8
1235 Second order line (3 nodes: 2 associated with the vertices and 1 with the
1236 edge).
1237
1238 9
1239 Second order triangle (6 nodes: 3 associated with the vertices and 3 with
1240 the edges).
1241
1242 10 Second order quadrangle (9 nodes: 4 associated with the
1243 vertices, 4 with the edges and 1 with the face).
1244
1245 11 Second order tetrahedron (10 nodes: 4 associated with the vertices and 6
1246 with the edges).
1247
1248 12 Second order hexahedron (27 nodes: 8 associated with the vertices, 12
1249 with the edges, 6 with the faces and 1 with the volume).
1250
1251 13 Second order prism (18 nodes: 6 associated with the vertices, 9 with the
1252 edges and 3 with the quadrangular faces).
1253
1254 14 Second order pyramid (14 nodes: 5 associated with the vertices, 8 with
1255 the edges and 1 with the quadrangular face).
1256
1257 15 Point (1 node).
1258 */
1259
1260 if constexpr (dim == 0)
1261 return 15;
1262 else if constexpr (dim == 1)
1263 return 1;
1264 else if constexpr (dim == 2)
1265 {
1266 switch (this->kind)
1267 {
1269 return 2;
1271 return 3;
1272 }
1273 }
1274 else if constexpr (dim == 3)
1275 {
1276 switch (this->kind)
1277 {
1279 return 4;
1281 return 7;
1283 return 6;
1285 return 5;
1286 }
1287 }
1288
1291}
1292
1293
1294
1295namespace
1296{
1297 // Compute the nearest point to @p on the line segment. Return this point
1298 // and the square of its distance to @p.
1299 template <int dim>
1300 std::pair<Point<dim>, double>
1301 project_to_line(const Point<dim> &x0,
1302 const Point<dim> &x1,
1303 const Point<dim> &p)
1304 {
1305 Assert(x0 != x1, ExcInternalError());
1306 // t is the convex combination coefficient (x = (1 - t) * x0 + t * x1)
1307 // defining the position of the closest point on the line (not line segment)
1308 // to p passing through x0 and x1. This formula is equivalent to the
1309 // standard 'project a vector onto another vector', where each vector is
1310 // shifted to start at x0.
1311 const double t = ((x1 - x0) * (p - x0)) / ((x1 - x0).norm_square());
1312
1313 if (t <= 0)
1314 // The projection onto the infinite line is ahead of x0 on the line
1315 return std::make_pair(x0, x0.distance_square(p));
1316 else if (t <= 1)
1317 {
1318 // The projection onto the infinite line is between x0 and x1.
1319 const auto p2 = x0 + t * (x1 - x0);
1320 return std::make_pair(p2, p2.distance_square(p));
1321 }
1322 else
1323 // The projection onto the infinite line is beyond x1 on the line
1324 return std::make_pair(x1, x1.distance_square(p));
1325 }
1326
1327
1328
1329 // template base-case
1330 template <int dim>
1331 std::pair<Point<dim>, double>
1332 project_to_quad(const std::array<Point<dim>, 3> & /*vertices*/,
1333 const Point<dim> & /*p*/,
1334 const ReferenceCell<dim - 1> /*face_reference_cell*/)
1335 {
1337 return std::make_pair(Point<dim>(),
1338 std::numeric_limits<double>::signaling_NaN());
1339 }
1340
1358 template <>
1359 std::pair<Point<3>, double>
1360 project_to_quad(const std::array<Point<3>, 3> &vertices,
1361 const Point<3> &p,
1362 const ReferenceCell<2> face_reference_cell)
1363 {
1364 Assert(face_reference_cell == ReferenceCells::Triangle ||
1365 face_reference_cell == ReferenceCells::Quadrilateral,
1367
1368 // Make the problem slightly easier by shifting everything to avoid a point
1369 // at the origin (this way we can invert the matrix of vertices). Use 2.0 so
1370 // that the bottom left vertex of a Pyramid is now at x = 1.
1371 std::array<Point<3>, 3> shifted_vertices = vertices;
1372 const Tensor<1, 3> shift{{2.0, 2.0, 2.0}};
1373 for (Point<3> &shifted_vertex : shifted_vertices)
1374 shifted_vertex += shift;
1375 const Point<3> shifted_p = p + shift;
1376
1377 // As we are projecting onto a face of a reference cell, the vectors
1378 // describing its local coordinate system should be orthogonal. We don't
1379 // know which of the three vectors computed from p are mutually orthogonal
1380 // for triangles so that case requires an extra check.
1381 Tensor<1, 3> e0;
1382 Tensor<1, 3> e1;
1383 const Point<3> vertex = shifted_vertices[0];
1384 // Triangles are difficult because of two cases:
1385 // 1. the top face of a Tetrahedron, which does not have a right angle
1386 // 2. wedges and pyramids, whose faces do not lie on the reference simplex
1387 //
1388 // Deal with both by creating a locally orthogonal (but not necessarily
1389 // orthonormal) coordinate system and testing if the projected point is in
1390 // the triangle by expressing it as a convex combination of the vertices.
1391 if (face_reference_cell == ReferenceCells::Triangle)
1392 {
1393 e0 = shifted_vertices[1] - shifted_vertices[0];
1394 e1 = shifted_vertices[2] - shifted_vertices[0];
1395 e1 -= (e0 * e1) * e0 / (e0.norm_square());
1396 }
1397 else
1398 {
1399 e0 = shifted_vertices[1] - shifted_vertices[0];
1400 e1 = shifted_vertices[2] - shifted_vertices[0];
1401 }
1402 Assert(std::abs(e0 * e1) <= 1e-14, ExcInternalError());
1403 // the quadrilaterals on pyramids and wedges don't necessarily have edge
1404 // lengths of 1 so we cannot skip the denominator
1405 const double c0 = e0 * (shifted_p - vertex) / e0.norm_square();
1406 const double c1 = e1 * (shifted_p - vertex) / e1.norm_square();
1407 const Point<3> projected_shifted_p = vertex + c0 * e0 + c1 * e1;
1408
1409 bool in_quad = false;
1410 if (face_reference_cell == ReferenceCells::Triangle)
1411 {
1412 Tensor<2, 3> shifted_vertex_matrix;
1413 for (unsigned int i = 0; i < 3; ++i)
1414 shifted_vertex_matrix[i] = shifted_vertices[i];
1415 const Tensor<1, 3> combination_coordinates =
1416 invert(transpose(shifted_vertex_matrix)) * projected_shifted_p;
1417 bool is_convex_combination = true;
1418 for (unsigned int i = 0; i < 3; ++i)
1419 is_convex_combination = is_convex_combination &&
1420 (0.0 <= combination_coordinates[i]) &&
1421 (combination_coordinates[i] <= 1.0);
1422 in_quad = is_convex_combination;
1423 }
1424 else
1425 in_quad = (0.0 <= c0 && c0 <= 1.0 && 0.0 <= c1 && c1 <= 1.0);
1426
1427 if (in_quad)
1428 return std::make_pair(projected_shifted_p - shift,
1429 shifted_p.distance_square(projected_shifted_p));
1430 else
1431 return std::make_pair(Point<3>(), std::numeric_limits<double>::max());
1432 }
1433} // namespace
1434
1435
1436
1437template <int dim>
1440{
1441 // Handle simple cases first:
1442
1443 // A 0d point has no coordinates. Any point *equals* the 0d vertex and so
1444 // is also within it:
1445 if constexpr (dim == 0)
1446 return Point<dim>();
1447
1448 if (contains_point(p, 0.0))
1449 return p;
1450
1451 if constexpr (dim == 1)
1452 return project_to_line(vertex(0), vertex(1), p).first;
1453 else
1454 {
1455 // Find the closest vertex so that we only need to check adjacent faces
1456 // and lines.
1457 Point<dim> result;
1458 unsigned int closest_vertex_no = 0;
1459 double closest_vertex_distance_square = vertex(0).distance_square(p);
1460 for (unsigned int i = 1; i < n_vertices(); ++i)
1461 {
1462 const double new_vertex_distance_square =
1463 vertex(i).distance_square(p);
1464 if (new_vertex_distance_square < closest_vertex_distance_square)
1465 {
1466 closest_vertex_no = i;
1467 closest_vertex_distance_square = new_vertex_distance_square;
1468 }
1469 }
1470
1471 double min_distance_square = std::numeric_limits<double>::max();
1472 if constexpr (dim == 2)
1473 {
1474 for (const unsigned int face_no :
1475 faces_for_given_vertex(closest_vertex_no))
1476 {
1477 const Point<dim> v0 = vertex(line_to_cell_vertices(face_no, 0));
1478 const Point<dim> v1 = vertex(line_to_cell_vertices(face_no, 1));
1479
1480 auto pair = project_to_line(v0, v1, p);
1481 if (pair.second < min_distance_square)
1482 {
1483 result = pair.first;
1484 min_distance_square = pair.second;
1485 }
1486 }
1487 }
1488 else
1489 // the 3d case
1490 {
1491 // Check faces and then lines.
1492 //
1493 // For reference cells with sloped faces (i.e., all 3D shapes except
1494 // Hexahedra), we might be able to do a valid normal projection to a
1495 // face with a different slope which is on the 'other side' of the
1496 // reference cell. To catch that case we have to unconditionally check
1497 // lines after checking faces.
1498 //
1499 // For pyramids the closest vertex might not be on the closest face:
1500 // for example, the origin is closest to vertex 4 which is not on the
1501 // bottom plane. Get around that by just checking all faces for
1502 // pyramids.
1503 const std::array<unsigned int, 5> all_pyramid_faces{{0, 1, 2, 3, 4}};
1504 const auto &faces =
1505 *this == ReferenceCells::Pyramid ?
1506 ArrayView<const unsigned int>(all_pyramid_faces) :
1507 faces_for_given_vertex(closest_vertex_no);
1508 for (const unsigned int face_no : faces)
1509 {
1510 auto face_cell = face_reference_cell(face_no);
1511 // We only need the first three points since for quads the last
1512 // point is redundant
1513 std::array<Point<dim>, 3> vertices;
1514 for (unsigned int vertex_no = 0; vertex_no < 3; ++vertex_no)
1515 vertices[vertex_no] = vertex(face_to_cell_vertices(
1516 face_no, vertex_no, numbers::default_geometric_orientation));
1517
1518 auto pair = project_to_quad(vertices, p, face_cell);
1519 if (pair.second < min_distance_square)
1520 {
1521 result = pair.first;
1522 min_distance_square = pair.second;
1523 }
1524 }
1525
1526 for (const unsigned int face_no :
1527 faces_for_given_vertex(closest_vertex_no))
1528 {
1529 auto face_cell = face_reference_cell(face_no);
1530 for (const unsigned int face_line_no : face_cell.line_indices())
1531 {
1532 const auto cell_line_no =
1533 face_to_cell_lines(face_no,
1534 face_line_no,
1536 const auto v0 =
1537 vertex(line_to_cell_vertices(cell_line_no, 0));
1538 const auto v1 =
1539 vertex(line_to_cell_vertices(cell_line_no, 1));
1540 auto pair = project_to_line(v0, v1, p);
1541 if (pair.second < min_distance_square)
1542 {
1543 result = pair.first;
1544 min_distance_square = pair.second;
1545 }
1546 }
1547 }
1548 }
1549
1550 Assert(min_distance_square < std::numeric_limits<double>::max(),
1552
1553 // If necessary, slightly adjust the computed point so that it is closer
1554 // to being on the surface of the reference cell. Due to roundoff it is
1555 // difficult to place points on sloped surfaces (e.g., for Pyramids) so
1556 // this check isn't perfect but does improve the accuracy of the projected
1557 // point.
1558 if (!contains_point(result, 0.0))
1559 {
1560 if constexpr (dim == 2)
1561 {
1562 switch (this->kind)
1563 {
1565 // the bounds for each dimension of a hypercube are mutually
1566 // independent:
1567 for (unsigned int d = 0; d < dim; ++d)
1568 result[d] = std::clamp(result[d], 0.0, 1.0);
1569 break;
1570
1572 // simplices can use the standard definition of a simplex:
1573 result[0] = std::clamp(result[0], 0.0, 1.0);
1574 result[1] = std::clamp(result[1], 0.0, 1.0 - result[0]);
1575 break;
1576
1577 default:
1579 }
1580 }
1581 else if constexpr (dim == 3)
1582 {
1583 switch (this->kind)
1584 {
1586 // the bounds for each dimension of a hypercube are mutually
1587 // independent:
1588 for (unsigned int d = 0; d < dim; ++d)
1589 result[d] = std::clamp(result[d], 0.0, 1.0);
1590 break;
1591
1593 // simplices can use the standard definition of a simplex:
1594 result[0] = std::clamp(result[0], 0.0, 1.0);
1595 result[1] = std::clamp(result[1], 0.0, 1.0 - result[0]);
1596 result[2] =
1597 std::clamp(result[2], 0.0, 1.0 - result[0] - result[1]);
1598 break;
1599
1600 // wedges and pyramids are more ad-hoc:
1602 result[0] = std::clamp(result[0], 0.0, 1.0);
1603 result[1] = std::clamp(result[1], 0.0, 1.0 - result[0]);
1604 result[2] = std::clamp(result[2], 0.0, 1.0);
1605 break;
1606
1608 {
1609 result[0] = std::clamp(result[0], -1.0, 1.0);
1610 result[1] = std::clamp(result[1], -1.0, 1.0);
1611 // It suffices to transform everything to the first
1612 // quadrant to adjust z:
1613 const auto x_abs = std::abs(result[0]);
1614 const auto y_abs = std::abs(result[1]);
1615
1616 if (y_abs <= x_abs)
1617 result[2] = std::clamp(result[2], 0.0, 1.0 - x_abs);
1618 else
1619 result[2] = std::clamp(result[2], 0.0, 1.0 - y_abs);
1620 }
1621 break;
1622 default:
1624 }
1625 }
1626 else
1628 }
1629
1630 // We should be within 4 * eps of the cell by this point. The roundoff
1631 // error comes from, e.g., computing (1 - x) + x when moving points onto
1632 // the top of a Pyramid.
1633 Assert(contains_point(result,
1634 4.0 * std::numeric_limits<double>::epsilon()),
1636
1637 return result;
1638 }
1639}
1640
1641
1642
1643template <int dim>
1644std::ostream &
1645operator<<(std::ostream &out, const ReferenceCell<dim> &reference_cell)
1646{
1647 AssertThrow(out.fail() == false, ExcIO());
1648
1649 // Output as an integer to avoid outputting it as a character with
1650 // potentially non-printing value:
1651 out << static_cast<unsigned int>(reference_cell.kind);
1652 return out;
1653}
1654
1655
1656
1657template <int dim>
1658std::istream &
1659operator>>(std::istream &in, ReferenceCell<dim> &reference_cell)
1660{
1661 AssertThrow(in.fail() == false, ExcIO());
1662
1663 // Read the information as an integer and convert it to the correct type
1664 unsigned int value;
1665 in >> value;
1666 reference_cell.kind = static_cast<decltype(reference_cell.kind)>(value);
1667
1668 // Ensure that the object we read is valid
1669 Assert(
1670 (reference_cell == ReferenceCells::Vertex) ||
1671 (reference_cell == ReferenceCells::Line) ||
1672 (reference_cell == ReferenceCells::Triangle) ||
1673 (reference_cell == ReferenceCells::Quadrilateral) ||
1674 (reference_cell == ReferenceCells::Tetrahedron) ||
1675 (reference_cell == ReferenceCells::Hexahedron) ||
1676 (reference_cell == ReferenceCells::Wedge) ||
1677 (reference_cell == ReferenceCells::Pyramid) ||
1678 (reference_cell == ReferenceCells::Invalid<dim>),
1679 ExcMessage(
1680 "The reference cell kind just read does not correspond to one of the "
1681 "valid choices. There must be an error."));
1682
1683 return in;
1684}
1685
1686
1687#include "grid/reference_cell.inst"
1688
1689#ifndef DOXYGEN
1690// Here also instantiate select members for dimension 0 (vertices) in addition
1691// to the general class instantiation for dim=1,2,3 in the .inst file :
1692template std::string
1694
1695template unsigned int
1697
1698template unsigned int
1700
1701template unsigned int
1703
1704template unsigned int
1706 const unsigned int vertex_index) const;
1707
1708template Quadrature<0>
1709ReferenceCell<0>::get_gauss_type_quadrature(const unsigned n_points_1d) const;
1710
1711template const Quadrature<0> &
1713
1714template std::ostream &
1715operator<<(std::ostream &out, const ReferenceCell<0> &reference_cell);
1716
1717template std::istream &
1718operator>>(std::istream &in, ReferenceCell<0> &reference_cell);
1719#endif
1720
Implementation of the classic affine transformation mapping used for simplices.
Definition mapping_p1.h:71
Abstract base class for mapping classes.
Definition mapping.h:318
Definition point.h:111
constexpr numbers::NumberTraits< Number >::real_type distance_square(const Point< dim, Number > &p) const
static constexpr Point< dim, Number > unit_vector(const unsigned int i)
unsigned int unv_vertex_to_deal_vertex(const unsigned int vertex_n) const
const Quadrature< dim > & get_nodal_type_quadrature() const
unsigned int ucd_vertex_to_deal_vertex(const unsigned int vertex_n) const
unsigned int vtk_quadratic_type() const
Point< dim > closest_point(const Point< dim > &p) const
unsigned int vtk_lagrange_type() const
Quadrature< dim > get_gauss_type_quadrature(const unsigned n_points_1d) const
unsigned int vtk_vertex_to_deal_vertex(const unsigned int vertex_index) const
std::pair< unsigned int, RefinementCase< dim - 1 > > equivalent_refinement_case(const types::geometric_orientation combined_face_orientation, const internal::SubfaceCase< dim > subface_case, const unsigned int subface_no) const
std::unique_ptr< Mapping< dim, spacedim > > get_default_mapping(const unsigned int degree) const
std::string to_string() const
unsigned int vtk_lexicographic_to_node_index(const std::array< unsigned, dim > &node_indices, const std::array< unsigned, dim > &nodes_per_direction, const bool legacy_format) const
unsigned int gmsh_element_type() const
unsigned int exodusii_vertex_to_deal_vertex(const unsigned int vertex_n) const
const Mapping< dim, spacedim > & get_default_linear_mapping() const
Point< dim > subface_vertex_location(const unsigned int face_no, const unsigned int subface_no, const unsigned int subface_vertex_no, const RefinementCase< dim - 1 > face_refinement_case) const
unsigned int exodusii_face_to_deal_face(const unsigned int face_n) const
unsigned int vtk_linear_type() const
constexpr numbers::NumberTraits< Number >::real_type norm_square() const
#define DEAL_II_NAMESPACE_OPEN
Definition config.h:38
#define DEAL_II_NAMESPACE_CLOSE
Definition config.h:39
DerivativeForm< 1, spacedim, dim, Number > transpose(const DerivativeForm< 1, dim, spacedim, Number > &DF)
#define DEAL_II_ASSERT_UNREACHABLE()
#define DEAL_II_NOT_IMPLEMENTED()
Point< 2 > first
Definition grid_out.cc:4639
unsigned int vertex_indices[2]
const unsigned int v0
const unsigned int v1
static ::ExceptionBase & ExcIO()
static ::ExceptionBase & ExcNotImplemented()
#define Assert(cond, exc)
#define AssertIndexRange(index, range)
static ::ExceptionBase & ExcInternalError()
static ::ExceptionBase & ExcMessage(std::string arg1)
#define AssertThrow(cond, exc)
void shift(const Tensor< 1, spacedim > &shift_vector, Triangulation< dim, spacedim > &triangulation)
constexpr char V
constexpr ReferenceCell< 3 > Hexahedron
constexpr ReferenceCell< 2 > Quadrilateral
constexpr ReferenceCell< 1 > Line
constexpr ReferenceCell< 2 > Triangle
constexpr ReferenceCell< 3 > Tetrahedron
constexpr ReferenceCell< 3 > Pyramid
constexpr ReferenceCell< 3 > Wedge
constexpr ReferenceCell< 0 > Vertex
std::tuple< bool, bool, bool > split_face_orientation(const types::geometric_orientation combined_orientation)
constexpr unsigned int invalid_unsigned_int
Definition types.h:228
constexpr types::geometric_orientation reverse_line_orientation
Definition types.h:355
constexpr types::geometric_orientation default_geometric_orientation
Definition types.h:342
::VectorizedArray< Number, width > abs(const ::VectorizedArray< Number, width > &)
std::uint8_t geometric_orientation
Definition types.h:38
typename internal::ndarray::HelperArray< T, Ns... >::type ndarray
Definition ndarray.h:105
std::istream & operator>>(std::istream &in, ReferenceCell< dim > &reference_cell)
std::ostream & operator<<(std::ostream &out, const ReferenceCell< dim > &reference_cell)
static unsigned int child_cell_on_face(const RefinementCase< dim > &ref_case, const unsigned int face, const unsigned int subface, const bool face_orientation=true, const bool face_flip=false, const bool face_rotation=false, const RefinementCase< dim - 1 > &face_refinement_case=RefinementCase< dim - 1 >::isotropic_refinement)
constexpr SymmetricTensor< 2, dim, Number > invert(const SymmetricTensor< 2, dim, Number > &)