FEAT 3
Finite Element Analysis Toolbox
Loading...
Searching...
No Matches
mesh_part_ops.hpp
Go to the documentation of this file.
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
38// includes, feat
40#include <kernel/geometry/index_set.hpp>
41#include <kernel/geometry/mesh_part.hpp>
42#include <kernel/geometry/target_set.hpp>
43#include <kernel/shape.hpp>
45
46// includes, system
47#include <algorithm>
48#include <functional>
49#include <memory>
50#include <optional>
51
52namespace FEAT::Geometry
53{
54 namespace Inner
55 {
66 template<typename Shape_, typename T_>
67 class DimVector : public DimVector<typename Shape::FaceTraits<Shape_, Shape_::dimension - 1>::ShapeType, T_>
68 {
69 protected:
71 std::vector<T_> vec;
72
73 public:
79 template<int dim_>
80 std::vector<T_>& get()
81 {
82 static_assert(dim_ <= Shape_::dimension);
83
84 using DimShape = typename Shape::FaceTraits<Shape_, dim_>::ShapeType;
86 }
87
93 template<int dim_>
94 const std::vector<T_>& get() const
95 {
96 static_assert(dim_ <= Shape_::dimension);
97
98 using DimShape = typename Shape::FaceTraits<Shape_, dim_>::ShapeType;
100 }
101 };
102
114 template<typename T_>
115 class DimVector<Shape::Vertex, T_>
116 {
117 protected:
118 std::vector<T_> vec;
119
120 public:
121 template<int dim_>
122 std::vector<T_>& get()
123 {
124 static_assert(dim_ == 0);
125
126 return vec;
127 }
128
129 template<int dim_>
130 const std::vector<T_>& get() const
131 {
132 static_assert(dim_ == 0);
133
134 return vec;
135 }
136 };
137
156 {
158 std::optional<Index> left = std::nullopt;
159
161 std::optional<Index> right = std::nullopt;
162
164 std::optional<Index> index = std::nullopt;
165 };
166 } // namespace Inner
167
186 template<typename MeshType_>
188 {
189 private:
191 using MeshType = MeshType_;
192
194 using ShapeType = typename MeshType::ShapeType;
195
198
201
204
207
209 using JoinVec = std::vector<JoinEntry>;
210
213
215 using TargetSetHolderType = TargetSetHolder<ShapeType>;
216
218 using IndexSetHolderType = IndexSetHolder<ShapeType>;
219
220 public:
257 template<typename AttributeFn_>
258 static MeshPartType
259 meshpart_intersection(const MeshPartType& left, const MeshPartType& right, const AttributeFn_& attribute_merge)
260 {
261 const auto join_pred = [](const JoinEntry& e) { return e.left.has_value() && e.right.has_value(); };
262
263 // The result meshpart has a topology as long as any of the inputs have a topology
264 const bool create_topology = left.has_topology() || right.has_topology();
265 return meshpart_op(left, right, attribute_merge, join_pred, create_topology);
266 }
267
304 template<typename AttributeFn_>
305 static MeshPartType
306 meshpart_union(const MeshPartType& left, const MeshPartType& right, const AttributeFn_& attribute_merge)
307 {
308 const auto join_pred = [](const JoinEntry& e) { return e.left.has_value() || e.right.has_value(); };
309 // The result meshpart has a topology as long as both of the inputs have a topology
310 const bool create_topology = left.has_topology() && right.has_topology();
311 return meshpart_op(left, right, attribute_merge, join_pred, create_topology);
312 }
313
345 {
346 // NOTE(mmuegge): Differences between meshparts with topology are technically supported.
347 // They currently behave exactly as defined, which means they do not include any boundary entities
348 // along the common border of the left and right mesh parts.
349 // The implementation puts ~Index(0) values into the meshpart topology for these entities.
350 // That is the proper operation, but not very useful in the context of meshparts
351 // and quite surprising.
352 // Hence the assertion below.
353 // We could include these boundary entities in the difference.
354 // To do so we would need to either determine how many additional entities
355 // we need to add, so that we can size the meshpart topology appropriately,
356 // or create a dynamic data structure for topologies.
357 // Both of these solution are quite a lot of work, so we leave out differences between
358 // meshparts with topology for now.
359 XASSERTM(
360 !left.has_topology() && !right.has_topology(),
361 "Difference between meshparts with topology are not supported.");
362
363 const auto merge = [](AttributeDataType& a, AttributeDataType& /*b*/) { return a; };
364 const auto join_pred = [](const JoinEntry& e) { return e.left.has_value() && !e.right.has_value(); };
365 return meshpart_op(left, right, merge, join_pred, false);
366 }
367
400 {
401 // Identity merge, because no common entities end up in result
402 const auto merge = [](AttributeDataType& a, AttributeDataType& /*b*/) { return a; };
403 MeshPartType a = meshpart_difference(left, right, merge);
404 MeshPartType b = meshpart_difference(right, left, merge); // NOLINT
405 return meshpart_union(a, b, merge, false);
406 }
407
408 private:
426 template<typename AttributeFn_, typename PredFn_>
428 const MeshPartType& left,
429 const MeshPartType& right,
430 const AttributeFn_& attribute_merge,
431 const PredFn_& join_pred,
432 const bool create_topology)
433 {
434 // Determine join of mesh parts
435 // The join tracks which indices belong to the same entity on the left, right, and result meshparts.
436 Join join;
437 join_elements(join, left.get_target_set_holder(), right.get_target_set_holder());
438
439 // The join predicate determines which elements get added to the target set holder
441 build_target_set_holder(tsh, join, join_pred, left.get_target_set_holder(), right.get_target_set_holder());
442
443 // Determine result size from target set
444 std::array<Index, ShapeType::dimension + 1> size{};
445 for(int i(0); i < ShapeType::dimension + 1; i++)
446 {
447 size[i] = tsh.get_num_entities(i);
448 }
449
450
451 // Create result mesh part
452 MeshPartType result(size.data(), create_topology);
453 result.get_target_set_holder() = std::move(tsh);
454
455 if(create_topology)
456 {
457 // Empty dummy IndexSetHolder. Allows us to handle all configurations of has/has-no topology the same
458 IndexSetHolderType empty;
459 const IndexSetHolderType* left_ish = left.has_topology() ? left.get_topology() : &empty;
460 const IndexSetHolderType* right_ish = right.has_topology() ? right.get_topology() : &empty;
461
463 *result.get_topology(),
464 join,
465 *left_ish,
466 left.get_target_set_holder(),
467 *right_ish,
468 right.get_target_set_holder());
469 }
470
471 // Handle attributes
472 copy_attributes(attribute_merge, join, result, left, right);
473
474 // Return intersection mesh part
475 return std::move(result);
476 }
477
486 static std::set<String> common_attributes(const MeshPartType& left, const MeshPartType& right)
487 {
488 std::set<String> attributes;
489
490 for(const auto& pair : right.get_mesh_attributes())
491 {
492 const AttributeSetType* attribute = left.find_attribute(pair.first);
493 if(attribute != nullptr && pair.second->get_dimension() == attribute->get_dimension())
494 {
495 attributes.insert(pair.first);
496 }
497 }
498
499 return attributes;
500 }
501
516 template<typename AttributeFn_>
517 static void copy_attributes(
518 const AttributeFn_& fn,
519 const Join& join,
520 MeshPartType& result,
521 const MeshPartType& left,
522 const MeshPartType& right)
523 {
524 // Determine attributes that exist on both the left and right meshpart
525 std::set<String> attributes = common_attributes(left, right);
526
527 // Copy common attributes to result meshpart
528 for(const String& name : attributes)
529 {
530 const AttributeSetType* left_attribute = left.find_attribute(name);
531 const AttributeSetType* right_attribute = right.find_attribute(name);
532
533 const int dim = left_attribute->get_dimension();
534
535 auto new_attribute = std::make_unique<AttributeSetType>(result.get_num_entities(0), dim);
536
537 const JoinVec& join_vec = join.template get<0>();
538
539 for(const JoinEntry& t : join_vec)
540 {
541 if(!t.index.has_value())
542 {
543 // Entity is not in result meshpart. Skip it.
544 continue;
545 }
546
547 if(t.left.has_value() && t.right.has_value())
548 {
549 // Entity exists on both meshpart. Merge the attribute values
550 for(int i(0); i < dim; i++)
551 {
552 AttributeDataType l = left_attribute->operator()(t.left.value(), i);
553 AttributeDataType r = right_attribute->operator()(t.right.value(), i);
554 new_attribute->operator()(t.index.value(), i) = fn(l, r);
555 }
556 }
557 else if(t.left.has_value())
558 {
559 // Entity exists only on left meshpart. Copy the attribute value
560 for(int i(0); i < dim; i++)
561 {
562 AttributeDataType l = left_attribute->operator()(t.left.value(), i);
563 new_attribute->operator()(t.index.value(), i) = l;
564 }
565 }
566 else if(t.right.has_value())
567 {
568 // Entity exists only on right meshpart. Copy the attribute value
569 for(int i(0); i < dim; i++)
570 {
571 AttributeDataType r = right_attribute->operator()(t.right.value(), i);
572 new_attribute->operator()(t.index.value(), i) = r;
573 }
574 }
575 else
576 {
577 XABORTM("Entity in meshpart operation result without source index");
578 }
579 }
580 result.add_attribute(std::move(new_attribute), name);
581 }
582 }
583
599 template<int dim_ = ShapeType::dimension>
600 static void build_topology(
601 IndexSetHolderType& output_ish,
602 Join& join,
603 const IndexSetHolderType& left_ish,
604 const TargetSetHolderType& left_tsh,
605 const IndexSetHolderType& right_ish,
606 const TargetSetHolderType& right_tsh)
607 {
608 if constexpr(dim_ == 0)
609 {
610 // Nothing to do for vertices
611 return;
612 }
613 else
614 {
615 build_topology<dim_ - 1>(output_ish, join, left_ish, left_tsh, right_ish, right_tsh);
616 build_topology_inner<dim_, dim_ - 1>(output_ish, join, left_ish, left_tsh, right_ish, right_tsh);
617 }
618 }
619
636 template<int dim_, int codim_>
638 IndexSetHolderType& output_ish,
639 Join& join,
640 const IndexSetHolderType& left_ish,
641 const TargetSetHolderType& left_tsh,
642 const IndexSetHolderType& right_ish,
643 const TargetSetHolderType& right_tsh)
644 {
645 if constexpr(codim_ < 0)
646 {
647 // There are no facets with dimension lower than 0. No more work to be done.
648 return;
649 }
650 else
651 {
652 // Recursive call to sub entities
653 build_topology_inner<dim_, codim_ - 1>(output_ish, join, left_ish, left_tsh, right_ish, right_tsh);
654
655 using DimShape = typename Shape::FaceTraits<ShapeType, dim_>::ShapeType;
656 constexpr int num_indices = Shape::FaceTraits<DimShape, codim_>::count;
657 using IndexSetType = IndexSet<num_indices>;
658
659 JoinVec& dim_entities = join.template get<dim_>();
660 JoinVec& codim_entities = join.template get<codim_>();
661
662 const IndexSetType& right_is = right_ish.template get_index_set<dim_, codim_>();
663 const IndexSetType& left_is = left_ish.template get_index_set<dim_, codim_>();
664
665 IndexSetType& out = output_ish.template get_index_set<dim_, codim_>();
666
667 // NOTE(mmuegge): It is tempting to turn the JoinVecs into hashmaps,
668 // but we first search for right-indices and then left-indicies.
669 // This means we need to duplicate information two maps with different keys.
670 // So this might actually be more elegant.
671
673 // Right Search
675
676 // In the following, we need to search for entities in the codim_entities vector.
677 // To do so efficiently we sort it beforehand, to allow for binary searches later
678 std::sort(
679 codim_entities.begin(),
680 codim_entities.end(),
681 [](const JoinEntry& a, const JoinEntry& b) { return a.right < b.right; });
682
683 // We first search for topology in the entities of the right meshpart.
684 // If, for any entitiy in the result meshpart, we do not find the topology there,
685 // we need additionally search in the left meshpart.
686 // Because we need to re-sort the codim vector, we do these steps one after the other
687 // and use this bool to indicate whether we need to do the left-search at all.
688 bool left_entities = false;
689
690 // Determine topology for all entities that are part of the result
691 // mesh part and stem from the right mesh part
692 for(const JoinEntry& t : dim_entities)
693 {
694 if(!t.index.has_value())
695 {
696 // This entry is not part of the result mesh part. Skip it.
697 continue;
698 }
699
700 if(!t.right.has_value())
701 {
702 // This entry does not stem from the right mesh part. Skip it.
703
704 // This also means we need to handle entities stemming from the left
705 // mesh part after this
706 left_entities = true;
707 continue;
708 }
709
710 for(int j(0); j < num_indices; j++)
711 {
712 // Search for join entry of j-th sub-entity.
713
714 // NOTE(mmuegge): We are searching among the "std::optional<Index> right"
715 // members of the join entries. We thus wrap our target value itself into
716 // an optional before starting the search
717
718 const std::optional<Index> target(right_is[t.right.value()][j]);
719 std::optional<Index> codim_index = search(
720 codim_entities.begin(),
721 codim_entities.end(),
722 target,
723 [](const JoinEntry& a, const std::optional<Index>& i) { return a.right < i; });
724 XASSERT(codim_index.has_value());
725
726 const JoinEntry& codim_entry = codim_entities[codim_index.value()];
727
728 // NOTE(mmuegge): codim_entry.index is only empty when trying to produce differences on meshparts.
729 // In that case we could be searching for a topology element that has been removed as part of the
730 // shared boundary between left and right.
731 // That case is forbidden via an assertion above, but is safely handled here anyway.
732 out(t.index.value(), j) = codim_entry.index.value_or(~Index(0));
733 }
734 }
735
736 if(!left_entities)
737 {
738 return;
739 }
740
742 // Left Search
744
745 // Prepare join for searching entities of right mesh part
746 std::sort(
747 codim_entities.begin(),
748 codim_entities.end(),
749 [](const JoinEntry& a, const JoinEntry& b) { return a.left < b.left; });
750
751 for(const JoinEntry& t : dim_entities)
752 {
753 if(!t.index.has_value() || t.right.has_value() || !t.left.has_value())
754 {
755 // We only handle entries that are part of the result,
756 // part of the left meshpart,
757 // and have not been handled by the right meshpart case above
758 continue;
759 }
760
761 for(int j(0); j < num_indices; j++)
762 {
763 const std::optional<Index> target(left_is[t.left.value()][j]);
764 std::optional<Index> codim_index = search(
765 codim_entities.begin(),
766 codim_entities.end(),
767 target,
768 [](const JoinEntry& a, const std::optional<Index>& i) { return a.left < i; });
769 XASSERT(codim_index.has_value());
770
771 const JoinEntry& codim_entry = codim_entities[codim_index.value()];
772 out(t.index.value(), j) = codim_entry.index.value_or(~Index(0));
773 }
774 }
775 }
776 }
777
794 template<int dim_ = ShapeType::dimension>
796 TargetSetHolderType& output_tsh,
797 Join& join,
798 const std::function<bool(const JoinEntry&)>& pred,
799 const TargetSetHolderType& left_tsh,
800 const TargetSetHolderType& right_tsh)
801 {
802 if constexpr(dim_ < 0)
803 {
804 // No more work to be done
805 return;
806 }
807 else
808 {
809 // Recurse to lower dimension
810 build_target_set_holder<dim_ - 1>(output_tsh, join, pred, left_tsh, right_tsh);
811
812 JoinVec& entities = join.template get<dim_>();
813 const TargetSet& right_ts = right_tsh.template get_target_set<dim_>();
814 const TargetSet& left_ts = left_tsh.template get_target_set<dim_>();
815
816 std::vector<Index> target_set_vec;
817 for(JoinEntry& t : entities)
818 {
819 if(pred(t))
820 {
821 // Entity should be added; assign it an index
822 t.index = target_set_vec.size();
823 if(t.left)
824 {
825 // Pull target set information from left target set
826 target_set_vec.push_back(left_ts[t.left.value()]);
827 }
828 else if(t.right)
829 {
830 // Pull target set information from right target set
831 target_set_vec.push_back(right_ts[t.right.value()]);
832 }
833 else
834 {
835 // NOTE(mmuegge): This seems at first glance like the starting place
836 // for a complement operation. But I think we need to special case that,
837 // because we would need to grab any entitity not in one of the meshparts
838 // here and keep track of which entities we have already added.
839 XABORTM("No source index for entity!");
840 }
841 }
842 }
843
844 // Copy to actual target set, now that we know the final size.
845 TargetSet result(target_set_vec.size());
846 for(Index i(0); i < target_set_vec.size(); i++)
847 {
848 result[i] = target_set_vec[i];
849 }
850
851 // Move into output target set holder
852 output_tsh.template get_target_set<dim_>() = std::move(result);
853 }
854 }
855
871 template<int dim_ = ShapeType::dimension>
872 static void join_elements(Join& join, const TargetSetHolderType& left_tsh, const TargetSetHolderType& right_tsh)
873 {
874 if constexpr(dim_ < 0)
875 {
876 return;
877 }
878 else
879 {
880 // Recurse to lower dimension
881 join_elements<dim_ - 1>(join, left_tsh, right_tsh);
882
883 JoinVec& join_vec = join.template get<dim_>();
884
885 const TargetSet& left = left_tsh.template get_target_set<dim_>();
886 const TargetSet& right = right_tsh.template get_target_set<dim_>();
887
889 // Prepare lookup vector for left mesh part
891
892 // Create a copy of the target set we can safely modify
893 TargetSet left_lookup = left.clone();
894
895 Index* lbegin = left_lookup.get_indices();
896 Index* lend = left_lookup.get_indices() + left_lookup.get_num_entities();
897
898 // The target set will be sorted to enable binary searches.
899 // We need a way to retrieve the original indices later.
900 // We thus create this vector of indices and sort it the same way
901 // we sort the target set.
902 std::vector<Index> left_indices(left_lookup.get_num_entities());
903 for(Index i(0); i < left_lookup.get_num_entities(); i++)
904 {
905 left_indices[i] = i;
906 }
907
908 std::sort(
909 left_indices.begin(),
910 left_indices.end(),
911 [&](Index a, Index b) { return left_lookup[a] < left_lookup[b]; });
912 std::sort(lbegin, lend);
913
915 // Find entities exclusive to right mesh part and entities in intersection
917
918 for(Index i(0); i < right.get_num_entities(); i++)
919 {
920 JoinEntry entry;
921 entry.right = i;
922
923 const std::optional<Index> sorted_left = search(lbegin, lend, right[i]);
924 if(sorted_left)
925 {
926 entry.left = left_indices[sorted_left.value()];
927 }
928
929 join_vec.push_back(entry);
930 }
931
932 // OPTIMIZATION(mmuegge): At this point we have sufficient information to
933 // determine the intersection of two mesh parts. We calculate the full join
934 // only for other mesh part operations. If mesh part intersection ever
935 // becomes a performance bottleneck, we can calculate these joins more fine grained.
936
938 // Prepare lookup vector for left mesh part
940
941 // Create a copy of the target set we can safely modify
942 TargetSet right_lookup = right.clone();
943
944 Index* rbegin = right_lookup.get_indices();
945 Index* rend = right_lookup.get_indices() + right_lookup.get_num_entities();
946
947 // The target set will be sorted to enable binary searches.
948 // We need a way to retrieve the original indices later.
949 // We thus create this vector of indices and sort it the same way
950 // we sort the target set.
951 std::vector<Index> right_indices(right_lookup.get_num_entities());
952 for(Index i(0); i < right_lookup.get_num_entities(); i++)
953 {
954 right_indices[i] = i;
955 }
956
957 std::sort(
958 right_indices.begin(),
959 right_indices.end(),
960 [&](Index a, Index b) { return right_lookup[a] < right_lookup[b]; });
961 std::sort(rbegin, rend);
962
964 // Find entities exclusive to left mesh part
966
967 for(Index i(0); i < left.get_num_entities(); i++)
968 {
969 const std::optional<Index> sorted_right = search(rbegin, rend, left[i]);
970 if(!sorted_right.has_value())
971 {
972 JoinEntry entry;
973 entry.left = i;
974 join_vec.push_back(entry);
975 }
976 }
977 }
978 }
979
992 template<typename Iter_, typename T_>
993 static std::optional<Index> search(Iter_ begin, Iter_ end, const T_& value)
994 {
995 Iter_ it = std::lower_bound(begin, end, value);
996 if(it == end || *it != value)
997 {
998 return std::nullopt;
999 }
1000 else
1001 {
1002 return std::distance(begin, it);
1003 }
1004 }
1005
1020 template<typename Iter_, typename T_, typename CompFn_>
1021 static std::optional<Index> search(Iter_ begin, Iter_ end, const T_& value, const CompFn_& fn)
1022 {
1023 Iter_ it = std::lower_bound(begin, end, value, fn);
1024 if(it == end || fn(*it, value))
1025 {
1026 return std::nullopt;
1027 }
1028 else
1029 {
1030 return std::distance(begin, it);
1031 }
1032 }
1033 };
1034} // namespace FEAT::Geometry
#define XABORTM(msg)
Abortion macro definition with custom message.
Definition: assertion.hpp:192
#define XASSERT(expr)
Assertion macro definition.
Definition: assertion.hpp:262
#define XASSERTM(expr, msg)
Assertion macro definition with custom message.
Definition: assertion.hpp:263
FEAT Kernel base header.
Conformal Index-Set class template.
Definition: index_set.hpp:82
Utility class for storing one vector per dimension.
std::vector< T_ > & get()
accessor
std::vector< T_ > vec
Vector for current dimension.
const std::vector< T_ > & get() const
const accessor
Class template for partial meshes.
Definition: mesh_part.hpp:90
AttributeSet< AttributeDataType > AttributeSetType
Type for mesh attributes.
Definition: mesh_part.hpp:135
Index get_num_entities(int dim) const
Returns the number of entities.
Definition: mesh_part.hpp:311
MeshType::VertexSetType::CoordType AttributeDataType
Data type for attributes.
Definition: mesh_part.hpp:133
virtual bool add_attribute(std::unique_ptr< AttributeSetType > attribute, const String &identifier)
Copies one attribute to this MeshPart's AttributeHolder.
Definition: mesh_part.hpp:374
Boolean operations on mesh parts.
static MeshPartType meshpart_difference(const MeshPartType &left, const MeshPartType &right)
Compute the difference of two meshparts.
static MeshPartType meshpart_op(const MeshPartType &left, const MeshPartType &right, const AttributeFn_ &attribute_merge, const PredFn_ &join_pred, const bool create_topology)
Common logic for all meshpart operations.
std::vector< JoinEntry > JoinVec
Collection of JoinEntries.
static void copy_attributes(const AttributeFn_ &fn, const Join &join, MeshPartType &result, const MeshPartType &left, const MeshPartType &right)
Attribute copy logic.
static std::set< String > common_attributes(const MeshPartType &left, const MeshPartType &right)
Find common attributes of two meshparts.
TargetSetHolder< ShapeType > TargetSetHolderType
TargetSetHolder type.
static void join_elements(Join &join, const TargetSetHolderType &left_tsh, const TargetSetHolderType &right_tsh)
Construct a join of entities.
static std::optional< Index > search(Iter_ begin, Iter_ end, const T_ &value, const CompFn_ &fn)
Binary search.
typename MeshType::ShapeType ShapeType
Shape type.
static MeshPartType meshpart_symmetric_difference(const MeshPartType &left, const MeshPartType &right)
Compute the symmetric difference of two meshparts.
MeshType_ MeshType
Mesh type underlying all mesh parts.
static void build_topology(IndexSetHolderType &output_ish, Join &join, const IndexSetHolderType &left_ish, const TargetSetHolderType &left_tsh, const IndexSetHolderType &right_ish, const TargetSetHolderType &right_tsh)
Outer wrapper for topology construction.
static std::optional< Index > search(Iter_ begin, Iter_ end, const T_ &value)
Binary search.
typename MeshPartType::AttributeDataType AttributeDataType
Data types for meshpart attributes.
static MeshPartType meshpart_union(const MeshPartType &left, const MeshPartType &right, const AttributeFn_ &attribute_merge)
Compute the union of two meshparts.
static MeshPartType meshpart_intersection(const MeshPartType &left, const MeshPartType &right, const AttributeFn_ &attribute_merge)
Compute the intersection of two meshparts.
static void build_topology_inner(IndexSetHolderType &output_ish, Join &join, const IndexSetHolderType &left_ish, const TargetSetHolderType &left_tsh, const IndexSetHolderType &right_ish, const TargetSetHolderType &right_tsh)
Inner logc for topology construction.
static void build_target_set_holder(TargetSetHolderType &output_tsh, Join &join, const std::function< bool(const JoinEntry &)> &pred, const TargetSetHolderType &left_tsh, const TargetSetHolderType &right_tsh)
Construct target sets out of the join.
IndexSetHolder< ShapeType > IndexSetHolderType
IndexSetHolder type.
typename MeshPartType::AttributeSetType AttributeSetType
AttributeSet type.
MeshPart< MeshType > MeshPartType
Meshpart type.
Target set class.
Definition: target_set.hpp:27
Index get_num_entities() const
Returns the number of entities.
Definition: target_set.hpp:92
String class implementation.
Definition: string.hpp:46
Geometry namespace.
@ value
specifies whether the space should supply basis function values
std::uint64_t Index
Index data type.
Helper-struct for storing relationships between meshpart entities.
std::optional< Index > right
Index of this entry in the right mesh part of the join, if it exists.
std::optional< Index > left
Index of this entry in the left mesh part of the join, it it exists.
std::optional< Index > index
Index of this entry in the result mesh part, after target sets have been built.
Face traits tag struct template.
Definition: shape.hpp:106