FEAT 3
Finite Element Analysis Toolbox
Loading...
Searching...
No Matches
adaptive_mesh_layer.hpp
1// FEAT3: Finite Element Analysis Toolbox, Version 3
2// Copyright (C) 2010 by Stefan Turek & the FEAT group
3// FEAT3 is released under the GNU General Public License version 3,
4// see the file 'copyright.txt' in the top level directory for details.
5
6#pragma once
7
8#include "kernel/geometry/common_factories.hpp"
9#include "kernel/geometry/mesh_permutation.hpp"
10#include "kernel/shape.hpp"
11#include "kernel/util/string.hpp"
12#include <iterator>
13#include <kernel/adjacency/permutation.hpp>
15#include <kernel/geometry/adaptive_mesh.hpp>
16#include <kernel/util/tiny_algebra.hpp>
17
18#include <memory>
19#include <optional>
20#include <utility>
21
22namespace FEAT::Geometry
23{
24
35 template<typename AdaptiveMeshType_>
37 {
38 public:
40 using AdaptiveMeshType = AdaptiveMeshType_;
41
43 static constexpr int num_coords = AdaptiveMeshType::world_dim;
44
46 using CoordType = typename AdaptiveMeshType::CoordType;
47
49 using VertexType = typename AdaptiveMeshType::VertexType;
50
51 private:
53 std::shared_ptr<AdaptiveMeshType> _mesh;
54
55 // Mesh layer we refer to
56 Layer _layer;
57
58 // Vertex permutation
59 // Stored, because we can not apply it to the vertex set directly, as the
60 // underlying memory is shared between all AdaptiveVertexSets.
61 // Stored as an optional to avoid allocating a permutation if no permutation
62 // is required by the user.
63 std::optional<Adjacency::Permutation> _perm;
64
65 public:
72 AdaptiveVertexSet(std::shared_ptr<AdaptiveMeshType> mesh, Layer layer) : _mesh(std::move(mesh)), _layer(layer)
73 {
74 }
75
84 {
85 return AdaptiveVertexSet(_mesh, _layer);
86 }
87
88 std::size_t bytes() const
89 {
90 // We are just a view into data stored elsewhere.
91 return 0;
92 }
93
97 int get_num_coords() const
98 {
99 return num_coords;
100 }
101
106 {
107 return _mesh->get_num_entities(_layer, 0);
108 }
109
118 {
119 if(_perm)
120 {
121 return _mesh->vertex(_layer, _perm.value().map(idx));
122 }
123 return _mesh->vertex(_layer, idx);
124 }
125
127 const VertexType& operator[](Index idx) const
128 {
129 if(_perm)
130 {
131 return _mesh->vertex(_layer, _perm.value().map(idx));
132 }
133 return _mesh->vertex(_layer, idx);
134 }
135
136 // NOTE: Unsupported on purpose. Transform underlying mesh if required.
137 //void transform(const VertexType& origin, const VertexType& angles, const VertexType& offset)
138
151 void permute(const Adjacency::Permutation& perm, bool invert = false)
152 {
153 if(!_perm)
154 {
155 _perm = std::optional{invert ? perm.inverse() : perm.clone()};
156 }
157 else
158 {
159 if(invert)
160 {
161 _perm.value().concat(perm.inverse());
162 }
163 else
164 {
165 _perm.value().concat(perm);
166 }
167 }
168 }
169
170 static String name()
171 {
172 return "AdaptiveVertexSet<...>";
173 }
174 };
175
186 template<typename AdaptiveMeshType_, int cell_dim_, int face_dim_>
188 {
189 public:
191 using AdaptiveMeshType = AdaptiveMeshType_;
192
193 private:
195 std::shared_ptr<AdaptiveMeshType> _mesh;
201 std::optional<Adjacency::Permutation> _inv_perm;
202
203 public:
204 // Wrapper Code
205
207 static constexpr int num_indices = Shape::FaceTraits<Shape::Hypercube<cell_dim_>, face_dim_>::count;
208
216 AdaptiveIndexTuple(std::shared_ptr<AdaptiveMeshType> mesh, Layer layer, Index entity_idx) :
217 _mesh(std::move(mesh)),
218 _layer(layer),
219 _entity_idx(entity_idx),
220 _inv_perm(std::nullopt)
221 {
222 }
223
226
227 AdaptiveIndexTuple& operator=(const AdaptiveIndexTuple& other) = default;
228 AdaptiveIndexTuple& operator=(AdaptiveIndexTuple&& other) = default;
229
231 Index operator[](int idx) const
232 {
233 ASSERT(idx >= 0);
234 ASSERT(idx < num_indices);
235 Index result = _mesh->template get_face_index<cell_dim_, face_dim_>(_layer, _entity_idx, idx);
236 if(_inv_perm)
237 {
238 return _inv_perm.value().map(result);
239 }
240 return result;
241 }
242
255 {
256 if(_inv_perm)
257 {
258 _inv_perm.value().concat(inv_perm);
259 }
260 else
261 {
262 _inv_perm = inv_perm.clone();
263 }
264 }
265 };
266
277 template<typename AdaptiveMeshType_, int cell_dim_, int face_dim_>
279 {
280 public:
282 using AdaptiveMeshType = AdaptiveMeshType_;
283
285 static constexpr int num_indices = Shape::FaceTraits<Shape::Hypercube<cell_dim_>, face_dim_>::count;
286
289 {
290 // Adaptive mesh reference
291 std::shared_ptr<AdaptiveMeshType> _mesh;
292 Layer _layer;
293
294 // Iterator state
295 Index _domain_node;
296 Index _next;
297
298 public:
299 AdaptiveIndexSetAdjactor(std::shared_ptr<AdaptiveMeshType> mesh, Layer layer, Index domain_node, Index next) :
300 _mesh(std::move(mesh)),
301 _layer(layer),
302 _domain_node(domain_node),
303 _next(next)
304 {
305 }
306
307 bool operator==(const AdaptiveIndexSetAdjactor& other) const
308 {
309 return _domain_node == other._domain_node && _next == other._next && _layer == other._layer &&
310 _mesh == other._mesh;
311 }
312
313 bool operator!=(const AdaptiveIndexSetAdjactor& other) const
314 {
315 return !(*this == other);
316 }
317
318 AdaptiveIndexSetAdjactor& operator++()
319 {
320 _next++;
321 return *this;
322 }
323
324 Index operator*() const
325 {
326 return _mesh->template get_face_index<cell_dim_, face_dim_>(_layer, _domain_node, _next);
327 }
328 };
329
332
335
336 private:
337 // Mesh reference
338 std::shared_ptr<AdaptiveMeshType> _mesh;
339 Layer _layer;
340
341 std::optional<Adjacency::Permutation> _permutation;
342 std::optional<Adjacency::Permutation> _inverse_face_permutation;
343
344 public:
351 AdaptiveIndexSet(std::shared_ptr<AdaptiveMeshType> mesh, Layer layer) : _mesh(std::move(mesh)), _layer(layer)
352 {
353 }
354
355 AdaptiveIndexSet(const AdaptiveIndexSet& other) = default;
357
358 AdaptiveIndexSet& operator=(const AdaptiveIndexSet& other) = default;
359 AdaptiveIndexSet& operator=(AdaptiveIndexSet&& other) = default;
360
369 {
370 return AdaptiveIndexSet(_mesh, _layer);
371 }
372
376 std::size_t bytes() const
377 {
378 return 0;
379 }
380
383 {
384 if(_permutation)
385 {
386 return _mesh->template get_face_index<cell_dim_, face_dim_>(
387 _layer,
388 _permutation.value().map(i),
389 _inverse_face_permutation.value().map(j)
390 );
391 }
392 return _mesh->template get_face_index<cell_dim_, face_dim_>(_layer, i, j);
393 }
394
396 Index operator()(Index i, int j) const
397 {
398 if(_permutation)
399 {
400 return _mesh->template get_face_index<cell_dim_, face_dim_>(
401 _layer,
402 _permutation.value().map(i),
403 _inverse_face_permutation.value().map(j)
404 );
405 }
406 return _mesh->template get_face_index<cell_dim_, face_dim_>(_layer, i, j);
407 }
408
417 {
419 if(_permutation)
420 {
421 auto tuple = IndexTupleType(_mesh, _layer, _permutation.value().map(i));
422 tuple.permute_map(_inverse_face_permutation.value());
423 return tuple;
424 }
425 return IndexTupleType(_mesh, _layer, i);
426 }
427
430 {
432 if(_permutation)
433 {
434 auto tuple = IndexTupleType(_mesh, _layer, _permutation.value().map(i));
435 tuple.permute_map(_inverse_face_permutation.value());
436 return tuple;
437 }
438 return IndexTupleType(_mesh, _layer, i);
439 }
440
445 {
446 return _mesh->get_num_entities(_layer, cell_dim_);
447 }
448
452 int get_num_indices() const
453 {
454 return num_indices;
455 }
456
461 {
462 return _mesh->get_num_entities(_layer, face_dim_);
463 }
464
465 // Unsupported.
466 // IndexSet API depends on pointer arithmetic that we can not support.
467 // Rewrite IndexSet API first, if this is required somewhere
468 //AdaptiveIndexTuple* get_indices()
469 //const AdaptiveIndexTuple* get_indices() const
470
471 void permute(const Adjacency::Permutation& perm, const Adjacency::Permutation& inv_perm_face)
472 {
473 // Permute index tuples
474 if(_permutation)
475 {
476 _permutation.value().concat(perm);
477 }
478 else
479 {
480 _permutation = perm.clone();
481 }
482
483 // Permute indices by the inverse face permutation
484 if(_inverse_face_permutation)
485 {
486 _inverse_face_permutation.value().concat(inv_perm_face);
487 }
488 else
489 {
490 _inverse_face_permutation = inv_perm_face.clone();
491 }
492 }
493
494 static String name()
495 {
496 return "AdaptiveIndexSet<" + stringify(cell_dim_) + ", " + stringify(face_dim_) + ">";
497 }
498
499 // Adjactor Interface
500
501 Index get_num_nodes_domain() const
502 {
503 return get_num_entities();
504 }
505
506 Index get_num_nodes_image() const
507 {
508 return _mesh->get_num_entities(_layer, face_dim_);
509 }
510
511 AdaptiveIndexSetAdjactor image_begin(Index domain_node) const
512 {
513 return AdaptiveIndexSetAdjactor(_mesh, _layer, domain_node, 0);
514 }
515
516 AdaptiveIndexSetAdjactor image_end(Index domain_node) const
517 {
518 return AdaptiveIndexSetAdjactor(_mesh, _layer, domain_node, num_indices);
519 }
520 };
521
522 template<
523 typename AdaptiveMeshType_,
524 typename Shape_,
525 int face_dim_ = Shape_::dimension - 1>
527 public AdaptiveIndexSetWrapper<AdaptiveMeshType_, Shape_, face_dim_ - 1>
528 {
529 static_assert(face_dim_ < Shape_::dimension, "invalid face dimension");
530 static_assert(face_dim_ > 0, "invalid face dimension");
531
532 public:
533 using AdaptiveMeshType = AdaptiveMeshType_;
534
535 static constexpr int cell_dim = Shape_::dimension;
536
537 using BaseClass =
538 AdaptiveIndexSetWrapper<AdaptiveMeshType_, Shape_, face_dim_ - 1>;
539
540 using IndexSetType =
542
543
544 protected:
545 template<int face_dim__>
548
549 std::shared_ptr<AdaptiveMeshType> _mesh;
550 Layer _layer;
551
552 IndexSetType _index_set;
553
554 public:
555 AdaptiveIndexSetWrapper(std::shared_ptr<AdaptiveMeshType> mesh, Layer layer) :
556 BaseClass(mesh, layer),
557 _mesh(mesh),
558 _layer(layer),
559 _index_set(mesh, layer)
560 {
561 }
562
565
566 AdaptiveIndexSetWrapper& operator=(const AdaptiveIndexSetWrapper& other) = default;
568
569 void clone(const AdaptiveIndexSetWrapper& other)
570 {
571 BaseClass::clone(other);
572 _index_set = other._index_set;
573 }
574
575 AdaptiveIndexSetWrapper clone() const
576 {
577 return AdaptiveIndexSetWrapper(_mesh, _layer);
578 }
579
580 template<int face_dim__>
582 {
583 static_assert(face_dim__ >= 0, "invalid face dimension");
584 static_assert(face_dim__ < Shape_::dimension, "invalid face dimension");
585
587 }
588
589 template<int face_dim__>
590 const AdaptiveIndexSetByFaceDim<face_dim__>& get_index_set() const
591 {
592 static_assert(face_dim__ >= 0, "invalid face dimension");
593 static_assert(face_dim__ < Shape_::dimension, "invalid face dimension");
594
596 }
597
598 template<std::size_t np_>
599 void permute(const Adjacency::Permutation& shape_perm,
600 const std::array<Adjacency::Permutation, np_>& inv_perm)
601 {
602 BaseClass::permute(shape_perm, inv_perm);
603 _index_set.permute(shape_perm, inv_perm.at(face_dim_));
604 }
605
606 static String name()
607 {
608 return "AdaptiveIndexSetWrapper<" + Shape_::name() + ", " + stringify(face_dim_) + ">";
609 }
610
611 std::size_t bytes()
612 {
613 return BaseClass::bytes() + _index_set.bytes();
614 }
615 };
616
617 template<typename AdaptiveMeshType_, typename Shape_>
618 class AdaptiveIndexSetWrapper<AdaptiveMeshType_, Shape_, 0>
619 {
620 static_assert(Shape_::dimension > 0, "invalid shape dimension");
621
622 public:
623 using AdaptiveMeshType = AdaptiveMeshType_;
624
625 static constexpr int cell_dim = Shape_::dimension;
626
627 using IndexSetType =
629
630
631 protected:
632 template<int face_dim__>
635
636 std::shared_ptr<AdaptiveMeshType> _mesh;
637 Layer _layer;
638
639 IndexSetType _index_set;
640
641 public:
642 AdaptiveIndexSetWrapper(std::shared_ptr<AdaptiveMeshType> mesh, Layer layer) :
643 _mesh(mesh),
644 _layer(layer),
645 _index_set(mesh, layer)
646 {
647 }
648
651
652 AdaptiveIndexSetWrapper& operator=(const AdaptiveIndexSetWrapper& other) = default;
654
655 void clone(const AdaptiveIndexSetWrapper& other)
656 {
657 _index_set = other._index_set;
658 }
659
660 AdaptiveIndexSetWrapper clone() const
661 {
662 return AdaptiveIndexSetWrapper(_mesh, _layer);
663 }
664
665 template<int face_dim__>
667 {
668 static_assert(face_dim__ == 0, "invalid face dimension");
669
671 }
672
673 template<int face_dim__>
674 const AdaptiveIndexSetByFaceDim<face_dim__>& get_index_set() const
675 {
676 static_assert(face_dim__ == 0, "invalid face dimension");
677
679 }
680
681 template<std::size_t np_>
682 void permute(const Adjacency::Permutation& shape_perm,
683 const std::array<Adjacency::Permutation, np_>& inv_perm)
684 {
685 _index_set.permute(shape_perm, inv_perm.at(0));
686 }
687
688 static String name()
689 {
690 return "AdaptiveIndexSetWrapper<" + Shape_::name() + ",0>";
691 }
692
693 std::size_t bytes() const
694 {
695 return _index_set.bytes();
696 }
697 };
698
709 template<typename AdaptiveMeshType_, typename Shape_>
712 AdaptiveMeshType_,
713 typename Shape::FaceTraits<Shape_, Shape_::dimension - 1>::ShapeType>
714 {
716 using AdaptiveMeshType = AdaptiveMeshType_;
717
719 AdaptiveMeshType_,
720 typename Shape::FaceTraits<Shape_, Shape_::dimension - 1>::ShapeType>;
721
723
724 protected:
725 template<int shape_dim_>
730
731 std::shared_ptr<AdaptiveMeshType> _mesh;
732 Layer _layer;
733
734 IndexSetWrapperType _index_set_wrapper;
735
736 public:
737 AdaptiveIndexSetHolder(std::shared_ptr<AdaptiveMeshType> mesh, Layer layer) :
738 BaseClass(mesh, layer),
739 _mesh(mesh),
740 _layer(layer),
741 _index_set_wrapper(mesh, layer)
742 {
743 }
744
747
748 AdaptiveIndexSetHolder& operator=(const AdaptiveIndexSetHolder& other) = default;
750
751 void clone(const AdaptiveIndexSetHolder& other)
752 {
753 BaseClass::clone(other);
754 _index_set_wrapper.clone(other._index_set_wrapper);
755 }
756
757 AdaptiveIndexSetHolder clone() const
758 {
759 return AdaptiveIndexSetHolder(_mesh, _layer);
760 }
761
762 template<int shape_dim_>
763 AdaptiveIndexSetWrapperByShapeDim<shape_dim_>& get_index_set_wrapper()
764 {
765 static_assert(shape_dim_ > 0, "invalid shape dimension");
766 static_assert(shape_dim_ <= Shape_::dimension, "invalid shape dimension");
767 typedef typename Shape::FaceTraits<Shape_, shape_dim_>::ShapeType ShapeType;
768 return AdaptiveIndexSetHolder<AdaptiveMeshType, ShapeType>::_index_set_wrapper;
769 }
770
771 template<int shape_dim_>
772 const AdaptiveIndexSetWrapperByShapeDim<shape_dim_>& get_index_set_wrapper() const
773 {
774 static_assert(shape_dim_ > 0, "invalid shape dimension");
775 static_assert(shape_dim_ <= Shape_::dimension, "invalid shape dimension");
776 typedef typename Shape::FaceTraits<Shape_, shape_dim_>::ShapeType ShapeType;
777 return AdaptiveIndexSetHolder<AdaptiveMeshType, ShapeType>::_index_set_wrapper;
778 }
779
780 template<int cell_dim_, int face_dim_>
781 AdaptiveIndexSet<AdaptiveMeshType, cell_dim_, face_dim_>& get_index_set()
782 {
783 static_assert(cell_dim_ <= Shape_::dimension, "invalid cell dimension");
784 static_assert(face_dim_ < cell_dim_, "invalid face/cell dimension");
785 static_assert(face_dim_ >= 0, "invalid face dimension");
786 return get_index_set_wrapper<cell_dim_>().template get_index_set<face_dim_>();
787 }
788
789 template<int cell_dim_, int face_dim_>
790 const AdaptiveIndexSet<AdaptiveMeshType, cell_dim_, face_dim_>& get_index_set() const
791 {
792 static_assert(cell_dim_ <= Shape_::dimension, "invalid cell dimension");
793 static_assert(face_dim_ < cell_dim_, "invalid face/cell dimension");
794 static_assert(face_dim_ >= 0, "invalid face dimension");
795 return get_index_set_wrapper<cell_dim_>().template get_index_set<face_dim_>();
796 }
797
798 template<std::size_t np_>
799 void permute(const std::array<Adjacency::Permutation, np_>& perms,
800 const std::array<Adjacency::Permutation, np_>& inv_perms)
801 {
802 BaseClass::permute(perms, inv_perms);
803 _index_set_wrapper.permute(perms.at(Shape_::dimension), inv_perms);
804 }
805
806 static String name()
807 {
808 return "AdaptiveIndexSetHolder<" + Shape_::name() + ">";
809 }
810
811 std::size_t bytes() const
812 {
813 return BaseClass::bytes() + _index_set_wrapper.bytes();
814 }
815 };
816
817 template<typename AdaptiveMeshType_>
818 class AdaptiveIndexSetHolder<AdaptiveMeshType_, Shape::Vertex>
819 {
820 public:
821 AdaptiveIndexSetHolder() = default;
822
823 AdaptiveIndexSetHolder(std::shared_ptr<AdaptiveMeshType_> /*mesh*/, Layer /*layer*/){
824 }
825
828
829 AdaptiveIndexSetHolder& operator=(const AdaptiveIndexSetHolder& other) = default;
831
832 void clone(const AdaptiveIndexSetHolder& /*unused*/)
833 {
834 }
835
836 AdaptiveIndexSetHolder clone() const
837 {
838 return AdaptiveIndexSetHolder();
839 }
840
841 template<std::size_t np_>
842 void permute(const std::array<Adjacency::Permutation, np_>&,
843 const std::array<Adjacency::Permutation, np_>&)
844 {
845 }
846
847 static String name()
848 {
849 return "AdaptiveIndexSetHolder<Vertex>";
850 }
851
852 std::size_t bytes() const
853 {
854 return std::size_t(0);
855 }
856 };
857
861 template<typename AdaptiveMeshType_>
863 {
864 protected:
865 using AdaptiveMeshType = AdaptiveMeshType_;
866
867 std::shared_ptr<AdaptiveMeshType> _mesh;
868 Layer _layer;
869
870 public:
871
873 static constexpr int num_indices =
874 Shape::FaceTraits<typename AdaptiveMeshType::ShapeType, AdaptiveMeshType_::shape_dim - 1>::count;
875
878 {
879 // Adaptive mesh reference
880 std::shared_ptr<AdaptiveMeshType> _mesh;
881 Layer _layer;
882
883 // Iterator state
884 Index _domain_node;
885 Index _next;
886
887 std::optional<Adjacency::Permutation> _inverse_face_permutation = std::nullopt;
888
889 public:
890 AdaptiveNeighborsAdjactor(std::shared_ptr<AdaptiveMeshType> mesh, Layer layer, Index domain_node, Index next) :
891 _mesh(std::move(mesh)),
892 _layer(layer),
893 _domain_node(domain_node),
894 _next(next)
895 {
896 }
897
899 std::shared_ptr<AdaptiveMeshType> mesh,
900 Layer layer,
901 Index domain_node,
902 Index next,
903 Adjacency::Permutation inverse_face_permutation) :
904 _mesh(std::move(mesh)),
905 _layer(layer),
906 _domain_node(domain_node),
907 _next(next),
908 _inverse_face_permutation(std::move(inverse_face_permutation))
909 {
910 }
911
912 bool operator==(const AdaptiveNeighborsAdjactor& other) const
913 {
914 return _domain_node == other._domain_node && _next == other._next && _layer == other._layer &&
915 _mesh == other._mesh;
916 }
917
918 bool operator!=(const AdaptiveNeighborsAdjactor& other) const
919 {
920 return !(*this == other);
921 }
922
923 AdaptiveNeighborsAdjactor& operator++()
924 {
925 _next++;
926 return *this;
927 }
928
929 Index operator*() const
930 {
931 if(_inverse_face_permutation)
932 {
933 return _mesh->get_neighbor(_layer, _domain_node, _inverse_face_permutation.value().map(_next));
934 }
935 return _mesh->get_neighbor(_layer, _domain_node, _next);
936 }
937 };
938
941
942 private:
943 // Mesh reference
944 std::optional<Adjacency::Permutation> _permutation;
945 std::optional<Adjacency::Permutation> _inverse_face_permutation;
946
947 public:
954 AdaptiveNeighbors(std::shared_ptr<AdaptiveMeshType> mesh, Layer layer) : _mesh(std::move(mesh)), _layer(layer)
955 {
956 }
957
958 AdaptiveNeighbors(const AdaptiveNeighbors& other) = default;
960
961 AdaptiveNeighbors& operator=(const AdaptiveNeighbors& other) = default;
962 AdaptiveNeighbors& operator=(AdaptiveNeighbors&& other) = default;
963
972 {
973 return AdaptiveNeighbors(_mesh, _layer);
974 }
975
979 std::size_t bytes() const
980 {
981 return 0;
982 }
983
986 {
987 if(_permutation)
988 {
989 return _mesh->get_neighbor(
990 _layer,
991 _permutation.value().map(i),
992 Index(_inverse_face_permutation.value().map(j))
993 );
994 }
995 return _mesh->get_neighbor(_layer, i, Index(j));
996 }
997
999 Index operator()(Index i, int j) const
1000 {
1001 if(_permutation)
1002 {
1003 return _mesh->get_neighbor(
1004 _layer,
1005 _permutation.value().map(i),
1006 Index(_inverse_face_permutation.value().map(j))
1007 );
1008 }
1009 return _mesh->get_neighbor(_layer, i, Index(j));
1010 }
1011
1012 IndexTuple<num_indices> operator[](Index i) const
1013 {
1015 for(int j(0); j < num_indices; j++)
1016 {
1017 result[j] = (*this)(i, j);
1018 }
1019 return result;
1020 }
1021
1026 {
1027 return _mesh->get_num_entities(_layer, AdaptiveMeshType::ShapeType::dimension);
1028 }
1029
1034 {
1035 return num_indices;
1036 }
1037
1042 {
1043 return _mesh->get_num_entities(_layer, AdaptiveMeshType::ShapeType::dimension);
1044 }
1045
1046 // Unsupported.
1047 // IndexSet API depends on pointer arithmetic that we can not support.
1048 // Rewrite IndexSet API first, if this is required somewhere
1049 //AdaptiveIndexTuple* get_indices()
1050 //const AdaptiveIndexTuple* get_indices() const
1051
1052 void permute(const Adjacency::Permutation& perm, const Adjacency::Permutation& inv_perm_face)
1053 {
1054 // Permute index tuples
1055 if(_permutation)
1056 {
1057 _permutation.value().concat(perm);
1058 }
1059 else
1060 {
1061 _permutation = perm.clone();
1062 }
1063
1064 // Permute indices by the inverse face permutation
1065 if(_inverse_face_permutation)
1066 {
1067 _inverse_face_permutation.value().concat(inv_perm_face);
1068 }
1069 else
1070 {
1071 _inverse_face_permutation = inv_perm_face.clone();
1072 }
1073 }
1074
1075 static String name()
1076 {
1077 return "AdaptiveNeighbors";
1078 }
1079
1080 // Adjactor Interface
1081
1082 Index get_num_nodes_domain() const
1083 {
1084 return get_num_entities();
1085 }
1086
1087 Index get_num_nodes_image() const
1088 {
1089 return _mesh->get_num_entities(_layer, AdaptiveMeshType::ShapeType::dimension);
1090 }
1091
1092 AdaptiveNeighborsAdjactor image_begin(Index domain_node) const
1093 {
1094 if(_permutation)
1095 {
1096 return AdaptiveIndexSetAdjactor(_mesh, _layer, _permutation.value().map(domain_node), 0, _inverse_face_permutation.value());
1097 }
1098 return AdaptiveIndexSetAdjactor(_mesh, _layer, domain_node, 0);
1099 }
1100
1101 AdaptiveNeighborsAdjactor image_end(Index domain_node) const
1102 {
1103 if(_permutation)
1104 {
1105 return AdaptiveIndexSetAdjactor(_mesh, _layer, _permutation.value().map(domain_node), num_indices, _inverse_face_permutation.value());
1106 }
1107 return AdaptiveIndexSetAdjactor(_mesh, _layer, domain_node, num_indices);
1108 }
1109 };
1110
1121 template<typename AdaptiveMeshType_>
1123 {
1124 public:
1126 using AdaptiveMeshType = AdaptiveMeshType_;
1127
1130
1131 // Conformal Mesh Interface
1132
1134 using ShapeType = typename AdaptiveMeshType::ShapeType;
1135
1138
1140 using VertexType = typename AdaptiveMeshType::VertexType;
1141
1143 using CoordType = typename AdaptiveMeshType::CoordType;
1144
1147
1149 static constexpr int shape_dim = AdaptiveMeshType::shape_dim;
1150
1152 static constexpr int world_dim = AdaptiveMeshType::world_dim;
1153
1155 static constexpr bool is_structured = false;
1156
1159
1161 template<int cell_dim_, int face_dim_>
1163 {
1165 };
1166
1167 private:
1168 std::shared_ptr<AdaptiveMeshType> _mesh;
1169 Layer _layer;
1170
1171 VertexSetType _vertex_set;
1172 IndexSetHolderType _index_set_holder;
1173
1174 MeshPermutation<ShapeType> _permutation;
1175
1176 public:
1183 AdaptiveMeshLayer(std::shared_ptr<AdaptiveMeshType> mesh, Layer layer) :
1184 _mesh(mesh),
1185 _layer(layer),
1186 _vertex_set(VertexSetType(_mesh, layer)),
1187 _index_set_holder(_mesh, layer),
1188 _permutation()
1189 {
1190 }
1191
1192 AdaptiveMeshLayer(const AdaptiveMeshLayer& other) = default;
1194
1195 AdaptiveMeshLayer& operator=(const AdaptiveMeshLayer& other) = default;
1196 AdaptiveMeshLayer& operator=(AdaptiveMeshLayer&& other) = default;
1197
1198 void clone(const AdaptiveMeshLayer& other)
1199 {
1200 _mesh = other._mesh;
1201 _layer = other._layer;
1202 _vertex_set = other._vertex_set;
1203 _index_set_holder = other._index_set_holder;
1204 _permutation = other._permutation.clone();
1205 }
1206
1207 AdaptiveMeshLayer clone() const
1208 {
1209 AdaptiveMeshLayer result(_mesh, _layer);
1210 result._vertex_set = _vertex_set;
1211 result._index_set_holder = _index_set_holder;
1212 result._permutation = _permutation;
1213 return result;
1214 }
1215
1216 std::size_t bytes() const
1217 {
1218 return _vertex_set.bytes() + _index_set_holder.bytes() + _permutation.bytes();
1219 }
1220
1227 {
1228 return _mesh->get_num_entities(_layer, dim);
1229 }
1230
1235 {
1236 return get_num_entities(0);
1237 }
1238
1243 {
1244 return get_num_entities(ShapeType::dimension);
1245 }
1246
1252 bool is_permuted() const
1253 {
1254 return !_permutation.empty();
1255 }
1256
1261 {
1262 return _permutation;
1263 }
1264
1283 {
1284 // make sure that we don't already have a permutation
1285 XASSERTM(this->_permutation.empty(), "mesh is already permuted!");
1286
1287 // create the permutation
1288 this->_permutation.create(strategy, this->_index_set_holder, this->_vertex_set);
1289
1290 // permute vertex set
1291 this->_vertex_set.permute(this->_permutation.get_perm(0));
1292
1293 // permute index sets
1294 this->_index_set_holder.permute(this->_permutation.get_perms(), this->_permutation.get_inv_perms());
1295 }
1296
1314 {
1315 // make sure that we don't already have a permutation
1316 XASSERTM(this->_permutation.empty(), "mesh is already permuted!");
1317
1318 // check the dimensions
1319 const std::array<Index, 4> mesh_size {
1324 };
1325 XASSERTM(mesh_perm.validate_sizes(mesh_size.data()) == 0, "mesh permutation has invalid size!");
1326
1327 // save the permutation
1328 this->_permutation = std::forward<MeshPermutationType>(mesh_perm);
1329
1330 // permute vertex set
1331 this->_vertex_set.permute(this->_permutation.get_perm(0));
1332
1333 // permute index sets
1334 this->_index_set_holder.permute(this->_permutation.get_perms(), this->_permutation.get_inv_perms());
1335 }
1336
1346 {
1347 // no coloring?
1348 const std::vector<Index>& coloring = this->get_mesh_permutation().get_element_coloring();
1349 if(coloring.empty())
1350 return true;
1351
1352 // get vertices-at-element index set
1353 const auto& verts_at_elem = this->template get_index_set<shape_dim, 0>();
1354
1355 // render transpose
1356 Adjacency::Graph elems_at_vert(Adjacency::RenderType::transpose, verts_at_elem);
1357
1358 // loop over all color blocks
1359 for(std::size_t icol(0); icol+1u < coloring.size(); ++icol)
1360 {
1361 // get the bounds of our current color block
1362 const Index iel_beg = coloring[icol];
1363 const Index iel_end = coloring[icol+1u];
1364
1365 // loop over all elements in the current color block
1366 for(Index iel(iel_beg); iel < iel_end; ++iel)
1367 {
1368 // loop over all vertices adjacent to this element
1369 for(int ivt(0); ivt < verts_at_elem.num_indices; ++ivt)
1370 {
1371 // loop over all elements adjacent to this vertex
1372 const Index ivtx = verts_at_elem(iel, ivt);
1373 for(auto it = elems_at_vert.image_begin(ivtx); it != elems_at_vert.image_end(ivtx); ++it)
1374 {
1375 // two adjacent element must not be in the same color block
1376 if((iel_beg <= *it) && (*it < iel_end) && (*it != iel))
1377 return false; // invalid coloring
1378 }
1379 }
1380 }
1381 } // next color block
1382
1383 // ok, coloring is valid
1384 return true;
1385 }
1386
1396 {
1397 // no layering?
1398 const std::vector<Index>& layering = this->get_mesh_permutation().get_element_layering();
1399 if(layering.empty())
1400 return true;
1401
1402 // get vertices-at-element index set
1403 const auto& verts_at_elem = this->template get_index_set<shape_dim, 0>();
1404
1405 // render transpose
1406 Adjacency::Graph elems_at_vert(Adjacency::RenderType::transpose, verts_at_elem);
1407
1408 // loop over all layers
1409 for(std::size_t ilay(0); ilay+1u < layering.size(); ++ilay)
1410 {
1411 // get the bounds of our current layer
1412 const Index iel_beg = layering[ilay];
1413 const Index iel_end = layering[ilay+1u];
1414
1415 // get the lower bound for valid neighbors of our current layer = beginning of previous layer
1416 const Index iel_lower = layering[Math::max(ilay, std::size_t(1)) - 1u];
1417
1418 // get the upper bound for valid neighbors of our current layer = end of next layer
1419 const Index iel_upper = layering[Math::min(ilay+2u, layering.size()-1u)];
1420
1421 // loop over all elements in the current layer
1422 for(Index iel(iel_beg); iel < iel_end; ++iel)
1423 {
1424 // loop over all vertices adjacent to this element
1425 for(int ivt(0); ivt < verts_at_elem.num_indices; ++ivt)
1426 {
1427 // loop over all elements adjacent to this vertex
1428 const Index ivtx = verts_at_elem(iel, ivt);
1429 for(auto it = elems_at_vert.image_begin(ivtx); it != elems_at_vert.image_end(ivtx); ++it)
1430 {
1431 // adjacent element outside of adjacent layers?
1432 if(!((iel_lower <= *it) && (*it < iel_upper)))
1433 return false; // invalid layer
1434 }
1435 }
1436 }
1437 } // next color block
1438
1439 // ok, layering is valid
1440 return true;
1441 }
1442
1443 void fill_neighbours()
1444 {
1445 _mesh->fill_neighbors();
1446 }
1447
1448 AdaptiveNeighborsType get_neighbors()
1449 {
1450 return AdaptiveNeighbors(_mesh, _layer);
1451 }
1452
1453 AdaptiveNeighborsType get_neighbors() const
1454 {
1455 return AdaptiveNeighbors(_mesh, _layer);
1456 }
1457
1462 {
1463 return _vertex_set;
1464 }
1465
1470 {
1471 return _vertex_set;
1472 }
1473
1480 template<int cell_dim_, int face_dim_>
1482 {
1483 return _index_set_holder.template get_index_set<cell_dim_, face_dim_>();
1484 }
1485
1492 template<int cell_dim_, int face_dim_>
1493 const auto& get_index_set() const
1494 {
1495 return _index_set_holder.template get_index_set<cell_dim_, face_dim_>();
1496 }
1497
1502 {
1503 return _index_set_holder;
1504 }
1505
1510 {
1511 return _index_set_holder;
1512 }
1513
1514 IndexSetHolderType& get_topology()
1515 {
1516 return _index_set_holder;
1517 }
1518
1519 const IndexSetHolderType& get_topology() const
1520 {
1521 return _index_set_holder;
1522 }
1523
1524 // Wrapper specific code
1525
1536 std::optional<Index> get_child(Index elem, Index child) const
1537 {
1538 return _mesh->template get_child<ShapeType::dimension>(_layer, elem, child);
1539 }
1540
1547 {
1548 return _mesh->template get_num_children<ShapeType::dimension>(_layer, elem);
1549 }
1550
1551 static String name()
1552 {
1553 return "AdaptiveMeshLayer<...>";
1554 }
1555
1561 bool has_vertex_changed(Index vertex_idx) const
1562 {
1563 return _mesh->has_vertex_changed(_layer, vertex_idx);
1564 }
1565
1567 typename AdaptiveMeshType::FoundationMeshType& foundation_mesh()
1568 {
1569 return _mesh->foundation_mesh();
1570 }
1571
1573 const typename AdaptiveMeshType::FoundationMeshType& foundation_mesh() const
1574 {
1575 return _mesh->foundation_mesh();
1576 }
1577
1580 {
1581 return *_mesh;
1582 }
1583
1586 {
1587 return *_mesh;
1588 }
1589
1591 std::shared_ptr<AdaptiveMeshType> adaptive_mesh_ptr() const
1592 {
1593 return _mesh;
1594 }
1595 };
1596
1602 template<typename AdaptiveMeshLayerType>
1604 {
1605 public:
1607 {
1608 public:
1609 ChildCellIterator(const AdaptiveMeshLayerType* coarse, Index cell, Index child) :
1610 _coarse(coarse),
1611 _cell(cell),
1612 _child(child)
1613 {
1614 }
1615
1616 ChildCellIterator() = default;
1617
1618 ChildCellIterator(const ChildCellIterator& other) = default;
1620
1621 ChildCellIterator& operator=(const ChildCellIterator& other) = default;
1622 ChildCellIterator& operator=(ChildCellIterator&& other) = default;
1623
1624 ~ChildCellIterator() = default;
1625
1626 Index operator*() const
1627 {
1628 return _coarse->get_child(_cell, _child).value_or(0);
1629 }
1630
1631 ChildCellIterator& operator++()
1632 {
1633 _child++;
1634 return *this;
1635 }
1636
1637 bool operator!=(const ChildCellIterator& other)
1638 {
1639 return _cell != other._cell || _child != other._child;
1640 }
1641
1642 private:
1643 static constexpr int dim = AdaptiveMeshLayerType::ShapeType::dimension;
1644
1645 const AdaptiveMeshLayerType* _coarse;
1646 Index _cell;
1647 Index _child;
1648 };
1649
1651
1652 private:
1653 // TODO: Should these be std::shared_ptrs?
1654 const AdaptiveMeshLayerType& _coarse;
1655 const AdaptiveMeshLayerType& _fine;
1656
1657 public:
1658 AdaptiveChildMapping(const AdaptiveMeshLayerType& coarse, const AdaptiveMeshLayerType& fine) : _coarse(coarse), _fine(fine)
1659 {
1660 }
1661
1662 Index get_num_nodes_domain() const
1663 {
1664 return _coarse.get_num_elements();
1665 }
1666
1667 Index get_num_nodes_image() const
1668 {
1669 return _fine.get_num_elements();
1670 }
1671
1672 ChildCellIterator image_begin(Index domain_node) const
1673 {
1674 return ChildCellIterator(&_coarse, domain_node, 0);
1675 }
1676
1677 ChildCellIterator image_end(Index domain_node) const
1678 {
1679 Index num_children = _coarse.get_num_children(domain_node);
1680 return ChildCellIterator(&_coarse, domain_node, num_children);
1681 }
1682 };
1683
1687 template<typename AdaptiveMeshType_, int dim_ = AdaptiveMeshType_::ShapeType::dimension>
1689 {
1690 const AdaptiveMeshType_& _amesh;
1691 public:
1693 {
1694 Index _idx;
1695 bool _done;
1696
1697 public:
1698
1699 using iterator_category = std::input_iterator_tag;
1700 using difference_type = std::size_t;
1701 using value_type = Index;
1702 using pointer = Index*;
1703 using reference = Index&;
1704
1705 OnceIterator() : _idx(0), _done(true)
1706 {
1707 }
1708
1709 explicit OnceIterator(Index i) : _idx(i), _done(false)
1710 {
1711 }
1712
1713 OnceIterator(const OnceIterator& other) = default;
1714 OnceIterator(OnceIterator&& other) = default;
1715
1716 OnceIterator& operator=(const OnceIterator& other) = default;
1717 OnceIterator& operator=(OnceIterator&& other) = default;
1718
1719 ~OnceIterator() = default;
1720
1721
1723 {
1724 return _idx;
1725 }
1726
1727 Index operator->()
1728 {
1729 return _idx;
1730 }
1731
1732 OnceIterator& operator++()
1733 {
1734 _done = true;
1735 return *this;
1736 }
1737
1738 OnceIterator& operator++(int)
1739 {
1740 OnceIterator tmp = *this;
1741 ++(*this);
1742 return tmp;
1743 }
1744
1745 friend bool operator==(const OnceIterator& a, const OnceIterator& b)
1746 {
1747 return (a._done && b._done);
1748 }
1749
1750 friend bool operator!=(const OnceIterator& a, const OnceIterator& b)
1751 {
1752 return !(a == b);
1753 }
1754 };
1755
1757
1758 explicit FoundationAdaptiveAdjactor(const AdaptiveMeshType_& amesh) : _amesh(amesh)
1759 {
1760 }
1761
1762 Index get_num_nodes_domain() const
1763 {
1764 return _amesh.foundation_mesh().get_num_entities(dim_);
1765 }
1766
1767 Index get_num_nodes_image() const
1768 {
1769 return _amesh.get_num_entities(Geometry::Layer{0}, dim_);
1770 }
1771
1772 ImageIterator image_begin(Index domain_idx) const
1773 {
1774 if(auto mapping = _amesh.template get_overlap_cell<dim_>(domain_idx))
1775 {
1776 return OnceIterator(*mapping);
1777 }
1778 else
1779 {
1780 return OnceIterator{};
1781 }
1782 }
1783
1784 ImageIterator image_end(Index /*domain_idx*/) const
1785 {
1786 return OnceIterator{};
1787 }
1788 };
1789} // namespace FEAT::Geometry
#define ASSERT(expr)
Debug-Assertion macro definition.
Definition: assertion.hpp:229
#define XASSERTM(expr, msg)
Assertion macro definition with custom message.
Definition: assertion.hpp:263
FEAT Kernel base header.
Adjacency Graph implementation.
Definition: graph.hpp:34
ImageIterator image_begin(Index domain_node) const
Returns an iterator for the first adjacent image node.
Definition: graph.hpp:964
ImageIterator image_end(Index domain_node) const
Returns an iterator for the first position past the last adjacent image node.
Definition: graph.hpp:972
void concat(const Permutation &p)
concatenates two permutations
Permutation inverse() const
Computes the inverse permutation.
Permutation clone() const
Clones this permutation.
Adjactor for iterating over children of adaptive mesh elements.
IndexSetHolder interface for adaptive meshes.
AdaptiveMeshType_ AdaptiveMeshType
Type of underlying mesh.
IndexSet interface for adaptive meshes.
std::size_t bytes() const
Returns the size of dynamically allocated memory in bytes.
Index operator()(Index i, int j) const
access operator
Index operator()(Index i, int j)
access operator
AdaptiveIndexSet clone() const
Returns a clone of this index set.
IndexTupleType operator[](Index i)
Returns an adaptive index tuple.
Index get_num_entities() const
Returns the number of entities.
int get_num_indices() const
Returns the number of indices per entity.
AdaptiveIndexSet(std::shared_ptr< AdaptiveMeshType > mesh, Layer layer)
Constructor.
AdaptiveIndexTuple< AdaptiveMeshType, cell_dim_, face_dim_ > IndexTupleType
Tuple type.
Index get_index_bound() const
Returns the maximum index of any face returned by this index set.
IndexTupleType operator[](Index i) const
Returns an adaptive index tuple.
static constexpr int num_indices
Number of indices per entry.
AdaptiveMeshType_ AdaptiveMeshType
Type of underlying mesh.
IndexTuple interface for adaptive meshes.
Index operator[](int idx) const
access operator
AdaptiveMeshType_ AdaptiveMeshType
Type of underlying adaptive mesh.
static constexpr int num_indices
Number of indices per tuple.
AdaptiveIndexTuple(std::shared_ptr< AdaptiveMeshType > mesh, Layer layer, Index entity_idx)
Constructor.
void permute_map(const Adjacency::Permutation &inv_perm)
Permute this index tuple.
std::shared_ptr< AdaptiveMeshType > _mesh
Pointer to underlying mesh.
std::optional< Adjacency::Permutation > _inv_perm
Permutation of this tuple.
Layer _layer
Layer this tuple refers to.
Index _entity_idx
Index of entity this tuple refers to.
ConformalMesh interface for adaptive meshes.
const MeshPermutation< ShapeType > & get_mesh_permutation() const
Returns the permutation applied to the mesh.
auto & get_index_set()
Returns a reference to an index set of this mesh.
AdaptiveMeshType::FoundationMeshType & foundation_mesh()
accessor for foundation mesh
Index get_num_children(Index elem) const
Returns the number of children of mesh a mesh element.
AdaptiveMeshLayer(std::shared_ptr< AdaptiveMeshType > mesh, Layer layer)
Constructor.
static constexpr int world_dim
World dimension.
const AdaptiveMeshType & adaptive_mesh() const
accessor for adaptive mesh
bool validate_element_layering() const
Validates the element layering.
AdaptiveMeshType & adaptive_mesh()
accessor for adaptive mesh
typename AdaptiveMeshType::ShapeType ShapeType
Shape type.
VertexSetType & get_vertex_set()
Returns the vertex set of this mesh.
typename AdaptiveMeshType::VertexType VertexType
Vertex type.
const VertexSetType & get_vertex_set() const
Returns the vertex set of this mesh.
std::optional< Index > get_child(Index elem, Index child) const
Retrieve index of a child element.
bool validate_element_coloring() const
Validates the element coloring.
Index get_num_elements() const
Returns the number of elements (highest dimension entities) in the mesh.
Index get_num_vertices() const
Returns the number of vertices in the mesh.
const auto & get_index_set() const
Returns a reference to an index set of this mesh.
bool has_vertex_changed(Index vertex_idx) const
Indicates whether any mesh element adjacent to the given vertex has changed on the given layer.
std::shared_ptr< AdaptiveMeshType > adaptive_mesh_ptr() const
accessor for adaptive mesh
static constexpr bool is_structured
This is an unstructured mesh.
const AdaptiveMeshType::FoundationMeshType & foundation_mesh() const
accessor for foundation mesh
AdaptiveMeshType_ AdaptiveMeshType
Type of underlying mesh.
void set_permutation(MeshPermutationType &&mesh_perm)
Sets a custom mesh permutation for this mesh.
static constexpr int shape_dim
Shape dimension.
IndexSetHolderType & get_index_set_holder()
Returns a reference to the index set holder of this mesh.
typename AdaptiveMeshType::CoordType CoordType
Coord type.
const IndexSetHolderType & get_index_set_holder() const
Returns a reference to the index set holder of this mesh.
AdaptiveIndexSetHolder< AdaptiveMeshType, ShapeType > IndexSetHolderType
IndexSetHolder type.
Index get_num_entities(int dim) const
Returns the number of entities of dimension dim in the mesh.
AdaptiveNeighbors< AdaptiveMeshType > AdaptiveNeighborsType
Type of neighbors wrapper.
bool is_permuted() const
Checks whether the mesh is permuted.
void create_permutation(PermutationStrategy strategy)
Creates a mesh permutation based on one of the standard permutation strategies.
Gives access to neighbor information on a mesh layer.
Index operator()(Index i, int j)
access operator
static constexpr int num_indices
Number of indices per entry.
Index get_index_bound() const
Returns the maximum index of any face returned by this index set.
std::size_t bytes() const
Returns the size of dynamically allocated memory in bytes.
AdaptiveNeighbors clone() const
Returns a clone of this index set.
int get_num_indices() const
Returns the number of indices per entity.
AdaptiveNeighbors(std::shared_ptr< AdaptiveMeshType > mesh, Layer layer)
Constructor.
Index operator()(Index i, int j) const
access operator
Index get_num_entities() const
Returns the number of entities.
VertexSet interface for adaptive meshes.
void permute(const Adjacency::Permutation &perm, bool invert=false)
Permutes this vertex set.
AdaptiveMeshType_ AdaptiveMeshType
Type of underlying adaptive mesh.
AdaptiveVertexSet(std::shared_ptr< AdaptiveMeshType > mesh, Layer layer)
Constructor.
int get_num_coords() const
Returns the number of coordinates per vertex.
VertexType & operator[](Index idx)
Returns a reference to a vertex.
typename AdaptiveMeshType::VertexType VertexType
Vertex type.
typename AdaptiveMeshType::CoordType CoordType
Type of single vertex coordinate.
Index get_num_vertices() const
Returns the number of vertices in the vertex set.
AdaptiveVertexSet clone() const
Returns a clone of this vertex set.
std::shared_ptr< AdaptiveMeshType > _mesh
Underlying adaptive mesh.
static constexpr int num_coords
Number of coordinates per vertex.
const VertexType & operator[](Index idx) const
Returns a reference to a vertex.
Adjactor from elements of the foundation mesh to layer 0 of an adaptive mesh.
const std::vector< Index > & get_element_layering() const
Returns a const reference to the element layering vector.
const PermArray & get_perms() const
bool empty() const
Checks whether this permutation is empty.
const std::vector< Index > & get_element_coloring() const
Returns a const reference to the element coloring vector.
void create(PermutationStrategy strategy, const IndexSetHolder< Shape_ > &ish, const VertexSet< num_coords_, Coord_ > &vtx)
Creates a mesh permutation for a conformal mesh.
const Adjacency::Permutation & get_perm(int dim=shape_dim) const
Returns a const reference to a (forward) permutation.
String class implementation.
Definition: string.hpp:47
@ transpose
Render-Transpose mode.
Geometry namespace.
PermutationStrategy
Mesh permutation strategy enumeration.
@ other
generic/other permutation strategy
T_ min(T_ a, T_ b)
Returns the minimum of two values.
Definition: math.hpp:123
T_ max(T_ a, T_ b)
Returns the maximum of two values.
Definition: math.hpp:137
static constexpr bool operator*(TrafoTags a)
bool conversion operator
Definition: eval_tags.hpp:81
String stringify(const T_ &item)
Converts an item into a String.
Definition: string.hpp:993
std::uint64_t Index
Index data type.
Used for return type of get_index_set.
Index Tuple class template.
Definition: index_set.hpp:35
Newtype wrapper for mesh layers.
Face traits tag struct template.
Definition: shape.hpp:106
typedef ShapeType
Shape type of the face.
Definition: shape.hpp:108