deal.II version GIT relicensing-6834-g5b78e6bcdf 2026-10-01 11: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
fe_collection.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) 2003 - 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
13
15
18
19#include <deque>
20#include <limits>
21#include <set>
22
23
24
26
27namespace hp
28{
29 template <int dim, int spacedim>
31 {
32 set_default_hierarchy();
33 }
34
35
36
37 template <int dim, int spacedim>
44
45
46
47 template <int dim, int spacedim>
49 const std::vector<const FiniteElement<dim, spacedim> *> &fes)
50 : FECollection()
51 {
52 Assert(fes.size() > 0,
53 ExcMessage("Need to pass at least one finite element."));
54
55 for (unsigned int i = 0; i < fes.size(); ++i)
56 push_back(*fes[i]);
57 }
58
59
60
61 template <int dim, int spacedim>
62 void
64 const FiniteElement<dim, spacedim> &new_fe)
65 {
66 // check that the new element has the right number of components. only check
67 // with the first element, since all the other elements have already passed
68 // the test against the first element
69 Assert(this->empty() ||
70 new_fe.n_components() == this->operator[](0).n_components(),
71 ExcMessage("All elements inside a collection need to have the "
72 "same number of vector components!"));
73
75 }
76
77
78
79 template <int dim, int spacedim>
82 {
83 Assert(this->size() > 0, ExcNoFiniteElements());
84
85 // Since we can only add elements to an FECollection, we are safe comparing
86 // the sizes of this object and the MappingCollection. One circumstance that
87 // might lead to their sizes diverging is this:
88 // - An FECollection is created and then this function is called. The shared
89 // map is now initialized.
90 // - A second FECollection is made as a copy of this one. The shared map is
91 // not recreated.
92 // - The second FECollection is then resized by adding a new FE. The shared
93 // map is thus invalid for the second instance.
94 if (!reference_cell_default_linear_mapping ||
95 reference_cell_default_linear_mapping->size() != this->size())
96 {
97 auto &this_nc = const_cast<FECollection<dim, spacedim> &>(*this);
98
100 std::make_shared<MappingCollection<dim, spacedim>>();
101
102 for (const auto &fe : *this)
103 this_nc.reference_cell_default_linear_mapping->push_back(
104 fe.reference_cell()
105 .template get_default_linear_mapping<spacedim>());
106 }
107
108 return *reference_cell_default_linear_mapping;
110
111
112
113 template <int dim, int spacedim>
114 std::set<unsigned int>
116 const std::set<unsigned int> &fes,
117 const unsigned int codim) const
118 {
119 if constexpr (running_in_debug_mode())
120 {
121 // Validate user inputs.
122 Assert(codim <= dim, ExcImpossibleInDim(dim));
123 Assert(this->size() > 0, ExcEmptyObject());
124 for (const auto &fe : fes)
125 AssertIndexRange(fe, this->size());
126 }
127
128 // Check if any element of this FECollection is able to dominate all
129 // elements of @p fes. If one was found, we add it to the set of
130 // dominating elements.
131 std::set<unsigned int> dominating_fes;
132 for (unsigned int current_fe = 0; current_fe < this->size(); ++current_fe)
133 {
134 // Check if current_fe can dominate all elements in @p fes.
137 for (const auto &other_fe : fes)
138 domination =
139 domination &
140 this->operator[](current_fe)
141 .compare_for_domination(this->operator[](other_fe), codim);
142
143 // If current_fe dominates, add it to the set.
146 /*covers cases like {Q2,Q3,Q1,Q1} with fes={2,3}*/))
147 dominating_fes.insert(current_fe);
148 }
149 return dominating_fes;
150 }
151
152
153
154 template <int dim, int spacedim>
155 std::set<unsigned int>
157 const std::set<unsigned int> &fes,
158 const unsigned int codim) const
159 {
160 if constexpr (running_in_debug_mode())
161 {
162 // Validate user inputs.
163 Assert(codim <= dim, ExcImpossibleInDim(dim));
164 Assert(this->size() > 0, ExcEmptyObject());
165 for (const auto &fe : fes)
166 AssertIndexRange(fe, this->size());
167 }
168
169 // Check if any element of this FECollection is dominated by all
170 // elements of @p fes. If one was found, we add it to the set of
171 // dominated elements.
172 std::set<unsigned int> dominated_fes;
173 for (unsigned int current_fe = 0; current_fe < this->size(); ++current_fe)
174 {
175 // Check if current_fe is dominated by all other elements in @p fes.
178 for (const auto &other_fe : fes)
179 domination =
180 domination &
181 this->operator[](current_fe)
182 .compare_for_domination(this->operator[](other_fe), codim);
183
184 // If current_fe is dominated, add it to the set.
187 /*covers cases like {Q2,Q3,Q1,Q1} with fes={2,3}*/))
188 dominated_fes.insert(current_fe);
189 }
190 return dominated_fes;
191 }
192
193
194
195 template <int dim, int spacedim>
196 unsigned int
198 const std::set<unsigned int> &fes,
199 const unsigned int codim) const
200 {
201 // If the set of elements contains only a single element,
202 // then this very element is considered to be the dominating one.
203 if (fes.size() == 1)
204 return *fes.begin();
205
206 if constexpr (running_in_debug_mode())
207 {
208 // Validate user inputs.
209 Assert(codim <= dim, ExcImpossibleInDim(dim));
210 Assert(this->size() > 0, ExcEmptyObject());
211 for (const auto &fe : fes)
212 AssertIndexRange(fe, this->size());
213 }
214
215 // There may also be others, in which case we'll check if any of these
216 // elements is able to dominate all others. If one was found, we stop
217 // looking further and return the dominating element.
218 for (const auto &current_fe : fes)
219 {
220 // Check if current_fe can dominate all elements in @p fes.
223 for (const auto &other_fe : fes)
224 if (current_fe != other_fe)
225 domination =
226 domination &
227 this->operator[](current_fe)
228 .compare_for_domination(this->operator[](other_fe), codim);
229
230 // If current_fe dominates, return its index.
233 /*covers cases like {Q2,Q3,Q1,Q1} with fes={2,3}*/))
234 return current_fe;
235 }
236
237 // If we couldn't find the dominating object, return an invalid one.
239 }
240
241
242
243 template <int dim, int spacedim>
244 unsigned int
246 const std::set<unsigned int> &fes,
247 const unsigned int codim) const
248 {
249 // If the set of elements contains only a single element,
250 // then this very element is considered to be the dominated one.
251 if (fes.size() == 1)
252 return *fes.begin();
253
254 if constexpr (running_in_debug_mode())
255 {
256 // Validate user inputs.
257 Assert(codim <= dim, ExcImpossibleInDim(dim));
258 Assert(this->size() > 0, ExcEmptyObject());
259 for (const auto &fe : fes)
260 AssertIndexRange(fe, this->size());
261 }
262
263 // There may also be others, in which case we'll check if any of these
264 // elements is dominated by all others. If one was found, we stop
265 // looking further and return the dominated element.
266 for (const auto &current_fe : fes)
267 {
268 // Check if current_fe is dominated by all other elements in @p fes.
271 for (const auto &other_fe : fes)
272 if (current_fe != other_fe)
273 domination =
274 domination &
275 this->operator[](current_fe)
276 .compare_for_domination(this->operator[](other_fe), codim);
277
278 // If current_fe is dominated, return its index.
281 /*covers cases like {Q2,Q3,Q1,Q1} with fes={2,3}*/))
282 return current_fe;
283 }
284
285 // If we couldn't find the dominated object, return an invalid one.
288
289
290
291 template <int dim, int spacedim>
292 unsigned int
294 const std::set<unsigned int> &fes,
295 const unsigned int codim) const
296 {
297 unsigned int fe_index = find_dominating_fe(fes, codim);
298
299 if (fe_index == numbers::invalid_fe_index)
300 {
301 const std::set<unsigned int> dominating_fes =
302 find_common_fes(fes, codim);
303 fe_index = find_dominated_fe(dominating_fes, codim);
304 }
305
306 return fe_index;
307 }
308
309
310
311 template <int dim, int spacedim>
312 unsigned int
314 const std::set<unsigned int> &fes,
315 const unsigned int codim) const
316 {
317 unsigned int fe_index = find_dominated_fe(fes, codim);
318
319 if (fe_index == numbers::invalid_fe_index)
320 {
321 const std::set<unsigned int> dominated_fes =
322 find_enclosing_fes(fes, codim);
323 fe_index = find_dominating_fe(dominated_fes, codim);
324 }
325
326 return fe_index;
327 }
328
329
330
331 namespace
332 {
337 std::vector<std::map<unsigned int, unsigned int>>
338 compute_hp_dof_identities(
339 const std::set<unsigned int> &fes,
340 const std::function<std::vector<std::pair<unsigned int, unsigned int>>(
341 const unsigned int,
342 const unsigned int)> &query_identities)
344 // Let's deal with the easy cases first. If the set of fe indices is empty
345 // or has only one entry, then there are no identities:
346 if (fes.size() <= 1)
347 return {};
348
349 // If the set has two entries, then the
350 // FiniteElement::hp_*_dof_identities() function directly returns what we
351 // need. We just need to prefix its output with the respective fe indices:
352 if (fes.size() == 2)
353 {
354 const unsigned int fe_index_1 = *fes.begin();
355 const unsigned int fe_index_2 = *(++fes.begin());
356 const auto reduced_identities =
357 query_identities(fe_index_1, fe_index_2);
358
359 std::vector<std::map<unsigned int, unsigned int>> complete_identities;
360
361 for (const auto &reduced_identity : reduced_identities)
362 {
363 // Each identity returned by query_identities() is a pair of
364 // dof indices. Prefix each with its fe index and put the result
365 // into a vector
366 std::map<unsigned int, unsigned int> complete_identity = {
367 {fe_index_1, reduced_identity.first},
368 {fe_index_2, reduced_identity.second}};
369 complete_identities.emplace_back(std::move(complete_identity));
370 }
371
372 return complete_identities;
373 }
374
375 // Now for the general case of three or more elements:
376 //
377 // Consider all degrees of freedom of the identified elements (represented
378 // via (fe_index,dof_index) pairs) as the nodes in a graph. Then draw
379 // edges for all DoFs that are identified based on what the elements
380 // selected in the argument say. Let us first build this graph, where we
381 // only store the edges of the graph, and as a consequence ignore nodes
382 // (DoFs) that simply don't show up at all in any of the identities:
383 using Node = std::pair<unsigned int, unsigned int>;
384 using Edge = std::pair<Node, Node>;
385 using Graph = std::set<Edge>;
387 Graph identities_graph;
388 for (const unsigned int fe_index_1 : fes)
389 for (const unsigned int fe_index_2 : fes)
390 if (fe_index_1 != fe_index_2)
391 for (const auto &identity :
392 query_identities(fe_index_1, fe_index_2))
393 identities_graph.emplace(Node(fe_index_1, identity.first),
394 Node(fe_index_2, identity.second));
395
396 if constexpr (running_in_debug_mode())
397 {
398 // Now verify that indeed the graph is symmetric: If one element
399 // declares that certain ones of its DoFs are to be unified with those
400 // of the other, then the other one should agree with this. As a
401 // consequence of this test succeeding, we know that the graph is
402 // actually undirected.
403 for (const auto &edge : identities_graph)
404 Assert(identities_graph.find({edge.second, edge.first}) !=
405 identities_graph.end(),
407 }
408
409 // The next step is that we ought to verify that if there is an identity
410 // between (fe1,dof1) and (fe2,dof2), as well as with (fe2,dof2) and
411 // (fe3,dof3), then there should also be an identity between (fe1,dof1)
412 // and (fe3,dof3). The same logic should apply to chains of four
413 // identities.
414 //
415 // This means that the graph we have built above must be composed of a
416 // collection of complete sub-graphs (complete = each possible edge in the
417 // sub-graph exists) -- or, using a different term, that the graph
418 // consists of a number of "cliques". Each of these cliques is then one
419 // extended identity between two or more DoFs, and these are the ones that
420 // we will want to return.
421 //
422 // To ascertain that this is true, and to figure out what we want to
423 // return, we need to divide the graph into its sub-graphs and then ensure
424 // that each sub-graph is indeed a clique. This is slightly cumbersome,
425 // but can be done as follows:
426 // - pick one edge 'e' of G
427 // - add e=(n1,n2) to the sub-graph SG
428 // - set N={n1,n2}
429 // - loop over the remainder of the edges 'e' of the graph:
430 // - if 'e' has one or both nodes in N:
431 // - add 'e' to SG and
432 // - add its two nodes to N (they may already be in there)
433 // - remove 'e' from G
434 //
435 // In general, this may not find the entire sub-graph if the edges are
436 // stored in random order. For example, if the graph consisted of the
437 // following edges in this order:
438 // (a,b)
439 // (c,d)
440 // (a,c)
441 // (a,d)
442 // (b,c)
443 // (b,d)
444 // then the graph itself is a clique, but the algorithm outlined above
445 // would skip the edge (c,d) because neither of the nodes are already
446 // in the set N which at that point is still (a,b).
447 //
448 // But, we store the graph with a std::set, which stored edges in sorted
449 // order where the order is the lexicographic order of nodes. This ensures
450 // that we really capture all edges that correspond to a sub-graph (but
451 // we will assert this as well).
452 //
453 // (For programmatic reasons, we skip the removal of 'e' from G in a first
454 // run through because it modifies the graph and thus invalidates
455 // iterators. But because SG stores all of these edges, we can remove them
456 // all from G after collecting the edges in SG.)
457 std::vector<std::map<unsigned int, unsigned int>> identities;
458 while (identities_graph.size() > 0)
459 {
460 Graph sub_graph; // SG
461 std::set<Node> sub_graph_nodes; // N
462
463 sub_graph.emplace(*identities_graph.begin());
464 sub_graph_nodes.emplace(identities_graph.begin()->first);
465 sub_graph_nodes.emplace(identities_graph.begin()->second);
466
467 for (const Edge &e : identities_graph)
468 if ((sub_graph_nodes.find(e.first) != sub_graph_nodes.end()) ||
469 (sub_graph_nodes.find(e.second) != sub_graph_nodes.end()))
470 {
471 sub_graph.insert(e);
472 sub_graph_nodes.insert(e.first);
473 sub_graph_nodes.insert(e.second);
474 }
475
476 // We have now obtained a sub-graph from the overall graph.
477 // Now remove it from the bigger graph
478 for (const Edge &e : sub_graph)
479 identities_graph.erase(e);
480
481 if constexpr (running_in_debug_mode())
482 {
483 // There are three checks we ought to perform:
484 // - That the sub-graph is undirected, i.e. that every edge
485 // appears
486 // in both directions
487 for (const auto &edge : sub_graph)
488 Assert(sub_graph.find({edge.second, edge.first}) !=
489 sub_graph.end(),
491
492 // - None of the nodes in the sub-graph should have appeared in
493 // any of the other sub-graphs. If they did, then we have a bug
494 // in extracting sub-graphs. This is actually more easily
495 // checked the other way around: none of the nodes of the
496 // sub-graph we just extracted should be in any of the edges of
497 // the *remaining* graph
498 for (const Node &n : sub_graph_nodes)
499 for (const Edge &e : identities_graph)
500 Assert((n != e.first) && (n != e.second), ExcInternalError());
501 // - Second, the sub-graph we just extracted needs to be complete,
502 // i.e.,
503 // be a "clique". We check this by counting how many edges it
504 // has. for 'n' nodes in 'N', we need to have n*(n-1) edges (we
505 // store both directed edges).
506 Assert(sub_graph.size() ==
507 sub_graph_nodes.size() * (sub_graph_nodes.size() - 1),
509 }
511 // At this point we're sure that we have extracted a complete
512 // sub-graph ("clique"). The DoFs involved are all identical then, and
513 // we will store this identity so we can return it later.
514 //
515 // The sub-graph is given as a set of Node objects, which is just
516 // a collection of (fe_index,dof_index) pairs. Because each
517 // fe_index can only appear once there, a better data structure
518 // is a std::map from fe_index to dof_index, which can conveniently
519 // be initialized from a range of iterators to pairs:
520 identities.emplace_back(sub_graph_nodes.begin(),
521 sub_graph_nodes.end());
522 Assert(identities.back().size() == sub_graph_nodes.size(),
524 }
525
526 return identities;
527 }
528 } // namespace
529
530
531
532 template <int dim, int spacedim>
533 std::vector<std::map<unsigned int, unsigned int>>
535 const std::set<unsigned int> &fes) const
537 auto query_vertex_dof_identities = [this](const unsigned int fe_index_1,
538 const unsigned int fe_index_2) {
539 return (*this)[fe_index_1].hp_vertex_dof_identities((*this)[fe_index_2]);
540 };
541 return compute_hp_dof_identities(fes, query_vertex_dof_identities);
542 }
543
544
545
546 template <int dim, int spacedim>
547 std::vector<std::map<unsigned int, unsigned int>>
549 const std::set<unsigned int> &fes) const
550 {
551 auto query_line_dof_identities = [this](const unsigned int fe_index_1,
552 const unsigned int fe_index_2) {
553 return (*this)[fe_index_1].hp_line_dof_identities((*this)[fe_index_2]);
554 };
555 return compute_hp_dof_identities(fes, query_line_dof_identities);
556 }
557
558
559
560 template <int dim, int spacedim>
561 std::vector<std::map<unsigned int, unsigned int>>
563 const std::set<std::pair<unsigned int, unsigned int>> &fes_and_faces) const
565 // first entry in the pair is the fe_index, the second entry is the face_no
566 std::pair<unsigned int, unsigned int> fe_and_face_1 =
567 *fes_and_faces.begin();
568 std::pair<unsigned int, unsigned int> fe_and_face_2 =
569 *(++fes_and_faces.begin());
570
571 auto query_quad_dof_identities =
572 [this, &fe_and_face_1, &fe_and_face_2](const unsigned int fe_index_1,
573 const unsigned int fe_index_2) {
574 // check if fe_index_1 is the same fe index as in fe_and_face_1
575 // then use the corresponding face
576 const unsigned int face_no = fe_index_1 == fe_and_face_1.first ?
577 fe_and_face_1.second :
578 fe_and_face_2.second;
579 return (*this)[fe_index_1].hp_quad_dof_identities((*this)[fe_index_2],
580 face_no);
581 };
582
583 // Extract only the FE indices from the pairs we got as inputs
584 std::set<unsigned int> fe_indices_only;
585 for (const auto &[fe_index, face_index] : fes_and_faces)
586 {
587 fe_indices_only.insert(fe_index);
588 (void)face_index;
589 }
590
591 return compute_hp_dof_identities(fe_indices_only,
592 query_quad_dof_identities);
593 }
594
595
596
597 template <int dim, int spacedim>
598 void
600 const std::function<
601 unsigned int(const typename hp::FECollection<dim, spacedim> &,
602 const unsigned int)> &next,
603 const std::function<
604 unsigned int(const typename hp::FECollection<dim, spacedim> &,
605 const unsigned int)> &prev)
606 {
607 // copy hierarchy functions
608 hierarchy_next = next;
609 hierarchy_prev = prev;
610 }
611
612
614 template <int dim, int spacedim>
615 void
617 {
618 // establish hierarchy corresponding to order of indices
619 set_hierarchy(&DefaultHierarchy::next_index,
620 &DefaultHierarchy::previous_index);
621 }
622
623
624
625 template <int dim, int spacedim>
626 std::vector<unsigned int>
628 const unsigned int fe_index) const
629 {
630 AssertIndexRange(fe_index, this->size());
631
632 std::deque<unsigned int> sequence = {fe_index};
633
634 // get predecessors
635 {
636 unsigned int front = sequence.front();
637 unsigned int previous;
638 while ((previous = previous_in_hierarchy(front)) != front)
639 {
640 sequence.push_front(previous);
641 front = previous;
642
643 Assert(sequence.size() <= this->size(),
645 "The registered hierarchy is not terminated: "
646 "previous_in_hierarchy() does not stop at a final index."));
647 }
648 }
649
650 // get successors
651 {
652 unsigned int back = sequence.back();
653 unsigned int next;
654 while ((next = next_in_hierarchy(back)) != back)
655 {
656 sequence.push_back(next);
657 back = next;
658
659 Assert(sequence.size() <= this->size(),
661 "The registered hierarchy is not terminated: "
662 "next_in_hierarchy() does not stop at a final index."));
663 }
664 }
665
666 return {sequence.begin(), sequence.end()};
667 }
668
669
670
671 template <int dim, int spacedim>
672 unsigned int
674 const unsigned int fe_index) const
676 AssertIndexRange(fe_index, this->size());
677
678 const unsigned int new_fe_index = hierarchy_next(*this, fe_index);
679 AssertIndexRange(new_fe_index, this->size());
680
681 return new_fe_index;
682 }
683
684
685
686 template <int dim, int spacedim>
687 unsigned int
689 const unsigned int fe_index) const
690 {
691 AssertIndexRange(fe_index, this->size());
692
693 const unsigned int new_fe_index = hierarchy_prev(*this, fe_index);
694 AssertIndexRange(new_fe_index, this->size());
696 return new_fe_index;
697 }
698
699
700
701 template <int dim, int spacedim>
704 const FEValuesExtractors::Scalar &scalar) const
705 {
706 Assert(this->size() > 0,
707 ExcMessage("This collection contains no finite element."));
708
709 // get the mask from the first element of the collection
710 const ComponentMask mask = (*this)[0].component_mask(scalar);
711
712 // but then also verify that the other elements of the collection
713 // would return the same mask
714 for (unsigned int c = 1; c < this->size(); ++c)
715 Assert(mask == (*this)[c].component_mask(scalar), ExcInternalError());
716
717 return mask;
718 }
720
721 template <int dim, int spacedim>
724 const FEValuesExtractors::Vector &vector) const
725 {
726 Assert(this->size() > 0,
727 ExcMessage("This collection contains no finite element."));
728
729 // get the mask from the first element of the collection
730 const ComponentMask mask = (*this)[0].component_mask(vector);
731
732 // but then also verify that the other elements of the collection
733 // would return the same mask
734 for (unsigned int c = 1; c < this->size(); ++c)
735 Assert(mask == (*this)[c].component_mask(vector), ExcInternalError());
736
737 return mask;
738 }
739
740
741 template <int dim, int spacedim>
744 const FEValuesExtractors::SymmetricTensor<2> &sym_tensor) const
745 {
746 Assert(this->size() > 0,
747 ExcMessage("This collection contains no finite element."));
749 // get the mask from the first element of the collection
750 const ComponentMask mask = (*this)[0].component_mask(sym_tensor);
751
752 // but then also verify that the other elements of the collection
753 // would return the same mask
754 for (unsigned int c = 1; c < this->size(); ++c)
755 Assert(mask == (*this)[c].component_mask(sym_tensor), ExcInternalError());
756
757 return mask;
758 }
759
760
761 template <int dim, int spacedim>
764 {
765 Assert(this->size() > 0,
766 ExcMessage("This collection contains no finite element."));
767
768 // get the mask from the first element of the collection
769 const ComponentMask mask = (*this)[0].component_mask(block_mask);
770
771 // but then also verify that the other elements of the collection
772 // would return the same mask
773 for (unsigned int c = 1; c < this->size(); ++c)
774 Assert(mask == (*this)[c].component_mask(block_mask),
775 ExcMessage("Not all elements of this collection agree on what "
776 "the appropriate mask should be."));
777
778 return mask;
779 }
780
781
782 template <int dim, int spacedim>
785 const FEValuesExtractors::Scalar &scalar) const
786 {
787 Assert(this->size() > 0,
788 ExcMessage("This collection contains no finite element."));
789
790 // get the mask from the first element of the collection
791 const BlockMask mask = (*this)[0].block_mask(scalar);
792
793 // but then also verify that the other elements of the collection
794 // would return the same mask
795 for (unsigned int c = 1; c < this->size(); ++c)
796 Assert(mask == (*this)[c].block_mask(scalar),
797 ExcMessage("Not all elements of this collection agree on what "
798 "the appropriate mask should be."));
800 return mask;
801 }
802
803
804 template <int dim, int spacedim>
807 const FEValuesExtractors::Vector &vector) const
808 {
809 Assert(this->size() > 0,
810 ExcMessage("This collection contains no finite element."));
811
812 // get the mask from the first element of the collection
813 const BlockMask mask = (*this)[0].block_mask(vector);
814
815 // but then also verify that the other elements of the collection
816 // would return the same mask
817 for (unsigned int c = 1; c < this->size(); ++c)
818 Assert(mask == (*this)[c].block_mask(vector),
819 ExcMessage("Not all elements of this collection agree on what "
820 "the appropriate mask should be."));
821
822 return mask;
823 }
824
825
826 template <int dim, int spacedim>
829 const FEValuesExtractors::SymmetricTensor<2> &sym_tensor) const
831 Assert(this->size() > 0,
832 ExcMessage("This collection contains no finite element."));
833
834 // get the mask from the first element of the collection
835 const BlockMask mask = (*this)[0].block_mask(sym_tensor);
836
837 // but then also verify that the other elements of the collection
838 // would return the same mask
839 for (unsigned int c = 1; c < this->size(); ++c)
840 Assert(mask == (*this)[c].block_mask(sym_tensor),
841 ExcMessage("Not all elements of this collection agree on what "
842 "the appropriate mask should be."));
843
844 return mask;
845 }
846
847
848
849 template <int dim, int spacedim>
852 const ComponentMask &component_mask) const
853 {
854 Assert(this->size() > 0,
855 ExcMessage("This collection contains no finite element."));
856
857 // get the mask from the first element of the collection
858 const BlockMask mask = (*this)[0].block_mask(component_mask);
859
860 // but then also verify that the other elements of the collection
861 // would return the same mask
862 for (unsigned int c = 1; c < this->size(); ++c)
863 Assert(mask == (*this)[c].block_mask(component_mask),
864 ExcMessage("Not all elements of this collection agree on what "
865 "the appropriate mask should be."));
866
867 return mask;
868 }
869
870
871
872 template <int dim, int spacedim>
873 unsigned int
875 {
876 Assert(this->size() > 0, ExcNoFiniteElements());
877
878 const unsigned int nb = this->operator[](0).n_blocks();
879 for (unsigned int i = 1; i < this->size(); ++i)
880 Assert(this->operator[](i).n_blocks() == nb,
881 ExcMessage("Not all finite elements in this collection have "
882 "the same number of components."));
883
884 return nb;
885 }
886} // namespace hp
887
888
889
890// explicit instantiations
891#include "hp/fe_collection.inst"
892
893
*  *  for(const auto &cell :triangulation.active_cell_iterators())
***mech_lbc_system increment_interpolation_handlers push_back(scale_z_handler)
unsigned int n_components() const
virtual std::unique_ptr< FiniteElement< dim, spacedim > > clone() const =0
std::vector< std::map< unsigned int, unsigned int > > hp_vertex_dof_identities(const std::set< unsigned int > &fes) const
unsigned int previous_in_hierarchy(const unsigned int fe_index) const
std::vector< unsigned int > get_hierarchy_sequence(const unsigned int fe_index=0) const
unsigned int find_dominating_fe_extended(const std::set< unsigned int > &fes, const unsigned int codim=0) const
std::vector< std::map< unsigned int, unsigned int > > hp_quad_dof_identities(const std::set< std::pair< unsigned int, unsigned int > > &fes_and_faces) const
const MappingCollection< dim, spacedim > & get_reference_cell_default_linear_mapping() const
std::set< unsigned int > find_common_fes(const std::set< unsigned int > &fes, const unsigned int codim=0) const
void push_back(const FiniteElement< dim, spacedim > &new_fe)
unsigned int next_in_hierarchy(const unsigned int fe_index) const
std::shared_ptr< MappingCollection< dim, spacedim > > reference_cell_default_linear_mapping
unsigned int find_dominating_fe(const std::set< unsigned int > &fes, const unsigned int codim=0) const
std::set< unsigned int > find_enclosing_fes(const std::set< unsigned int > &fes, const unsigned int codim=0) const
unsigned int find_dominated_fe(const std::set< unsigned int > &fes, const unsigned int codim=0) const
ComponentMask component_mask(const FEValuesExtractors::Scalar &scalar) const
std::vector< std::map< unsigned int, unsigned int > > hp_line_dof_identities(const std::set< unsigned int > &fes) const
void set_hierarchy(const std::function< unsigned int(const typename hp::FECollection< dim, spacedim > &, const unsigned int)> &next, const std::function< unsigned int(const typename hp::FECollection< dim, spacedim > &, const unsigned int)> &prev)
unsigned int find_dominated_fe_extended(const std::set< unsigned int > &fes, const unsigned int codim=0) const
unsigned int n_blocks() const
BlockMask block_mask(const FEValuesExtractors::Scalar &scalar) const
#define DEAL_II_NAMESPACE_OPEN
Definition config.h:38
constexpr bool running_in_debug_mode()
Definition config.h:76
#define DEAL_II_NAMESPACE_CLOSE
Definition config.h:39
Point< 2 > second
Definition grid_out.cc:4640
Point< 2 > first
Definition grid_out.cc:4639
static ::ExceptionBase & ExcEmptyObject()
#define Assert(cond, exc)
static ::ExceptionBase & ExcImpossibleInDim(int arg1)
#define AssertIndexRange(index, range)
static ::ExceptionBase & ExcInternalError()
static ::ExceptionBase & ExcMessage(std::string arg1)
std::size_t size
Definition mpi.cc:733
Definition hp.h:115
constexpr types::fe_index invalid_fe_index
Definition types.h:250
void prev(std::tuple< I1, I2 > &t)