Reference documentation for deal.II version Git b927e2c03c 2022-01-17 18:55:39 -0500
\(\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\}}\)
fe_collection.cc
Go to the documentation of this file.
1 // ---------------------------------------------------------------------
2 //
3 // Copyright (C) 2003 - 2021 by the deal.II authors
4 //
5 // This file is part of the deal.II library.
6 //
7 // The deal.II library is free software; you can use it, redistribute
8 // it, and/or modify it under the terms of the GNU Lesser General
9 // Public License as published by the Free Software Foundation; either
10 // version 2.1 of the License, or (at your option) any later version.
11 // The full text of the license can be found in the file LICENSE.md at
12 // the top level directory of deal.II.
13 //
14 // ---------------------------------------------------------------------
15 
16 
18 
20 
21 #include <set>
22 
23 
24 
26 
27 namespace hp
28 {
29  template <int dim, int spacedim>
31  {
32  set_default_hierarchy();
33  }
34 
35 
36 
37  template <int dim, int spacedim>
40  : FECollection()
41  {
42  push_back(fe);
43  }
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->size() == 0 ||
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 
74  Collection<FiniteElement<dim, spacedim>>::push_back(new_fe.clone());
75  }
76 
77 
78 
79  template <int dim, int spacedim>
80  std::set<unsigned int>
82  const std::set<unsigned int> &fes,
83  const unsigned int codim) const
84  {
85 #ifdef DEBUG
86  // Validate user inputs.
87  Assert(codim <= dim, ExcImpossibleInDim(dim));
88  Assert(this->size() > 0, ExcEmptyObject());
89  for (const auto &fe : fes)
90  AssertIndexRange(fe, this->size());
91 #endif
92 
93  // Check if any element of this FECollection is able to dominate all
94  // elements of @p fes. If one was found, we add it to the set of
95  // dominating elements.
96  std::set<unsigned int> dominating_fes;
97  for (unsigned int current_fe = 0; current_fe < this->size(); ++current_fe)
98  {
99  // Check if current_fe can dominate all elements in @p fes.
102  for (const auto &other_fe : fes)
103  domination =
104  domination &
105  this->operator[](current_fe)
106  .compare_for_domination(this->operator[](other_fe), codim);
107 
108  // If current_fe dominates, add it to the set.
111  /*covers cases like {Q2,Q3,Q1,Q1} with fes={2,3}*/))
112  dominating_fes.insert(current_fe);
113  }
114  return dominating_fes;
115  }
116 
117 
118 
119  template <int dim, int spacedim>
120  std::set<unsigned int>
122  const std::set<unsigned int> &fes,
123  const unsigned int codim) const
124  {
125 #ifdef DEBUG
126  // Validate user inputs.
127  Assert(codim <= dim, ExcImpossibleInDim(dim));
128  Assert(this->size() > 0, ExcEmptyObject());
129  for (const auto &fe : fes)
130  AssertIndexRange(fe, this->size());
131 #endif
132 
133  // Check if any element of this FECollection is dominated by all
134  // elements of @p fes. If one was found, we add it to the set of
135  // dominated elements.
136  std::set<unsigned int> dominated_fes;
137  for (unsigned int current_fe = 0; current_fe < this->size(); ++current_fe)
138  {
139  // Check if current_fe is dominated by all other elements in @p fes.
142  for (const auto &other_fe : fes)
143  domination =
144  domination &
145  this->operator[](current_fe)
146  .compare_for_domination(this->operator[](other_fe), codim);
147 
148  // If current_fe is dominated, add it to the set.
151  /*covers cases like {Q2,Q3,Q1,Q1} with fes={2,3}*/))
152  dominated_fes.insert(current_fe);
153  }
154  return dominated_fes;
155  }
156 
157 
158 
159  template <int dim, int spacedim>
160  unsigned int
162  const std::set<unsigned int> &fes,
163  const unsigned int codim) const
164  {
165  // If the set of elements contains only a single element,
166  // then this very element is considered to be the dominating one.
167  if (fes.size() == 1)
168  return *fes.begin();
169 
170 #ifdef DEBUG
171  // Validate user inputs.
172  Assert(codim <= dim, ExcImpossibleInDim(dim));
173  Assert(this->size() > 0, ExcEmptyObject());
174  for (const auto &fe : fes)
175  AssertIndexRange(fe, this->size());
176 #endif
177 
178  // There may also be others, in which case we'll check if any of these
179  // elements is able to dominate all others. If one was found, we stop
180  // looking further and return the dominating element.
181  for (const auto &current_fe : fes)
182  {
183  // Check if current_fe can dominate all elements in @p fes.
186  for (const auto &other_fe : fes)
187  if (current_fe != other_fe)
188  domination =
189  domination &
190  this->operator[](current_fe)
191  .compare_for_domination(this->operator[](other_fe), codim);
192 
193  // If current_fe dominates, return its index.
196  /*covers cases like {Q2,Q3,Q1,Q1} with fes={2,3}*/))
197  return current_fe;
198  }
199 
200  // If we couldn't find the dominating object, return an invalid one.
202  }
203 
204 
205 
206  template <int dim, int spacedim>
207  unsigned int
209  const std::set<unsigned int> &fes,
210  const unsigned int codim) const
211  {
212  // If the set of elements contains only a single element,
213  // then this very element is considered to be the dominated one.
214  if (fes.size() == 1)
215  return *fes.begin();
216 
217 #ifdef DEBUG
218  // Validate user inputs.
219  Assert(codim <= dim, ExcImpossibleInDim(dim));
220  Assert(this->size() > 0, ExcEmptyObject());
221  for (const auto &fe : fes)
222  AssertIndexRange(fe, this->size());
223 #endif
224 
225  // There may also be others, in which case we'll check if any of these
226  // elements is dominated by all others. If one was found, we stop
227  // looking further and return the dominated element.
228  for (const auto &current_fe : fes)
229  {
230  // Check if current_fe is dominated by all other elements in @p fes.
233  for (const auto &other_fe : fes)
234  if (current_fe != other_fe)
235  domination =
236  domination &
237  this->operator[](current_fe)
238  .compare_for_domination(this->operator[](other_fe), codim);
239 
240  // If current_fe is dominated, return its index.
243  /*covers cases like {Q2,Q3,Q1,Q1} with fes={2,3}*/))
244  return current_fe;
245  }
246 
247  // If we couldn't find the dominated object, return an invalid one.
249  }
250 
251 
252 
253  template <int dim, int spacedim>
254  unsigned int
256  const std::set<unsigned int> &fes,
257  const unsigned int codim) const
258  {
259  unsigned int fe_index = find_dominating_fe(fes, codim);
260 
261  if (fe_index == numbers::invalid_unsigned_int)
262  {
263  const std::set<unsigned int> dominating_fes =
264  find_common_fes(fes, codim);
265  fe_index = find_dominated_fe(dominating_fes, codim);
266  }
267 
268  return fe_index;
269  }
270 
271 
272 
273  template <int dim, int spacedim>
274  unsigned int
276  const std::set<unsigned int> &fes,
277  const unsigned int codim) const
278  {
279  unsigned int fe_index = find_dominated_fe(fes, codim);
280 
281  if (fe_index == numbers::invalid_unsigned_int)
282  {
283  const std::set<unsigned int> dominated_fes =
284  find_enclosing_fes(fes, codim);
285  fe_index = find_dominating_fe(dominated_fes, codim);
286  }
287 
288  return fe_index;
289  }
290 
291 
292 
293  namespace
294  {
299  std::vector<std::map<unsigned int, unsigned int>>
300  compute_hp_dof_identities(
301  const std::set<unsigned int> &fes,
302  const std::function<std::vector<std::pair<unsigned int, unsigned int>>(
303  const unsigned int,
304  const unsigned int)> & query_identities)
305  {
306  // Let's deal with the easy cases first. If the set of fe indices is empty
307  // or has only one entry, then there are no identities:
308  if (fes.size() <= 1)
309  return {};
310 
311  // If the set has two entries, then the
312  // FiniteElement::hp_*_dof_identities() function directly returns what we
313  // need. We just need to prefix its output with the respective fe indices:
314  if (fes.size() == 2)
315  {
316  const unsigned int fe_index_1 = *fes.begin();
317  const unsigned int fe_index_2 = *(++fes.begin());
318  const auto reduced_identities =
319  query_identities(fe_index_1, fe_index_2);
320 
321  std::vector<std::map<unsigned int, unsigned int>> complete_identities;
322 
323  for (const auto &reduced_identity : reduced_identities)
324  {
325  // Each identity returned by query_identities() is a pair of
326  // dof indices. Prefix each with its fe index and put the result
327  // into a vector
328  std::map<unsigned int, unsigned int> complete_identity = {
329  {fe_index_1, reduced_identity.first},
330  {fe_index_2, reduced_identity.second}};
331  complete_identities.emplace_back(std::move(complete_identity));
332  }
333 
334  return complete_identities;
335  }
336 
337  // Now for the general case of three or more elements:
338  //
339  // Consider all degrees of freedom of the identified elements (represented
340  // via (fe_index,dof_index) pairs) as the nodes in a graph. Then draw
341  // edges for all DoFs that are identified based on what the elements
342  // selected in the argument say. Let us first build this graph, where we
343  // only store the edges of the graph, and as a consequence ignore nodes
344  // (DoFs) that simply don't show up at all in any of the identities:
345  using Node = std::pair<unsigned int, unsigned int>;
346  using Edge = std::pair<Node, Node>;
347  using Graph = std::set<Edge>;
348 
349  Graph identities_graph;
350  for (const unsigned int fe_index_1 : fes)
351  for (const unsigned int fe_index_2 : fes)
352  if (fe_index_1 != fe_index_2)
353  for (const auto &identity :
354  query_identities(fe_index_1, fe_index_2))
355  identities_graph.emplace(Edge(Node(fe_index_1, identity.first),
356  Node(fe_index_2, identity.second)));
357 
358 #ifdef DEBUG
359  // Now verify that indeed the graph is symmetric: If one element
360  // declares that certain ones of its DoFs are to be unified with those
361  // of the other, then the other one should agree with this. As a
362  // consequence of this test succeeding, we know that the graph is actually
363  // undirected.
364  for (const auto &edge : identities_graph)
365  Assert(identities_graph.find({edge.second, edge.first}) !=
366  identities_graph.end(),
367  ExcInternalError());
368 #endif
369 
370  // The next step is that we ought to verify that if there is an identity
371  // between (fe1,dof1) and (fe2,dof2), as well as with (fe2,dof2) and
372  // (fe3,dof3), then there should also be an identity between (fe1,dof1)
373  // and (fe3,dof3). The same logic should apply to chains of four
374  // identities.
375  //
376  // This means that the graph we have built above must be composed of a
377  // collection of complete sub-graphs (complete = each possible edge in the
378  // sub-graph exists) -- or, using a different term, that the graph
379  // consists of a number of "cliques". Each of these cliques is then one
380  // extended identity between two or more DoFs, and these are the ones that
381  // we will want to return.
382  //
383  // To ascertain that this is true, and to figure out what we want to
384  // return, we need to divide the graph into its sub-graphs and then ensure
385  // that each sub-graph is indeed a clique. This is slightly cumbersome,
386  // but can be done as follows:
387  // - pick one edge 'e' of G
388  // - add e=(n1,n2) to the sub-graph SG
389  // - set N={n1,n2}
390  // - loop over the remainder of the edges 'e' of the graph:
391  // - if 'e' has one or both nodes in N:
392  // - add 'e' to SG and
393  // - add its two nodes to N (they may already be in there)
394  // - remove 'e' from G
395  //
396  // In general, this may not find the entire sub-graph if the edges are
397  // stored in random order. For example, if the graph consisted of the
398  // following edges in this order:
399  // (a,b)
400  // (c,d)
401  // (a,c)
402  // (a,d)
403  // (b,c)
404  // (b,d)
405  // then the graph itself is a clique, but the algorithm outlined above
406  // would skip the edge (c,d) because neither of the nodes are already
407  // in the set N which at that point is still (a,b).
408  //
409  // But, we store the graph with a std::set, which stored edges in sorted
410  // order where the order is the lexicographic order of nodes. This ensures
411  // that we really capture all edges that correspond to a sub-graph (but
412  // we will assert this as well).
413  //
414  // (For programmatic reasons, we skip the removal of 'e' from G in a first
415  // run through because it modifies the graph and thus invalidates
416  // iterators. But because SG stores all of these edges, we can remove them
417  // all from G after collecting the edges in SG.)
418  std::vector<std::map<unsigned int, unsigned int>> identities;
419  while (identities_graph.size() > 0)
420  {
421  Graph sub_graph; // SG
422  std::set<Node> sub_graph_nodes; // N
423 
424  sub_graph.emplace(*identities_graph.begin());
425  sub_graph_nodes.emplace(identities_graph.begin()->first);
426  sub_graph_nodes.emplace(identities_graph.begin()->second);
427 
428  for (const Edge &e : identities_graph)
429  if ((sub_graph_nodes.find(e.first) != sub_graph_nodes.end()) ||
430  (sub_graph_nodes.find(e.second) != sub_graph_nodes.end()))
431  {
432  sub_graph.insert(e);
433  sub_graph_nodes.insert(e.first);
434  sub_graph_nodes.insert(e.second);
435  }
436 
437  // We have now obtained a sub-graph from the overall graph.
438  // Now remove it from the bigger graph
439  for (const Edge &e : sub_graph)
440  identities_graph.erase(e);
441 
442 #if DEBUG
443  // There are three checks we ought to perform:
444  // - That the sub-graph is undirected, i.e. that every edge appears
445  // in both directions
446  for (const auto &edge : sub_graph)
447  Assert(sub_graph.find({edge.second, edge.first}) != sub_graph.end(),
448  ExcInternalError());
449 
450  // - None of the nodes in the sub-graph should have appeared in
451  // any of the other sub-graphs. If they did, then we have a bug
452  // in extracting sub-graphs. This is actually more easily checked
453  // the other way around: none of the nodes of the sub-graph we
454  // just extracted should be in any of the edges of the *remaining*
455  // graph
456  for (const Node &n : sub_graph_nodes)
457  for (const Edge &e : identities_graph)
458  Assert((n != e.first) && (n != e.second), ExcInternalError());
459  // - Second, the sub-graph we just extracted needs to be complete,
460  // i.e.,
461  // be a "clique". We check this by counting how many edges it has.
462  // for 'n' nodes in 'N', we need to have n*(n-1) edges (we store
463  // both directed edges).
464  Assert(sub_graph.size() ==
465  sub_graph_nodes.size() * (sub_graph_nodes.size() - 1),
466  ExcInternalError());
467 #endif
468 
469  // At this point we're sure that we have extracted a complete
470  // sub-graph ("clique"). The DoFs involved are all identical then, and
471  // we will store this identity so we can return it later.
472  //
473  // The sub-graph is given as a set of Node objects, which is just
474  // a collection of (fe_index,dof_index) pairs. Because each
475  // fe_index can only appear once there, a better data structure
476  // is a std::map from fe_index to dof_index, which can conveniently
477  // be initialized from a range of iterators to pairs:
478  identities.emplace_back(sub_graph_nodes.begin(),
479  sub_graph_nodes.end());
480  Assert(identities.back().size() == sub_graph_nodes.size(),
481  ExcInternalError());
482  }
483 
484  return identities;
485  }
486  } // namespace
487 
488 
489 
490  template <int dim, int spacedim>
491  std::vector<std::map<unsigned int, unsigned int>>
493  const std::set<unsigned int> &fes) const
494  {
495  auto query_vertex_dof_identities = [this](const unsigned int fe_index_1,
496  const unsigned int fe_index_2) {
497  return (*this)[fe_index_1].hp_vertex_dof_identities((*this)[fe_index_2]);
498  };
499  return compute_hp_dof_identities(fes, query_vertex_dof_identities);
500  }
501 
502 
503 
504  template <int dim, int spacedim>
505  std::vector<std::map<unsigned int, unsigned int>>
507  const std::set<unsigned int> &fes) const
508  {
509  auto query_line_dof_identities = [this](const unsigned int fe_index_1,
510  const unsigned int fe_index_2) {
511  return (*this)[fe_index_1].hp_line_dof_identities((*this)[fe_index_2]);
512  };
513  return compute_hp_dof_identities(fes, query_line_dof_identities);
514  }
515 
516 
517 
518  template <int dim, int spacedim>
519  std::vector<std::map<unsigned int, unsigned int>>
521  const std::set<unsigned int> &fes,
522  const unsigned int face_no) const
523  {
524  auto query_quad_dof_identities = [this,
525  face_no](const unsigned int fe_index_1,
526  const unsigned int fe_index_2) {
527  return (*this)[fe_index_1].hp_quad_dof_identities((*this)[fe_index_2],
528  face_no);
529  };
530  return compute_hp_dof_identities(fes, query_quad_dof_identities);
531  }
532 
533 
534 
535  template <int dim, int spacedim>
536  void
538  const std::function<
539  unsigned int(const typename hp::FECollection<dim, spacedim> &,
540  const unsigned int)> &next,
541  const std::function<
542  unsigned int(const typename hp::FECollection<dim, spacedim> &,
543  const unsigned int)> &prev)
544  {
545  // copy hierarchy functions
546  hierarchy_next = next;
547  hierarchy_prev = prev;
548  }
549 
550 
551 
552  template <int dim, int spacedim>
553  void
555  {
556  // establish hierarchy corresponding to order of indices
557  set_hierarchy(&DefaultHierarchy::next_index,
558  &DefaultHierarchy::previous_index);
559  }
560 
561 
562 
563  template <int dim, int spacedim>
564  std::vector<unsigned int>
566  const unsigned int fe_index) const
567  {
568  AssertIndexRange(fe_index, this->size());
569 
570  std::deque<unsigned int> sequence = {fe_index};
571 
572  // get predecessors
573  {
574  unsigned int front = sequence.front();
575  unsigned int previous;
576  while ((previous = previous_in_hierarchy(front)) != front)
577  {
578  sequence.push_front(previous);
579  front = previous;
580 
581  Assert(sequence.size() <= this->size(),
582  ExcMessage(
583  "The registered hierarchy is not terminated: "
584  "previous_in_hierarchy() does not stop at a final index."));
585  }
586  }
587 
588  // get successors
589  {
590  unsigned int back = sequence.back();
591  unsigned int next;
592  while ((next = next_in_hierarchy(back)) != back)
593  {
594  sequence.push_back(next);
595  back = next;
596 
597  Assert(sequence.size() <= this->size(),
598  ExcMessage(
599  "The registered hierarchy is not terminated: "
600  "next_in_hierarchy() does not stop at a final index."));
601  }
602  }
603 
604  return {sequence.begin(), sequence.end()};
605  }
606 
607 
608 
609  template <int dim, int spacedim>
610  unsigned int
612  const unsigned int fe_index) const
613  {
614  AssertIndexRange(fe_index, this->size());
615 
616  const unsigned int new_fe_index = hierarchy_next(*this, fe_index);
617  AssertIndexRange(new_fe_index, this->size());
618 
619  return new_fe_index;
620  }
621 
622 
623 
624  template <int dim, int spacedim>
625  unsigned int
627  const unsigned int fe_index) const
628  {
629  AssertIndexRange(fe_index, this->size());
630 
631  const unsigned int new_fe_index = hierarchy_prev(*this, fe_index);
632  AssertIndexRange(new_fe_index, this->size());
633 
634  return new_fe_index;
635  }
636 
637 
638 
639  template <int dim, int spacedim>
642  const FEValuesExtractors::Scalar &scalar) const
643  {
644  Assert(this->size() > 0,
645  ExcMessage("This collection contains no finite element."));
646 
647  // get the mask from the first element of the collection
648  const ComponentMask mask = (*this)[0].component_mask(scalar);
649 
650  // but then also verify that the other elements of the collection
651  // would return the same mask
652  for (unsigned int c = 1; c < this->size(); ++c)
653  Assert(mask == (*this)[c].component_mask(scalar), ExcInternalError());
654 
655  return mask;
656  }
657 
658 
659  template <int dim, int spacedim>
662  const FEValuesExtractors::Vector &vector) const
663  {
664  Assert(this->size() > 0,
665  ExcMessage("This collection contains no finite element."));
666 
667  // get the mask from the first element of the collection
668  const ComponentMask mask = (*this)[0].component_mask(vector);
669 
670  // but then also verify that the other elements of the collection
671  // would return the same mask
672  for (unsigned int c = 1; c < this->size(); ++c)
673  Assert(mask == (*this)[c].component_mask(vector), ExcInternalError());
674 
675  return mask;
676  }
677 
678 
679  template <int dim, int spacedim>
682  const FEValuesExtractors::SymmetricTensor<2> &sym_tensor) const
683  {
684  Assert(this->size() > 0,
685  ExcMessage("This collection contains no finite element."));
686 
687  // get the mask from the first element of the collection
688  const ComponentMask mask = (*this)[0].component_mask(sym_tensor);
689 
690  // but then also verify that the other elements of the collection
691  // would return the same mask
692  for (unsigned int c = 1; c < this->size(); ++c)
693  Assert(mask == (*this)[c].component_mask(sym_tensor), ExcInternalError());
694 
695  return mask;
696  }
697 
698 
699  template <int dim, int spacedim>
702  {
703  Assert(this->size() > 0,
704  ExcMessage("This collection contains no finite element."));
705 
706  // get the mask from the first element of the collection
707  const ComponentMask mask = (*this)[0].component_mask(block_mask);
708 
709  // but then also verify that the other elements of the collection
710  // would return the same mask
711  for (unsigned int c = 1; c < this->size(); ++c)
712  Assert(mask == (*this)[c].component_mask(block_mask),
713  ExcMessage("Not all elements of this collection agree on what "
714  "the appropriate mask should be."));
715 
716  return mask;
717  }
718 
719 
720  template <int dim, int spacedim>
721  BlockMask
723  const FEValuesExtractors::Scalar &scalar) const
724  {
725  Assert(this->size() > 0,
726  ExcMessage("This collection contains no finite element."));
727 
728  // get the mask from the first element of the collection
729  const BlockMask mask = (*this)[0].block_mask(scalar);
730 
731  // but then also verify that the other elements of the collection
732  // would return the same mask
733  for (unsigned int c = 1; c < this->size(); ++c)
734  Assert(mask == (*this)[c].block_mask(scalar),
735  ExcMessage("Not all elements of this collection agree on what "
736  "the appropriate mask should be."));
737 
738  return mask;
739  }
740 
741 
742  template <int dim, int spacedim>
743  BlockMask
745  const FEValuesExtractors::Vector &vector) const
746  {
747  Assert(this->size() > 0,
748  ExcMessage("This collection contains no finite element."));
749 
750  // get the mask from the first element of the collection
751  const BlockMask mask = (*this)[0].block_mask(vector);
752 
753  // but then also verify that the other elements of the collection
754  // would return the same mask
755  for (unsigned int c = 1; c < this->size(); ++c)
756  Assert(mask == (*this)[c].block_mask(vector),
757  ExcMessage("Not all elements of this collection agree on what "
758  "the appropriate mask should be."));
759 
760  return mask;
761  }
762 
763 
764  template <int dim, int spacedim>
765  BlockMask
767  const FEValuesExtractors::SymmetricTensor<2> &sym_tensor) const
768  {
769  Assert(this->size() > 0,
770  ExcMessage("This collection contains no finite element."));
771 
772  // get the mask from the first element of the collection
773  const BlockMask mask = (*this)[0].block_mask(sym_tensor);
774 
775  // but then also verify that the other elements of the collection
776  // would return the same mask
777  for (unsigned int c = 1; c < this->size(); ++c)
778  Assert(mask == (*this)[c].block_mask(sym_tensor),
779  ExcMessage("Not all elements of this collection agree on what "
780  "the appropriate mask should be."));
781 
782  return mask;
783  }
784 
785 
786 
787  template <int dim, int spacedim>
788  BlockMask
790  const ComponentMask &component_mask) const
791  {
792  Assert(this->size() > 0,
793  ExcMessage("This collection contains no finite element."));
794 
795  // get the mask from the first element of the collection
796  const BlockMask mask = (*this)[0].block_mask(component_mask);
797 
798  // but then also verify that the other elements of the collection
799  // would return the same mask
800  for (unsigned int c = 1; c < this->size(); ++c)
801  Assert(mask == (*this)[c].block_mask(component_mask),
802  ExcMessage("Not all elements of this collection agree on what "
803  "the appropriate mask should be."));
804 
805  return mask;
806  }
807 
808 
809 
810  template <int dim, int spacedim>
811  unsigned int
813  {
814  Assert(this->size() > 0, ExcNoFiniteElements());
815 
816  const unsigned int nb = this->operator[](0).n_blocks();
817  for (unsigned int i = 1; i < this->size(); ++i)
818  Assert(this->operator[](i).n_blocks() == nb,
819  ExcMessage("Not all finite elements in this collection have "
820  "the same number of components."));
821 
822  return nb;
823  }
824 } // namespace hp
825 
826 
827 
828 // explicit instantiations
829 #include "fe_collection.inst"
830 
831 
static const unsigned int invalid_unsigned_int
Definition: types.h:196
std::vector< std::map< unsigned int, unsigned int > > hp_quad_dof_identities(const std::set< unsigned int > &fes, const unsigned int face_no=0) const
SymmetricTensor< 2, dim, Number > e(const Tensor< 2, dim, Number > &F)
std::vector< bool > component_mask
#define AssertIndexRange(index, range)
Definition: exceptions.h:1720
adjacency_list< vecS, vecS, undirectedS, property< vertex_color_t, default_color_type, property< vertex_degree_t, int > >> Graph
virtual std::unique_ptr< FiniteElement< dim, spacedim > > clone() const =0
static ::ExceptionBase & ExcMessage(std::string arg1)
static ::ExceptionBase & ExcImpossibleInDim(int arg1)
#define Assert(cond, exc)
Definition: exceptions.h:1461
#define DEAL_II_NAMESPACE_CLOSE
Definition: config.h:407
std::enable_if< IsBlockVector< VectorType >::value, unsigned int >::type n_blocks(const VectorType &vector)
Definition: operators.h:50
Definition: hp.h:117
std::vector< bool > block_mask
Definition: block_mask.h:215
unsigned int n_components() const
#define DEAL_II_NAMESPACE_OPEN
Definition: config.h:406
static ::ExceptionBase & ExcEmptyObject()
std::vector< std::map< unsigned int, unsigned int > > hp_vertex_dof_identities(const std::set< unsigned int > &fes) const
static ::ExceptionBase & ExcInternalError()
std::vector< std::map< unsigned int, unsigned int > > hp_line_dof_identities(const std::set< unsigned int > &fes) const