deal.II version GIT relicensing-2850-g1646a780ac 2025-03-17 15:10:00+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
dof_tools_constraints.cc
Go to the documentation of this file.
1// ------------------------------------------------------------------------
2//
3// SPDX-License-Identifier: LGPL-2.1-or-later
4// Copyright (C) 2013 - 2024 by the deal.II authors
5//
6// This file is part of the deal.II library.
7//
8// Part of the source code is dual licensed under Apache-2.0 WITH
9// LLVM-exception OR LGPL-2.1-or-later. Detailed license information
10// governing the source code and code contributions can be found in
11// LICENSE.md and CONTRIBUTING.md at the top level directory of deal.II.
12//
13// ------------------------------------------------------------------------
14
15#include <deal.II/base/table.h>
19
23
24#include <deal.II/fe/fe.h>
25#include <deal.II/fe/fe_tools.h>
27
31#include <deal.II/grid/tria.h>
33
36
38#include <deal.II/lac/vector.h>
39
40#ifdef DEAL_II_WITH_MPI
42#endif
43
44#include <algorithm>
45#include <array>
46#include <memory>
47#include <numeric>
48
50
51
52
53namespace DoFTools
54{
55 namespace internal
56 {
57 namespace
58 {
59 inline bool
60 check_primary_dof_list(
61 const FullMatrix<double> &face_interpolation_matrix,
62 const std::vector<types::global_dof_index> &primary_dof_list)
63 {
64 const unsigned int N = primary_dof_list.size();
65
66 FullMatrix<double> tmp(N, N);
67 for (unsigned int i = 0; i < N; ++i)
68 for (unsigned int j = 0; j < N; ++j)
69 tmp(i, j) = face_interpolation_matrix(primary_dof_list[i], j);
70
71 // then use the algorithm from FullMatrix::gauss_jordan on this matrix
72 // to find out whether it is singular. the algorithm there does pivoting
73 // and at the end swaps rows back into their proper order -- we omit
74 // this step here, since we don't care about the inverse matrix, all we
75 // care about is whether the matrix is regular or singular
76
77 // first get an estimate of the size of the elements of this matrix, for
78 // later checks whether the pivot element is large enough, or whether we
79 // have to fear that the matrix is not regular
80 double diagonal_sum = 0;
81 for (unsigned int i = 0; i < N; ++i)
82 diagonal_sum += std::fabs(tmp(i, i));
83 const double typical_diagonal_element = diagonal_sum / N;
84
85 // initialize the array that holds the permutations that we find during
86 // pivot search
87 std::vector<unsigned int> p(N);
88 for (unsigned int i = 0; i < N; ++i)
89 p[i] = i;
90
91 for (unsigned int j = 0; j < N; ++j)
92 {
93 // pivot search: search that part of the line on and right of the
94 // diagonal for the largest element
95 double max = std::fabs(tmp(j, j));
96 unsigned int r = j;
97 for (unsigned int i = j + 1; i < N; ++i)
98 {
99 if (std::fabs(tmp(i, j)) > max)
100 {
101 max = std::fabs(tmp(i, j));
102 r = i;
103 }
104 }
105 // check whether the pivot is too small. if that is the case, then
106 // the matrix is singular and we shouldn't use this set of primary
107 // dofs
108 if (max < 1.e-12 * typical_diagonal_element)
109 return false;
110
111 // row interchange
112 if (r > j)
113 {
114 for (unsigned int k = 0; k < N; ++k)
115 std::swap(tmp(j, k), tmp(r, k));
116
117 std::swap(p[j], p[r]);
118 }
119
120 // transformation
121 const double hr = 1. / tmp(j, j);
122 tmp(j, j) = hr;
123 for (unsigned int k = 0; k < N; ++k)
124 {
125 if (k == j)
126 continue;
127 for (unsigned int i = 0; i < N; ++i)
128 {
129 if (i == j)
130 continue;
131 tmp(i, k) -= tmp(i, j) * tmp(j, k) * hr;
132 }
133 }
134 for (unsigned int i = 0; i < N; ++i)
135 {
136 tmp(i, j) *= hr;
137 tmp(j, i) *= -hr;
138 }
139 tmp(j, j) = hr;
140 }
141
142 // everything went fine, so we can accept this set of primary dofs (at
143 // least as far as they have already been collected)
144 return true;
145 }
146
147
148
170 template <int dim, int spacedim>
171 void
172 select_primary_dofs_for_face_restriction(
175 const FullMatrix<double> &face_interpolation_matrix,
176 std::vector<bool> &primary_dof_mask)
177 {
178 // TODO: the implementation makes the assumption that all faces have the
179 // same number of dofs
182 const unsigned int face_no = 0;
183 (void)face_no;
184
185 Assert(fe1.n_dofs_per_face(face_no) >= fe2.n_dofs_per_face(face_no),
187 AssertDimension(primary_dof_mask.size(), fe1.n_dofs_per_face(face_no));
188
193 Assert((dim < 3) ||
194 (fe2.n_dofs_per_quad(face_no) <= fe1.n_dofs_per_quad(face_no)),
196
197 // the idea here is to designate as many DoFs in fe1 per object (vertex,
198 // line, quad) as primary as there are such dofs in fe2 (indices are
199 // int, because we want to avoid the 'unsigned int < 0 is always false
200 // warning for the cases at the bottom in 1d and 2d)
201 //
202 // as mentioned in the paper, it is not always easy to find a set of
203 // primary dofs that produces an invertible matrix. to this end, we
204 // check in each step whether the matrix is still invertible and simply
205 // discard this dof if the matrix is not invertible anymore.
206 //
207 // the cases where we did have trouble in the past were with adding more
208 // quad dofs when Q3 and Q4 elements meet at a refined face in 3d (see
209 // the hp/crash_12 test that tests that we can do exactly this, and
210 // failed before we had code to compensate for this case). the other
211 // case are system elements: if we have say a Q1Q2 vs a Q2Q3 element,
212 // then we can't just take all primary dofs on a line from a single base
213 // element, since the shape functions of that base element are
214 // independent of that of the other one. this latter case shows up when
215 // running hp/hp_constraints_q_system_06
216
217 std::vector<types::global_dof_index> primary_dof_list;
218 unsigned int index = 0;
219 for (int v = 0;
220 v < static_cast<signed int>(GeometryInfo<dim>::vertices_per_face);
221 ++v)
222 {
223 unsigned int dofs_added = 0;
224 unsigned int i = 0;
225 while (dofs_added < fe2.n_dofs_per_vertex())
226 {
227 // make sure that we were able to find a set of primary dofs and
228 // that the code down below didn't just reject all our efforts
230
231 // tentatively push this vertex dof
232 primary_dof_list.push_back(index + i);
233
234 // then see what happens. if it succeeds, fine
235 if (check_primary_dof_list(face_interpolation_matrix,
236 primary_dof_list) == true)
237 ++dofs_added;
238 else
239 // well, it didn't. simply pop that dof from the list again
240 // and try with the next dof
241 primary_dof_list.pop_back();
242
243 // forward counter by one
244 ++i;
245 }
246 index += fe1.n_dofs_per_vertex();
247 }
248
249 for (int l = 0;
250 l < static_cast<signed int>(GeometryInfo<dim>::lines_per_face);
251 ++l)
252 {
253 // same algorithm as above
254 unsigned int dofs_added = 0;
255 unsigned int i = 0;
256 while (dofs_added < fe2.n_dofs_per_line())
257 {
259
260 primary_dof_list.push_back(index + i);
261 if (check_primary_dof_list(face_interpolation_matrix,
262 primary_dof_list) == true)
263 ++dofs_added;
264 else
265 primary_dof_list.pop_back();
266
267 ++i;
268 }
269 index += fe1.n_dofs_per_line();
270 }
271
272 for (int q = 0;
273 q < static_cast<signed int>(GeometryInfo<dim>::quads_per_face);
274 ++q)
275 {
276 // same algorithm as above
277 unsigned int dofs_added = 0;
278 unsigned int i = 0;
279 while (dofs_added < fe2.n_dofs_per_quad(q))
280 {
282
283 primary_dof_list.push_back(index + i);
284 if (check_primary_dof_list(face_interpolation_matrix,
285 primary_dof_list) == true)
286 ++dofs_added;
287 else
288 primary_dof_list.pop_back();
289
290 ++i;
291 }
292 index += fe1.n_dofs_per_quad(q);
293 }
294
295 AssertDimension(index, fe1.n_dofs_per_face(face_no));
296 AssertDimension(primary_dof_list.size(), fe2.n_dofs_per_face(face_no));
297
298 // finally copy the list into the mask
299 std::fill(primary_dof_mask.begin(), primary_dof_mask.end(), false);
300 for (const auto dof : primary_dof_list)
301 primary_dof_mask[dof] = true;
302 }
303
304
305
310 template <int dim, int spacedim>
311 void
312 ensure_existence_of_primary_dof_mask(
315 const FullMatrix<double> &face_interpolation_matrix,
316 std::unique_ptr<std::vector<bool>> &primary_dof_mask)
317 {
318 // TODO: the implementation makes the assumption that all faces have the
319 // same number of dofs
322 const unsigned int face_no = 0;
323
324 if (primary_dof_mask == nullptr)
325 {
326 primary_dof_mask =
327 std::make_unique<std::vector<bool>>(fe1.n_dofs_per_face(face_no));
328 select_primary_dofs_for_face_restriction(fe1,
329 fe2,
330 face_interpolation_matrix,
331 *primary_dof_mask);
332 }
333 }
334
335
336
342 template <int dim, int spacedim>
343 void
344 ensure_existence_of_face_matrix(
347 std::unique_ptr<FullMatrix<double>> &matrix)
348 {
349 // TODO: the implementation makes the assumption that all faces have the
350 // same number of dofs
353 const unsigned int face_no = 0;
354
355 if (matrix == nullptr)
356 {
357 matrix = std::make_unique<FullMatrix<double>>(
358 fe2.n_dofs_per_face(face_no), fe1.n_dofs_per_face(face_no));
359 fe1.get_face_interpolation_matrix(fe2, *matrix, face_no);
360 }
361 }
362
363
364
368 template <int dim, int spacedim>
369 void
370 ensure_existence_of_subface_matrix(
373 const unsigned int subface,
374 std::unique_ptr<FullMatrix<double>> &matrix)
375 {
376 // TODO: the implementation makes the assumption that all faces have the
377 // same number of dofs
380 const unsigned int face_no = 0;
381
382 if (matrix == nullptr)
383 {
384 matrix = std::make_unique<FullMatrix<double>>(
385 fe2.n_dofs_per_face(face_no), fe1.n_dofs_per_face(face_no));
387 subface,
388 *matrix,
389 face_no);
390 }
391 }
392
393
394
400 void
401 ensure_existence_of_split_face_matrix(
402 const FullMatrix<double> &face_interpolation_matrix,
403 const std::vector<bool> &primary_dof_mask,
404 std::unique_ptr<std::pair<FullMatrix<double>, FullMatrix<double>>>
405 &split_matrix)
406 {
407 AssertDimension(primary_dof_mask.size(), face_interpolation_matrix.m());
408 Assert(std::count(primary_dof_mask.begin(),
409 primary_dof_mask.end(),
410 true) ==
411 static_cast<signed int>(face_interpolation_matrix.n()),
413
414 if (split_matrix == nullptr)
415 {
416 split_matrix = std::make_unique<
417 std::pair<FullMatrix<double>, FullMatrix<double>>>();
418
419 const unsigned int n_primary_dofs = face_interpolation_matrix.n();
420 const unsigned int n_dofs = face_interpolation_matrix.m();
421
422 Assert(n_primary_dofs <= n_dofs, ExcInternalError());
423
424 // copy and invert the primary component, copy the dependent
425 // component
426 split_matrix->first.reinit(n_primary_dofs, n_primary_dofs);
427 split_matrix->second.reinit(n_dofs - n_primary_dofs,
428 n_primary_dofs);
429
430 unsigned int nth_primary_dof = 0, nth_dependent_dof = 0;
431
432 for (unsigned int i = 0; i < n_dofs; ++i)
433 if (primary_dof_mask[i] == true)
434 {
435 for (unsigned int j = 0; j < n_primary_dofs; ++j)
436 split_matrix->first(nth_primary_dof, j) =
437 face_interpolation_matrix(i, j);
438 ++nth_primary_dof;
439 }
440 else
441 {
442 for (unsigned int j = 0; j < n_primary_dofs; ++j)
443 split_matrix->second(nth_dependent_dof, j) =
444 face_interpolation_matrix(i, j);
445 ++nth_dependent_dof;
446 }
447
448 AssertDimension(nth_primary_dof, n_primary_dofs);
449 AssertDimension(nth_dependent_dof, n_dofs - n_primary_dofs);
450
451 // TODO[WB]: We should make sure very small entries are removed
452 // after inversion
453 split_matrix->first.gauss_jordan();
454 }
455 }
456
457
463 template <int dim, int spacedim>
464 unsigned int
465 n_finite_elements(const DoFHandler<dim, spacedim> &dof_handler)
466 {
467 if (dof_handler.has_hp_capabilities() == true)
468 return dof_handler.get_fe_collection().size();
469 else
470 return 1;
471 }
472
473
474
485 template <typename number1, typename number2>
486 void
487 filter_constraints(
488 const std::vector<types::global_dof_index> &primary_dofs,
489 const std::vector<types::global_dof_index> &dependent_dofs,
490 const FullMatrix<number1> &face_constraints,
491 AffineConstraints<number2> &constraints)
492 {
493 Assert(face_constraints.n() == primary_dofs.size(),
494 ExcDimensionMismatch(primary_dofs.size(), face_constraints.n()));
495 Assert(face_constraints.m() == dependent_dofs.size(),
496 ExcDimensionMismatch(dependent_dofs.size(),
497 face_constraints.m()));
498
499 const unsigned int n_primary_dofs = primary_dofs.size();
500 const unsigned int n_dependent_dofs = dependent_dofs.size();
501
502 // check for a couple conditions that happened in parallel distributed
503 // mode
504 for (unsigned int row = 0; row != n_dependent_dofs; ++row)
505 Assert(dependent_dofs[row] != numbers::invalid_dof_index,
507 for (unsigned int col = 0; col != n_primary_dofs; ++col)
508 Assert(primary_dofs[col] != numbers::invalid_dof_index,
510
511 // Build constraints in a vector of pairs that can be
512 // arbitrarily large, but that holds up to 25 elements without
513 // external memory allocation. This is good enough for hanging
514 // node constraints of Q4 elements in 3d, so covers most
515 // common cases. Sort the primary dofs to add a sorted list to the
516 // affine constraints, which increases performance there.
518 boost::container::small_vector<std::pair<size_type, size_type>, 25>
519 sorted_primary_dofs;
520 sorted_primary_dofs.reserve(n_primary_dofs);
521 for (unsigned int i = 0; i < n_primary_dofs; ++i)
522 sorted_primary_dofs.emplace_back(primary_dofs[i], i);
523 std::sort(sorted_primary_dofs.begin(), sorted_primary_dofs.end());
524
525 boost::container::small_vector<std::pair<size_type, number2>, 25>
526 entries;
527 entries.reserve(n_primary_dofs);
528 for (unsigned int row = 0; row != n_dependent_dofs; ++row)
529 if (constraints.is_constrained(dependent_dofs[row]) == false)
530 {
531 // Check if we have an identity constraint, i.e.,
532 // something of the form
533 // U(dependent_dof[row])==U(primary_dof[row]),
534 // where
535 // dependent_dof[row] == primary_dof[row].
536 // This can happen in the hp context where we have previously
537 // unified DoF indices, for example, the middle node on the
538 // face of a Q4 element will have gotten the same index
539 // as the middle node of the Q2 element on the neighbor
540 // cell. But because the other Q4 nodes will still have to be
541 // constrained, so the middle node shows up again here.
542 //
543 // If we find such a constraint, then it is trivially
544 // satisfied, and we can move on to the next dependent
545 // DoF (row). The only thing we should make sure is that the
546 // row of the matrix really just contains this one entry.
547 {
548 bool is_trivial_constraint = false;
549
550 for (unsigned int i = 0; i < n_primary_dofs; ++i)
551 if (face_constraints(row, i) == 1.0)
552 if (dependent_dofs[row] == primary_dofs[i])
553 {
554 is_trivial_constraint = true;
555
556 for (unsigned int ii = 0; ii < n_primary_dofs; ++ii)
557 if (ii != i)
558 Assert(face_constraints(row, ii) == 0.0,
560
561 break;
562 }
563
564 if (is_trivial_constraint == true)
565 continue;
566 }
567
568 // then enter those constraints that are larger than
569 // 1e-14; since numbers are normalized for the subface
570 // interpolation matrices, we do not need to normalize here.
571 // everything else probably originated from
572 // inexact inversion of matrices and similar effects. having
573 // those constraints in here will only lead to problems because
574 // it makes sparsity patterns fuller than necessary without
575 // producing any significant effect. do this in two steps, first
576 // filling a vector and then adding to the constraints in order
577 // to reduce the number of memory allocations.
578 entries.clear();
579 for (const auto &[dof_index, unsorted_index] :
580 sorted_primary_dofs)
581 if (std::fabs(face_constraints(row, unsorted_index)) >= 1e-14)
582 entries.emplace_back(dof_index,
583 face_constraints(row, unsorted_index));
584 constraints.add_constraint(dependent_dofs[row],
585 entries,
586 /* inhomogeneity= */ 0.);
587 }
588 }
589
590 } // namespace
591
592
593
594 template <typename number, int spacedim>
595 void
597 const DoFHandler<1, spacedim> & /*dof_handler*/,
598 AffineConstraints<number> & /*constraints*/)
599 {
600 // nothing to do for dof handlers in 1d
601 }
602
603
604
605 template <typename number, int spacedim>
606 void
608 const ::DoFHandler<1, spacedim> & /*dof_handler*/,
609 AffineConstraints<number> & /*constraints*/,
610 std::integral_constant<int, 1>)
611 {
612 // nothing to do for dof handlers in 1d
613 }
614
615
616
617 template <typename number, int spacedim>
618 void
620 const DoFHandler<1, spacedim> & /*dof_handler*/,
621 AffineConstraints<number> & /*constraints*/,
622 std::integral_constant<int, 1>)
623 {
624 // nothing to do for dof handlers in 1d
625 }
626
627
628
629 template <int dim_, int spacedim, typename number>
630 void
632 const DoFHandler<dim_, spacedim> &dof_handler,
633 AffineConstraints<number> &constraints,
634 std::integral_constant<int, 2>)
635 {
636 const unsigned int dim = 2;
637
638 std::vector<types::global_dof_index> dofs_on_mother;
639 std::vector<types::global_dof_index> dofs_on_children;
640
641 // Build constraints in a vector of pairs that can be
642 // arbitrarily large, but that holds up to 25 elements without
643 // external memory allocation. This is good enough for hanging
644 // node constraints of Q4 elements in 3d, so covers most
645 // common cases.
646 boost::container::small_vector<
647 std::pair<typename AffineConstraints<number>::size_type, number>,
648 25>
649 constraint_entries;
650
651 // loop over all lines; only on lines there can be constraints. We do so
652 // by looping over all active cells and checking whether any of the faces
653 // are refined which can only be from the neighboring cell because this
654 // one is active. In that case, the face is subject to constraints
655 //
656 // note that even though we may visit a face twice if the neighboring
657 // cells are equally refined, we can only visit each face with hanging
658 // nodes once
659 for (const auto &cell : dof_handler.active_cell_iterators())
660 {
661 // artificial cells can at best neighbor ghost cells, but we're not
662 // interested in these interfaces
663 if (cell->is_artificial())
664 continue;
665
666 for (const unsigned int face : cell->face_indices())
667 if (cell->face(face)->has_children())
668 {
669 // in any case, faces can have at most two active FE indices,
670 // but here the face can have only one (namely the same as that
671 // from the cell we're sitting on), and each of the children can
672 // have only one as well. check this
673 Assert(cell->face(face)->n_active_fe_indices() == 1,
675 Assert(cell->face(face)->fe_index_is_active(
676 cell->active_fe_index()) == true,
678 for (unsigned int c = 0; c < cell->face(face)->n_children();
679 ++c)
680 if (!cell->neighbor_child_on_subface(face, c)
681 ->is_artificial())
682 Assert(cell->face(face)->child(c)->n_active_fe_indices() ==
683 1,
685
686 // right now, all that is implemented is the case that both
687 // sides use the same FE
688 for (unsigned int c = 0; c < cell->face(face)->n_children();
689 ++c)
690 if (!cell->neighbor_child_on_subface(face, c)
691 ->is_artificial())
692 Assert(cell->face(face)->child(c)->fe_index_is_active(
693 cell->active_fe_index()) == true,
695
696 // ok, start up the work
697 const FiniteElement<dim, spacedim> &fe = cell->get_fe();
698 const types::fe_index fe_index = cell->active_fe_index();
699
700 const unsigned int n_dofs_on_mother =
701 2 * fe.n_dofs_per_vertex() +
702 fe.n_dofs_per_line(),
703 n_dofs_on_children =
704 fe.n_dofs_per_vertex() +
705 2 * fe.n_dofs_per_line();
706
707 dofs_on_mother.resize(n_dofs_on_mother);
708 // we might not use all of those in case of artificial cells, so
709 // do not resize(), but reserve() and use push_back later.
710 dofs_on_children.clear();
711 dofs_on_children.reserve(n_dofs_on_children);
712
713 Assert(n_dofs_on_mother == fe.constraints().n(),
714 ExcDimensionMismatch(n_dofs_on_mother,
715 fe.constraints().n()));
716 Assert(n_dofs_on_children == fe.constraints().m(),
717 ExcDimensionMismatch(n_dofs_on_children,
718 fe.constraints().m()));
719
721 this_face = cell->face(face);
722
723 // fill the dofs indices. Use same enumeration scheme as in
724 // @p{FiniteElement::constraints()}
725 unsigned int next_index = 0;
726 for (unsigned int vertex = 0; vertex < 2; ++vertex)
727 for (unsigned int dof = 0; dof != fe.n_dofs_per_vertex();
728 ++dof)
729 dofs_on_mother[next_index++] =
730 this_face->vertex_dof_index(vertex, dof, fe_index);
731 for (unsigned int dof = 0; dof != fe.n_dofs_per_line(); ++dof)
732 dofs_on_mother[next_index++] =
733 this_face->dof_index(dof, fe_index);
734 AssertDimension(next_index, dofs_on_mother.size());
735
736 for (unsigned int dof = 0; dof != fe.n_dofs_per_vertex(); ++dof)
737 dofs_on_children.push_back(
738 this_face->child(0)->vertex_dof_index(1, dof, fe_index));
739 for (unsigned int child = 0; child < 2; ++child)
740 {
741 // skip artificial cells
742 if (cell->neighbor_child_on_subface(face, child)
743 ->is_artificial())
744 continue;
745 for (unsigned int dof = 0; dof != fe.n_dofs_per_line();
746 ++dof)
747 dofs_on_children.push_back(
748 this_face->child(child)->dof_index(dof, fe_index));
749 }
750 // note: can get fewer DoFs when we have artificial cells
751 Assert(dofs_on_children.size() <= n_dofs_on_children,
753
754 // for each row in the AffineConstraints object for this line:
755 for (unsigned int row = 0; row != dofs_on_children.size();
756 ++row)
757 {
758 constraint_entries.clear();
759 constraint_entries.reserve(dofs_on_mother.size());
760 for (unsigned int i = 0; i != dofs_on_mother.size(); ++i)
761 constraint_entries.emplace_back(dofs_on_mother[i],
762 fe.constraints()(row, i));
763
764 constraints.add_constraint(dofs_on_children[row],
765 constraint_entries,
766 0.);
767 }
768 }
769 else
770 {
771 // this face has no children, but it could still be that it is
772 // shared by two cells that use a different FE index. check a
773 // couple of things, but ignore the case that the neighbor is an
774 // artificial cell
775 if (!cell->at_boundary(face) &&
776 !cell->neighbor(face)->is_artificial())
777 {
778 Assert(cell->face(face)->n_active_fe_indices() == 1,
780 Assert(cell->face(face)->fe_index_is_active(
781 cell->active_fe_index()) == true,
783 }
784 }
785 }
786 }
787
788
789
790 template <int dim_, int spacedim, typename number>
791 void
793 const DoFHandler<dim_, spacedim> &dof_handler,
794 AffineConstraints<number> &constraints,
795 std::integral_constant<int, 3>)
796 {
797 const unsigned int dim = 3;
798
799 std::vector<types::global_dof_index> dofs_on_mother;
800 std::vector<types::global_dof_index> dofs_on_children;
801
802 // Build constraints in a vector of pairs that can be
803 // arbitrarily large, but that holds up to 25 elements without
804 // external memory allocation. This is good enough for hanging
805 // node constraints of Q4 elements in 3d, so covers most
806 // common cases.
807 boost::container::small_vector<
808 std::pair<typename AffineConstraints<number>::size_type, number>,
809 25>
810 constraint_entries;
811
812 // loop over all quads; only on quads there can be constraints. We do so
813 // by looping over all active cells and checking whether any of the faces
814 // are refined which can only be from the neighboring cell because this
815 // one is active. In that case, the face is subject to constraints
816 //
817 // note that even though we may visit a face twice if the neighboring
818 // cells are equally refined, we can only visit each face with hanging
819 // nodes once
820 for (const auto &cell : dof_handler.active_cell_iterators())
821 {
822 // artificial cells can at best neighbor ghost cells, but we're not
823 // interested in these interfaces
824 if (cell->is_artificial())
825 continue;
826
827 for (const unsigned int face : cell->face_indices())
828 if (cell->face(face)->has_children())
829 {
830 // first of all, make sure that we treat a case which is
831 // possible, i.e. either no dofs on the face at all or no
832 // anisotropic refinement
833 if (cell->get_fe().n_dofs_per_face(face) == 0)
834 continue;
835
836 Assert(cell->face(face)->refinement_case() ==
839
840 // in any case, faces can have at most two active FE indices,
841 // but here the face can have only one (namely the same as that
842 // from the cell we're sitting on), and each of the children can
843 // have only one as well. check this
844 AssertDimension(cell->face(face)->n_active_fe_indices(), 1);
845 Assert(cell->face(face)->fe_index_is_active(
846 cell->active_fe_index()) == true,
848 for (unsigned int c = 0; c < cell->face(face)->n_children();
849 ++c)
850 if (!cell->neighbor_child_on_subface(face, c)
851 ->is_artificial())
853 cell->face(face)->child(c)->n_active_fe_indices(), 1);
854
855 // right now, all that is implemented is the case that both
856 // sides use the same fe, and not only that but also that all
857 // lines bounding this face and the children have the same FE
858 for (unsigned int c = 0; c < cell->face(face)->n_children();
859 ++c)
860 if (!cell->neighbor_child_on_subface(face, c)
861 ->is_artificial())
862 {
863 Assert(cell->face(face)->child(c)->fe_index_is_active(
864 cell->active_fe_index()) == true,
866 for (unsigned int e = 0; e < 4; ++e)
867 {
868 Assert(cell->face(face)
869 ->child(c)
870 ->line(e)
871 ->n_active_fe_indices() == 1,
873 Assert(cell->face(face)
874 ->child(c)
875 ->line(e)
876 ->fe_index_is_active(
877 cell->active_fe_index()) == true,
879 }
880 }
881 for (unsigned int e = 0; e < 4; ++e)
882 {
883 Assert(cell->face(face)->line(e)->n_active_fe_indices() ==
884 1,
886 Assert(cell->face(face)->line(e)->fe_index_is_active(
887 cell->active_fe_index()) == true,
889 }
890
891 // ok, start up the work
892 const FiniteElement<dim> &fe = cell->get_fe();
893 const types::fe_index fe_index = cell->active_fe_index();
894
895 const unsigned int n_dofs_on_mother = fe.n_dofs_per_face(face);
896 const unsigned int n_dofs_on_children =
897 (5 * fe.n_dofs_per_vertex() + 12 * fe.n_dofs_per_line() +
898 4 * fe.n_dofs_per_quad(face));
899
900 // TODO[TL]: think about this and the following in case of
901 // anisotropic refinement
902
903 dofs_on_mother.resize(n_dofs_on_mother);
904 // we might not use all of those in case of artificial cells, so
905 // do not resize(), but reserve() and use push_back later.
906 dofs_on_children.clear();
907 dofs_on_children.reserve(n_dofs_on_children);
908
909 Assert(n_dofs_on_mother == fe.constraints().n(),
910 ExcDimensionMismatch(n_dofs_on_mother,
911 fe.constraints().n()));
912 Assert(n_dofs_on_children == fe.constraints().m(),
913 ExcDimensionMismatch(n_dofs_on_children,
914 fe.constraints().m()));
915
917 this_face = cell->face(face);
918
919 // fill the dofs indices. Use same enumeration scheme as in
920 // @p{FiniteElement::constraints()}
921 unsigned int next_index = 0;
922 for (unsigned int vertex = 0; vertex < 4; ++vertex)
923 for (unsigned int dof = 0; dof != fe.n_dofs_per_vertex();
924 ++dof)
925 dofs_on_mother[next_index++] =
926 this_face->vertex_dof_index(vertex, dof, fe_index);
927 for (unsigned int line = 0; line < 4; ++line)
928 for (unsigned int dof = 0; dof != fe.n_dofs_per_line(); ++dof)
929 dofs_on_mother[next_index++] =
930 this_face->line(line)->dof_index(dof, fe_index);
931 for (unsigned int dof = 0; dof != fe.n_dofs_per_quad(face);
932 ++dof)
933 dofs_on_mother[next_index++] =
934 this_face->dof_index(dof, fe_index);
935 AssertDimension(next_index, dofs_on_mother.size());
936
937 // TODO: assert some consistency assumptions
938
939 // TODO[TL]: think about this in case of anisotropic refinement
940
941 Assert(dof_handler.get_triangulation()
943 ((this_face->child(0)->vertex_index(3) ==
944 this_face->child(1)->vertex_index(2)) &&
945 (this_face->child(0)->vertex_index(3) ==
946 this_face->child(2)->vertex_index(1)) &&
947 (this_face->child(0)->vertex_index(3) ==
948 this_face->child(3)->vertex_index(0))),
950
951 for (unsigned int dof = 0; dof != fe.n_dofs_per_vertex(); ++dof)
952 dofs_on_children.push_back(
953 this_face->child(0)->vertex_dof_index(3, dof));
954
955 // dof numbers on the centers of the lines bounding this face
956 for (unsigned int line = 0; line < 4; ++line)
957 for (unsigned int dof = 0; dof != fe.n_dofs_per_vertex();
958 ++dof)
959 dofs_on_children.push_back(
960 this_face->line(line)->child(0)->vertex_dof_index(
961 1, dof, fe_index));
962
963 // next the dofs on the lines interior to the face; the order of
964 // these lines is laid down in the FiniteElement class
965 // documentation
966 for (unsigned int dof = 0; dof < fe.n_dofs_per_line(); ++dof)
967 dofs_on_children.push_back(
968 this_face->child(0)->line(1)->dof_index(dof, fe_index));
969 for (unsigned int dof = 0; dof < fe.n_dofs_per_line(); ++dof)
970 dofs_on_children.push_back(
971 this_face->child(2)->line(1)->dof_index(dof, fe_index));
972 for (unsigned int dof = 0; dof < fe.n_dofs_per_line(); ++dof)
973 dofs_on_children.push_back(
974 this_face->child(0)->line(3)->dof_index(dof, fe_index));
975 for (unsigned int dof = 0; dof < fe.n_dofs_per_line(); ++dof)
976 dofs_on_children.push_back(
977 this_face->child(1)->line(3)->dof_index(dof, fe_index));
978
979 // dofs on the bordering lines
980 for (unsigned int line = 0; line < 4; ++line)
981 for (unsigned int child = 0; child < 2; ++child)
982 {
983 for (unsigned int dof = 0; dof != fe.n_dofs_per_line();
984 ++dof)
985 dofs_on_children.push_back(
986 this_face->line(line)->child(child)->dof_index(
987 dof, fe_index));
988 }
989
990 // finally, for the dofs interior to the four child faces
991 for (unsigned int child = 0; child < 4; ++child)
992 {
993 // skip artificial cells
994 if (cell->neighbor_child_on_subface(face, child)
995 ->is_artificial())
996 continue;
997 for (unsigned int dof = 0; dof != fe.n_dofs_per_quad(face);
998 ++dof)
999 dofs_on_children.push_back(
1000 this_face->child(child)->dof_index(dof, fe_index));
1001 }
1002
1003 // note: can get fewer DoFs when we have artificial cells:
1004 Assert(dofs_on_children.size() <= n_dofs_on_children,
1006
1007 // For each row in the AffineConstraints object for
1008 // this line, add the constraint. Ignore rows that
1009 // have already been added (e.g., in 3d degrees of
1010 // freedom on edges with hanging nodes will be visited
1011 // more than once).
1012 for (unsigned int row = 0; row != dofs_on_children.size();
1013 ++row)
1014 if (constraints.is_constrained(dofs_on_children[row]) ==
1015 false)
1016 {
1017 constraint_entries.clear();
1018 constraint_entries.reserve(dofs_on_mother.size());
1019 for (unsigned int i = 0; i != dofs_on_mother.size(); ++i)
1020 constraint_entries.emplace_back(dofs_on_mother[i],
1021 fe.constraints()(row,
1022 i));
1023
1024 constraints.add_constraint(dofs_on_children[row],
1025 constraint_entries,
1026 0.);
1027 }
1028 }
1029 else
1030 {
1031 // this face has no children, but it could still be that it is
1032 // shared by two cells that use a different FE index. check a
1033 // couple of things, but ignore the case that the neighbor is an
1034 // artificial cell
1035 if (!cell->at_boundary(face) &&
1036 !cell->neighbor(face)->is_artificial())
1037 {
1038 Assert(cell->face(face)->n_active_fe_indices() == 1,
1040 Assert(cell->face(face)->fe_index_is_active(
1041 cell->active_fe_index()) == true,
1043 }
1044 }
1045 }
1046 }
1047
1048
1049
1050 template <int dim_, int spacedim, typename number>
1051 void
1053 const DoFHandler<dim_, spacedim> &dof_handler,
1054 AffineConstraints<number> &constraints,
1055 std::integral_constant<int, 2>)
1056 {
1057 // Parts of this function are very similar to
1058 // make_oldstyle_hanging_node_constraints.
1059 // Therefore, only the parts that differ from the
1060 // make_oldstyle_hanging_node_constraints are commented on.
1061
1062 const unsigned int dim = 2;
1063
1064 std::vector<types::global_dof_index> face_dof_indices;
1065 std::map<types::global_dof_index, std::set<types::global_dof_index>>
1066 depends_on;
1067
1068 // loop over all lines
1069 for (const auto &cell : dof_handler.active_cell_iterators())
1070 {
1071 // skip artificial cells
1072 if (cell->is_artificial())
1073 continue;
1074
1075 // loop over all faces:
1076 for (const unsigned int f : cell->face_indices())
1077 {
1078 // check if the neighbor is refined; if so, we need to
1079 // treat the constraints on this interface
1080 if (!cell->face(f)->has_children())
1081 continue;
1082
1083 Assert(cell->face(f)->n_active_fe_indices() == 1,
1085 Assert(cell->face(f)->fe_index_is_active(
1086 cell->active_fe_index()) == true,
1088
1089 if constexpr (running_in_debug_mode())
1090 {
1091 for (unsigned int c = 0; c < cell->face(f)->n_children(); ++c)
1092 {
1093 if (cell->neighbor_child_on_subface(f, c)
1094 ->is_artificial())
1095 continue;
1096
1097 Assert(cell->face(f)->child(c)->n_active_fe_indices() ==
1098 1,
1100
1101 Assert(cell->face(f)->child(c)->fe_index_is_active(
1102 cell->active_fe_index()) == true,
1104 }
1105 } // DEBUG
1106
1107 // Ok, start up the work:
1108 const FiniteElement<dim, spacedim> &fe = cell->get_fe();
1109
1110 const unsigned int n_dofs = fe.n_dofs_per_line();
1111 face_dof_indices.resize(n_dofs);
1112
1113 cell->face(f)->get_dof_indices(face_dof_indices);
1114 const std::vector<types::global_dof_index> dof_on_mother_face =
1115 face_dof_indices;
1116
1117 cell->face(f)->child(0)->get_dof_indices(face_dof_indices);
1118 const std::vector<types::global_dof_index> dof_on_child_face_0 =
1119 face_dof_indices;
1120
1121 cell->face(f)->child(1)->get_dof_indices(face_dof_indices);
1122 const std::vector<types::global_dof_index> dof_on_child_face_1 =
1123 face_dof_indices;
1124
1125 // As the Nedelec elements are oriented, we need to take care of
1126 // the orientation of the lines.
1127 // Remark: "false" indicates the line is not flipped.
1128 // "true" indicates the line is flipped.
1129
1130 // get the orientation of the faces
1131 const bool direction_mother = (cell->face(f)->vertex_index(0) >
1132 cell->face(f)->vertex_index(1)) ?
1133 false :
1134 true;
1135 const bool direction_child_0 =
1136 (cell->face(f)->child(0)->vertex_index(0) >
1137 cell->face(f)->child(0)->vertex_index(1)) ?
1138 false :
1139 true;
1140 const bool direction_child_1 =
1141 (cell->face(f)->child(1)->vertex_index(0) >
1142 cell->face(f)->child(1)->vertex_index(1)) ?
1143 false :
1144 true;
1145
1146 for (unsigned int row = 0; row < n_dofs; ++row)
1147 {
1148 constraints.add_line(dof_on_child_face_0[row]);
1149 constraints.add_line(dof_on_child_face_1[row]);
1150 }
1151
1152 for (unsigned int row = 0; row < n_dofs; ++row)
1153 {
1154 for (unsigned int dof_i_on_mother = 0;
1155 dof_i_on_mother < n_dofs;
1156 ++dof_i_on_mother)
1157 {
1158 // We need to keep in mind that, if we use a FE_System
1159 // with multiple FE_NedelecSZ blocks inside, we need
1160 // to consider, that n_dofs depends on the number
1161 // of FE_NedelecSZ blocks used.
1162 unsigned int shift_0 =
1163 (direction_mother == direction_child_0) ? 0 : n_dofs;
1164 constraints.add_entry(dof_on_child_face_0[row],
1165 dof_on_mother_face[dof_i_on_mother],
1166 fe.constraints()(row + shift_0,
1167 dof_i_on_mother));
1168
1169 unsigned int shift_1 =
1170 (direction_mother == direction_child_1) ? 0 : n_dofs;
1171 constraints.add_entry(dof_on_child_face_1[row],
1172 dof_on_mother_face[dof_i_on_mother],
1173 fe.constraints()(row + shift_1,
1174 dof_i_on_mother));
1175 }
1176 }
1177 }
1178 }
1179 }
1180
1181
1182 template <int dim_, int spacedim, typename number>
1183 void
1185 const DoFHandler<dim_, spacedim> &dof_handler,
1186 AffineConstraints<number> &constraints,
1187 std::integral_constant<int, 3>)
1188 {
1189 // Parts of this function are very similar to
1190 // make_oldstyle_hanging_node_constraints.
1191 // Therefore, only the parts that differ from the
1192 // make_oldstyle_hanging_node_constraints are commented on.
1193
1194 const unsigned int dim = 3;
1195
1196 std::vector<types::global_dof_index> dofs_on_mother;
1197 std::vector<types::global_dof_index> dofs_on_children;
1198
1199 // loop over all quads
1200 for (const auto &cell : dof_handler.active_cell_iterators())
1201 {
1202 // skip artificial cells
1203 if (cell->is_artificial())
1204 continue;
1205
1206 // loop over all faces
1207 for (const unsigned int face : cell->face_indices())
1208 {
1209 // skip cells without children
1210 if (cell->face(face)->has_children() == false)
1211 continue;
1212
1213 if (cell->get_fe().n_dofs_per_face(face) == 0)
1214 continue;
1215
1216 Assert(cell->face(face)->refinement_case() ==
1219
1220 AssertDimension(cell->face(face)->n_active_fe_indices(), 1);
1221
1222 Assert(cell->face(face)->fe_index_is_active(
1223 cell->active_fe_index()) == true,
1225
1226 if constexpr (running_in_debug_mode())
1227 {
1228 for (unsigned int c = 0; c < cell->face(face)->n_children();
1229 ++c)
1230 {
1231 if (cell->neighbor_child_on_subface(face, c)
1232 ->is_artificial())
1233 continue;
1234
1236 cell->face(face)->child(c)->n_active_fe_indices(), 1);
1237
1238 Assert(cell->face(face)->child(c)->fe_index_is_active(
1239 cell->active_fe_index()) == true,
1241
1242 for (unsigned int e = 0;
1243 e < GeometryInfo<dim>::vertices_per_face;
1244 ++e)
1245 {
1246 Assert(cell->face(face)
1247 ->child(c)
1248 ->line(e)
1249 ->n_active_fe_indices() == 1,
1251
1252 Assert(cell->face(face)
1253 ->child(c)
1254 ->line(e)
1255 ->fe_index_is_active(
1256 cell->active_fe_index()) == true,
1258 }
1259 }
1260
1261 for (unsigned int e = 0;
1262 e < GeometryInfo<dim>::vertices_per_face;
1263 ++e)
1264 {
1265 Assert(cell->face(face)->line(e)->n_active_fe_indices() ==
1266 1,
1268
1269 Assert(cell->face(face)->line(e)->fe_index_is_active(
1270 cell->active_fe_index()) == true,
1272 }
1273 } // DEBUG
1274
1275 // Ok, start up the work
1276 const FiniteElement<dim, spacedim> &fe = cell->get_fe();
1277 const unsigned int fe_index = cell->active_fe_index();
1278
1279 // get the polynomial degree
1280 unsigned int degree(fe.degree);
1281
1282 // get the number of DoFs on mother and children;
1283 // number of DoFs on the mother
1284 const unsigned int n_dofs_on_mother = fe.n_dofs_per_face(face);
1285 dofs_on_mother.resize(n_dofs_on_mother);
1286
1287 const unsigned int n_lines_on_mother =
1289
1290 // number of internal lines of the children;
1291 // for more details see description of the
1292 // GeometryInfo<dim> class
1293 // .................
1294 // . | .
1295 // . c2 1 c3 .
1296 // . | .
1297 // .---2---+---3---.
1298 // . | .
1299 // . c0 0 c1 .
1300 // . | .
1301 // .................
1302 const unsigned int n_internal_lines_on_children = 4;
1303
1304 // number of external lines of the children
1305 // +---6--------7--+
1306 // | . |
1307 // 1 c2 . c3 3
1308 // | . |
1309 // |...............|
1310 // | . |
1311 // 0 c0 . c1 2
1312 // | . |
1313 // +---4---+---5---+
1314 const unsigned int n_external_lines_on_children = 8;
1315
1316 const unsigned int n_lines_on_children =
1317 n_internal_lines_on_children + n_external_lines_on_children;
1318
1319 // we only consider the isotropic case here
1320 const unsigned int n_children_per_face =
1322 const unsigned int n_children_per_line =
1323 GeometryInfo<dim - 1>::max_children_per_face;
1324
1325 // number of DoFs on the children
1326 // Remark: Nedelec elements have no DoFs on the vertices,
1327 // therefore we skip the vertices
1328 const unsigned int n_dofs_on_children =
1329 (n_lines_on_children * fe.n_dofs_per_line() +
1330 n_children_per_face * fe.n_dofs_per_quad(face));
1331
1332 dofs_on_children.clear();
1333 dofs_on_children.reserve(n_dofs_on_children);
1334
1335 AssertDimension(n_dofs_on_mother, fe.constraints().n());
1336 AssertDimension(n_dofs_on_children, fe.constraints().m());
1337
1338 // get the current face
1339 const typename DoFHandler<dim, dim>::face_iterator this_face =
1340 cell->face(face);
1341
1342 // fill the DoFs on the mother:
1343 unsigned int next_index = 0;
1344
1345 // DoFs on vertices:
1346 // Nedelec elements have no DoFs on the vertices
1347
1348 // DoFs on lines:
1349 for (unsigned int line = 0;
1350 line < GeometryInfo<dim>::lines_per_face;
1351 ++line)
1352 for (unsigned int dof = 0; dof != fe.n_dofs_per_line(); ++dof)
1353 dofs_on_mother[next_index++] =
1354 this_face->line(line)->dof_index(dof, fe_index);
1355
1356 // DoFs on the face:
1357 for (unsigned int dof = 0; dof != fe.n_dofs_per_quad(face); ++dof)
1358 dofs_on_mother[next_index++] =
1359 this_face->dof_index(dof, fe_index);
1360
1361 // check that we have added all DoFs
1362 AssertDimension(next_index, dofs_on_mother.size());
1363
1364 // the implementation does not support anisotropic refinement
1365 Assert(!dof_handler.get_triangulation()
1368
1369 // fill the DoF on the children:
1370 // DoFs on vertices:
1371 // Nedelec elements have no DoFs on the vertices
1372
1373 // DoFs on lines:
1374 // the DoFs on the interior lines to the children; the order
1375 // of these lines is shown above (see
1376 // n_internal_lines_on_children)
1377 for (unsigned int dof = 0; dof < fe.n_dofs_per_line(); ++dof)
1378 dofs_on_children.push_back(
1379 this_face->child(0)->line(1)->dof_index(dof, fe_index));
1380
1381 for (unsigned int dof = 0; dof < fe.n_dofs_per_line(); ++dof)
1382 dofs_on_children.push_back(
1383 this_face->child(2)->line(1)->dof_index(dof, fe_index));
1384
1385 for (unsigned int dof = 0; dof < fe.n_dofs_per_line(); ++dof)
1386 dofs_on_children.push_back(
1387 this_face->child(0)->line(3)->dof_index(dof, fe_index));
1388
1389 for (unsigned int dof = 0; dof < fe.n_dofs_per_line(); ++dof)
1390 dofs_on_children.push_back(
1391 this_face->child(1)->line(3)->dof_index(dof, fe_index));
1392
1393 // DoFs on the bordering lines:
1394 // DoFs on the exterior lines to the children; the order of
1395 // these lines is shown above (see n_external_lines_on_children)
1396 for (unsigned int line = 0;
1397 line < GeometryInfo<dim>::lines_per_face;
1398 ++line)
1399 for (unsigned int child = 0; child < n_children_per_line;
1400 ++child)
1401 for (unsigned int dof = 0; dof < fe.n_dofs_per_line(); ++dof)
1402 dofs_on_children.push_back(
1403 this_face->line(line)->child(child)->dof_index(dof,
1404 fe_index));
1405
1406 // DoFs on the faces of the four children:
1407 for (unsigned int child = 0; child < n_children_per_face; ++child)
1408 {
1409 // skip artificial cells
1410 if (cell->neighbor_child_on_subface(face, child)
1411 ->is_artificial())
1412 continue;
1413
1414 for (unsigned int dof = 0; dof < fe.n_dofs_per_quad(face);
1415 ++dof)
1416 dofs_on_children.push_back(
1417 this_face->child(child)->dof_index(dof, fe_index));
1418 } // rof: child
1419
1420 // consistency check:
1421 // note: we can get fewer DoFs when we have artificial cells
1422 Assert(dofs_on_children.size() <= n_dofs_on_children,
1424
1425 // As the Nedelec elements are oriented, we need to take care of
1426 // the orientation of the lines.
1427 // Remark: "false" indicates the line is not flipped.
1428 // "true" indicates the line is flipped.
1429
1430 // Orientation - Lines:
1431 // get the orientation from the edges from the mother cell
1432 std::vector<bool> direction_mother(
1434 for (unsigned int line = 0;
1435 line < GeometryInfo<dim>::lines_per_face;
1436 ++line)
1437 if (this_face->line(line)->vertex_index(0) >
1438 this_face->line(line)->vertex_index(1))
1439 direction_mother[line] = true;
1440
1441 // get the orientation from the intern edges of the children
1442 std::vector<bool> direction_child_intern(
1443 n_internal_lines_on_children, false);
1444
1445 // get the global vertex index of vertex in the center;
1446 // we need this vertex index, to compute the direction
1447 // of the internal edges
1448 unsigned int center = this_face->child(0)->vertex_index(3);
1449
1450 // compute the direction of the internal edges
1451 for (unsigned int line = 0; line < n_internal_lines_on_children;
1452 ++line)
1453 if (line % 2 == 0)
1454 {
1455 direction_child_intern[line] =
1456 this_face->line(line)->child(0)->vertex_index(1) <
1457 center ?
1458 false :
1459 true;
1460 }
1461 else
1462 {
1463 direction_child_intern[line] =
1464 this_face->line(line)->child(0)->vertex_index(1) >
1465 center ?
1466 false :
1467 true;
1468 }
1469
1470 // compute the direction of the outer edges
1471 std::vector<bool> direction_child(n_external_lines_on_children,
1472 false);
1473 for (unsigned int line = 0;
1474 line < GeometryInfo<dim>::lines_per_face;
1475 ++line)
1476 {
1477 if (this_face->line(line)->child(0)->vertex_index(0) >
1478 this_face->line(line)->child(0)->vertex_index(1))
1479 direction_child[2 * line] = true;
1480 if (this_face->line(line)->child(1)->vertex_index(0) >
1481 this_face->line(line)->child(1)->vertex_index(1))
1482 direction_child[2 * line + 1] = true;
1483 }
1484
1485
1486 // Orientation - Faces:
1487 bool mother_flip_x = false;
1488 bool mother_flip_y = false;
1489 bool mother_flip_xy = false;
1490 std::vector<bool> child_flip_x(n_children_per_face, false);
1491 std::vector<bool> child_flip_y(n_children_per_face, false);
1492 std::vector<bool> child_flip_xy(n_children_per_face, false);
1493 const unsigned int
1494 vertices_adjacent_on_face[GeometryInfo<dim>::vertices_per_face]
1495 [2] = {{1, 2}, {0, 3}, {3, 0}, {2, 1}};
1496
1497 {
1498 // Mother
1499 // get the position of the vertex with the highest number
1500 unsigned int current_glob = cell->face(face)->vertex_index(0);
1501 unsigned int current_max = 0;
1502 for (unsigned int v = 1;
1503 v < GeometryInfo<dim>::vertices_per_face;
1504 ++v)
1505 if (current_glob < this_face->vertex_index(v))
1506 {
1507 current_max = v;
1508 current_glob = this_face->vertex_index(v);
1509 }
1510
1511 // if the vertex with the highest DoF index is in the lower row
1512 // of the face, the face is flipped in y direction
1513 if (current_max < 2)
1514 mother_flip_y = true;
1515
1516 // if the vertex with the highest DoF index is on the left side
1517 // of the face is flipped in x direction
1518 if (current_max % 2 == 0)
1519 mother_flip_x = true;
1520
1521 // get the minor direction of the face of the mother
1522 if (this_face->vertex_index(
1523 vertices_adjacent_on_face[current_max][0]) <
1524 this_face->vertex_index(
1525 vertices_adjacent_on_face[current_max][1]))
1526 mother_flip_xy = true;
1527 }
1528
1529 // Children:
1530 // get the orientation of the faces of the children
1531 for (unsigned int child = 0; child < n_children_per_face; ++child)
1532 {
1533 unsigned int current_max = 0;
1534 unsigned int current_glob =
1535 this_face->child(child)->vertex_index(0);
1536
1537 for (unsigned int v = 1;
1538 v < GeometryInfo<dim>::vertices_per_face;
1539 ++v)
1540 if (current_glob < this_face->child(child)->vertex_index(v))
1541 {
1542 current_max = v;
1543 current_glob = this_face->child(child)->vertex_index(v);
1544 }
1545
1546 if (current_max < 2)
1547 child_flip_y[child] = true;
1548
1549 if (current_max % 2 == 0)
1550 child_flip_x[child] = true;
1551
1552 if (this_face->child(child)->vertex_index(
1553 vertices_adjacent_on_face[current_max][0]) <
1554 this_face->child(child)->vertex_index(
1555 vertices_adjacent_on_face[current_max][1]))
1556 child_flip_xy[child] = true;
1557
1558 child_flip_xy[child] = mother_flip_xy;
1559 }
1560
1561 // copy the constraint matrix, since we need to modify that matrix
1562 std::vector<std::vector<double>> constraints_matrix(
1563 n_lines_on_children * fe.n_dofs_per_line() +
1564 n_children_per_face * fe.n_dofs_per_quad(),
1565 std::vector<double>(dofs_on_mother.size(), 0));
1566
1567 {
1568 // copy the constraint matrix
1569 // internal lines
1570 for (unsigned int line = 0; line < n_internal_lines_on_children;
1571 ++line)
1572 {
1573 unsigned int row_start = line * fe.n_dofs_per_line();
1574 unsigned int line_mother = line / 2;
1575 unsigned int row_mother =
1576 (line_mother * 2) * fe.n_dofs_per_line();
1577 for (unsigned int row = 0; row < fe.n_dofs_per_line();
1578 ++row)
1579 for (unsigned int i = 0;
1580 i < n_lines_on_mother * fe.n_dofs_per_line();
1581 ++i)
1582 constraints_matrix[row + row_start][i] =
1583 fe.constraints()(row + row_mother, i);
1584 }
1585
1586 for (unsigned int line = 0; line < n_internal_lines_on_children;
1587 ++line)
1588 {
1589 unsigned int row_start = line * fe.n_dofs_per_line();
1590 unsigned int line_mother = line / 2;
1591 unsigned int row_mother =
1592 (line_mother * 2) * fe.n_dofs_per_line();
1593 for (unsigned int row = 0; row < fe.n_dofs_per_line();
1594 ++row)
1595 for (unsigned int i =
1596 n_lines_on_mother * fe.n_dofs_per_line();
1597 i < dofs_on_mother.size();
1598 ++i)
1599 constraints_matrix[row + row_start][i] =
1600 fe.constraints()(row + row_mother, i);
1601 }
1602
1603 // external lines
1604 unsigned int row_offset =
1605 n_internal_lines_on_children * fe.n_dofs_per_line();
1606 for (unsigned int line = 0; line < n_external_lines_on_children;
1607 line++)
1608 {
1609 unsigned int row_start = line * fe.n_dofs_per_line();
1610 unsigned int line_mother = line / 2;
1611 unsigned int row_mother =
1612 (line_mother * 2) * fe.n_dofs_per_line();
1613 for (unsigned int row = row_offset;
1614 row < row_offset + fe.n_dofs_per_line();
1615 ++row)
1616 for (unsigned int i = 0; i < dofs_on_mother.size(); ++i)
1617 constraints_matrix[row + row_start][i] =
1618 fe.constraints()(row + row_mother, i);
1619 }
1620
1621 // copy the weights for the faces
1622 row_offset = n_lines_on_children * fe.n_dofs_per_line();
1623 for (unsigned int face = 0; face < n_children_per_face; ++face)
1624 {
1625 unsigned int row_start = face * fe.n_dofs_per_quad();
1626 for (unsigned int row = row_offset;
1627 row < row_offset + fe.n_dofs_per_quad();
1628 row++)
1629 for (unsigned int i = 0; i < dofs_on_mother.size(); ++i)
1630 constraints_matrix[row + row_start][i] =
1631 fe.constraints()(row, i);
1632 }
1633 }
1634
1635 // Modify the matrix
1636 // Edge - Edge:
1637 // Interior edges: the interior edges have support on the
1638 // corresponding edges and faces loop over all 4 intern edges
1639 for (unsigned int i = 0;
1640 i < n_internal_lines_on_children * fe.n_dofs_per_line();
1641 ++i)
1642 {
1643 unsigned int line_i = i / fe.n_dofs_per_line();
1644 unsigned int tmp_i = i % degree;
1645
1646 // loop over the edges of the mother cell
1647 for (unsigned int j = 0;
1648 j < n_lines_on_mother * fe.n_dofs_per_line();
1649 ++j)
1650 {
1651 unsigned int line_j = j / fe.n_dofs_per_line();
1652 unsigned int tmp_j = j % degree;
1653
1654 if ((line_i < 2 && line_j < 2) ||
1655 (line_i >= 2 && line_j >= 2))
1656 {
1657 if (direction_child_intern[line_i] !=
1658 direction_mother[line_j])
1659 {
1660 if ((tmp_i + tmp_j) % 2 == 1)
1661 { // anti-symmetric
1662 constraints_matrix[i][j] *= -1.0;
1663 }
1664 }
1665 }
1666 else
1667 {
1668 if (direction_mother[line_i])
1669 {
1670 if ((tmp_i + tmp_j) % 2 == 1)
1671 { // anti-symmetric
1672 constraints_matrix[i][j] *= -1.0;
1673 }
1674 }
1675 }
1676 }
1677 }
1678
1679 // Exterior edges:
1680 for (unsigned int i =
1681 n_internal_lines_on_children * fe.n_dofs_per_line();
1682 i < n_lines_on_children * fe.n_dofs_per_line();
1683 ++i)
1684 {
1685 unsigned int line_i = (i / fe.n_dofs_per_line()) - 4;
1686 unsigned int tmp_i = i % degree;
1687
1688 // loop over the edges of the mother cell
1689 for (unsigned int j = 0;
1690 j < n_lines_on_mother * fe.n_dofs_per_line();
1691 ++j)
1692 {
1693 unsigned int line_j = j / fe.n_dofs_per_line();
1694 unsigned int tmp_j = j % degree;
1695
1696 if (direction_child[line_i] != direction_mother[line_j])
1697 {
1698 if ((tmp_i + tmp_j) % 2 == 1)
1699 { // anti-symmetric
1700 constraints_matrix[i][j] *= -1.0;
1701 }
1702 }
1703 }
1704 }
1705
1706 // Note:
1707 // We need to keep in mind that, if we use a FE_System
1708 // with multiple FE_NedelecSZ blocks inside, we need
1709 // to consider, that fe.n_dofs_per_line() depends on the number
1710 // of FE_NedelecSZ blocks used.
1711 const unsigned int n_blocks = fe.n_dofs_per_line() / degree;
1712
1713 // Edge - Face
1714 // Interior edges: for x-direction
1715 for (unsigned int i = 0; i < 2 * fe.n_dofs_per_line(); ++i)
1716 {
1717 unsigned int line_i = i / fe.n_dofs_per_line();
1718 unsigned int tmp_i = i % degree;
1719
1720 unsigned int start_j =
1721 n_lines_on_mother * fe.n_dofs_per_line();
1722
1723 for (unsigned int block = 0; block < n_blocks; ++block)
1724 {
1725 // Type 1:
1726 for (unsigned int jy = 0; jy < degree - 1; ++jy)
1727 for (unsigned int jx = 0; jx < degree - 1; ++jx)
1728 {
1729 unsigned int j = start_j + jx + (jy * (degree - 1));
1730 if (direction_child_intern[line_i] != mother_flip_y)
1731 {
1732 if ((jy + tmp_i) % 2 == 0)
1733 { // anti-symmetric case
1734 constraints_matrix[i][j] *= -1.0;
1735 }
1736 }
1737 }
1738
1739 start_j += (degree - 1) * (degree - 1);
1740
1741 // Type 2:
1742 for (unsigned int jy = 0; jy < degree - 1; ++jy)
1743 for (unsigned int jx = 0; jx < degree - 1; ++jx)
1744 {
1745 unsigned int j = start_j + jx + (jy * (degree - 1));
1746
1747 if (direction_child_intern[line_i] != mother_flip_y)
1748 {
1749 if ((jy + tmp_i) % 2 == 0)
1750 { // anti-symmetric case
1751 constraints_matrix[i][j] *= -1.0;
1752 }
1753 }
1754 }
1755 start_j += (degree - 1) * (degree - 1);
1756
1757 // Type 3.1:
1758 // nothing to do
1759 start_j += degree - 1;
1760
1761 // Type 3.2:
1762 // nothing to do
1763 start_j += degree - 1;
1764 }
1765 }
1766
1767 // Interior edges: for y-direction
1768 for (unsigned int i = 2 * fe.n_dofs_per_line();
1769 i < 4 * fe.n_dofs_per_line();
1770 i++)
1771 {
1772 unsigned int line_i = i / fe.n_dofs_per_line();
1773 unsigned int tmp_i = i % degree;
1774
1775 unsigned int start_j =
1776 n_lines_on_mother * fe.n_dofs_per_line();
1777
1778 for (unsigned int block = 0; block < n_blocks; block++)
1779 {
1780 // Type 1:
1781 for (unsigned int jy = 0; jy < degree - 1; ++jy)
1782 for (unsigned int jx = 0; jx < degree - 1; ++jx)
1783 {
1784 unsigned int j = start_j + jx + (jy * (degree - 1));
1785 if (direction_child_intern[line_i] != mother_flip_x)
1786 {
1787 if ((jx + tmp_i) % 2 == 0)
1788 { // anti-symmetric case
1789 constraints_matrix[i][j] *= -1.0;
1790 }
1791 }
1792 }
1793
1794 start_j += (degree - 1) * (degree - 1);
1795
1796 // Type 2:
1797 for (unsigned int jy = 0; jy < degree - 1; ++jy)
1798 for (unsigned int jx = 0; jx < degree - 1; ++jx)
1799 {
1800 unsigned int j = start_j + jx + (jy * (degree - 1));
1801 if (direction_child_intern[line_i] != mother_flip_x)
1802 {
1803 if ((jx + tmp_i) % 2 == 0)
1804 { // anti-symmetric case
1805 constraints_matrix[i][j] *= -1.0;
1806 }
1807 }
1808 }
1809 start_j += (degree - 1) * (degree - 1);
1810
1811 // Type 3.1:
1812 // nothing to do
1813 start_j += degree - 1;
1814
1815 // Type 3.2:
1816 // nothing to do
1817 start_j += degree - 1;
1818 }
1819 }
1820
1821 // Face - Face
1822 unsigned int degree_square = (degree - 1) * (degree - 1);
1823 {
1824 // Face
1825 unsigned int i = n_lines_on_children * fe.n_dofs_per_line();
1826 for (unsigned int child_face = 0;
1827 child_face < n_children_per_face;
1828 ++child_face)
1829 for (unsigned int block = 0; block < n_blocks; ++block)
1830 {
1831 unsigned int block_size = fe.n_dofs_per_quad() / n_blocks;
1832
1833 // check if the counting of the DoFs is correct:
1834 Assert((block == 0 &&
1835 i != n_lines_on_children * fe.n_dofs_per_line() +
1836 child_face * fe.n_dofs_per_quad()) ==
1837 false,
1839
1840 // Type 1:
1841 for (unsigned int iy = 0; iy < degree - 1; ++iy)
1842 for (unsigned int ix = 0; ix < degree - 1; ++ix)
1843 {
1844 // Type 1 on mother:
1845 unsigned int j =
1846 n_lines_on_mother * fe.n_dofs_per_line() +
1847 block * block_size;
1848 for (unsigned int jy = 0; jy < degree - 1; ++jy)
1849 for (unsigned int jx = 0; jx < degree - 1; ++jx)
1850 {
1851 if (child_flip_x[child_face] !=
1852 mother_flip_x) // x - direction (x-flip)
1853 {
1854 if ((ix + jx) % 2 == 1)
1855 { // anti-symmetric in x
1856 constraints_matrix[i][j] *= -1.0;
1857 }
1858 }
1859
1860 if (child_flip_y[child_face] !=
1861 mother_flip_y) // y - direction (y-flip)
1862 {
1863 if ((iy + jy) % 2 == 1)
1864 { // anti-symmetric in y
1865 constraints_matrix[i][j] *= -1.0;
1866 }
1867 }
1868
1869 j++;
1870 }
1871 i++;
1872 }
1873
1874 // Type 2:
1875 for (unsigned int iy = 0; iy < degree - 1; ++iy)
1876 for (unsigned int ix = 0; ix < degree - 1; ++ix)
1877 {
1878 // Type 2 on mother:
1879 unsigned int j =
1880 n_lines_on_mother * fe.n_dofs_per_line() +
1881 degree_square + block * block_size;
1882 for (unsigned int jy = 0; jy < degree - 1; ++jy)
1883 for (unsigned int jx = 0; jx < degree - 1; ++jx)
1884 {
1885 if (child_flip_x[child_face] !=
1886 mother_flip_x) // x - direction (x-flip)
1887 {
1888 if ((ix + jx) % 2 == 1)
1889 { // anti-symmetric in x
1890 constraints_matrix[i][j] *= -1.0;
1891 }
1892 }
1893
1894 if (child_flip_y[child_face] !=
1895 mother_flip_y) // y - direction (y-flip)
1896 {
1897 if ((iy + jy) % 2 == 1)
1898 { // anti-symmetric in y
1899 constraints_matrix[i][j] *= -1.0;
1900 }
1901 }
1902
1903 j++;
1904 }
1905
1906 i++;
1907 }
1908
1909
1910 // Type 3 (y):
1911 for (unsigned int iy = 0; iy < degree - 1; ++iy)
1912 {
1913 // Type 2 on mother:
1914 unsigned int j =
1915 n_lines_on_mother * fe.n_dofs_per_line() +
1916 degree_square + block * block_size;
1917 for (unsigned int jy = 0; jy < degree - 1; ++jy)
1918 for (unsigned int jx = 0; jx < degree - 1; ++jx)
1919 {
1920 if (child_flip_x[child_face] !=
1921 mother_flip_x) // x - direction (x-flip)
1922 {
1923 if ((jx) % 2 == 0)
1924 { // anti-symmetric in x
1925 constraints_matrix[i][j] *= -1.0;
1926 }
1927 }
1928
1929 if (child_flip_y[child_face] !=
1930 mother_flip_y) // y - direction (y-flip)
1931 {
1932 if ((iy + jy) % 2 == 1)
1933 { // anti-symmetric in y
1934 constraints_matrix[i][j] *= -1.0;
1935 }
1936 }
1937
1938 j++;
1939 }
1940
1941 // Type 3 on mother:
1942 j = n_lines_on_mother * fe.n_dofs_per_line() +
1943 2 * degree_square + block * block_size;
1944 for (unsigned int jy = 0; jy < degree - 1; ++jy)
1945 {
1946 if (child_flip_y[child_face] !=
1947 mother_flip_y) // y - direction (y-flip)
1948 {
1949 if ((iy + jy) % 2 == 1)
1950 { // anti-symmetric in y
1951 constraints_matrix[i][j] *= -1.0;
1952 }
1953 }
1954
1955 j++;
1956 }
1957 i++;
1958 }
1959
1960 // Type 3 (x):
1961 for (unsigned int ix = 0; ix < degree - 1; ++ix)
1962 {
1963 // Type 2 on mother:
1964 unsigned int j =
1965 n_lines_on_mother * fe.n_dofs_per_line() +
1966 degree_square + block * block_size;
1967 for (unsigned int jy = 0; jy < degree - 1; ++jy)
1968 for (unsigned int jx = 0; jx < degree - 1; ++jx)
1969 {
1970 if (child_flip_x[child_face] !=
1971 mother_flip_x) // x - direction (x-flip)
1972 {
1973 if ((ix + jx) % 2 == 1)
1974 { // anti-symmetric in x
1975 constraints_matrix[i][j] *= -1.0;
1976 }
1977 }
1978
1979 if (child_flip_y[child_face] !=
1980 mother_flip_y) // y - direction (y-flip)
1981 {
1982 if ((jy) % 2 == 0)
1983 { // anti-symmetric in y
1984 constraints_matrix[i][j] *= -1.0;
1985 }
1986 }
1987
1988 j++;
1989 } // rof: Dof j
1990
1991 // Type 3 on mother:
1992 j = n_lines_on_mother * fe.n_dofs_per_line() +
1993 2 * degree_square + (degree - 1) +
1994 block * block_size;
1995 for (unsigned int jx = 0; jx < degree - 1; ++jx)
1996 {
1997 if (child_flip_x[child_face] !=
1998 mother_flip_x) // x - direction (x-flip)
1999 {
2000 if ((ix + jx) % 2 == 1)
2001 { // anti-symmetric in x
2002 constraints_matrix[i][j] *= -1.0;
2003 }
2004 }
2005
2006 j++;
2007 }
2008 i++;
2009 }
2010 }
2011 }
2012
2013 // Next, after we have adapted the signs in the constraint matrix,
2014 // based on the directions of the edges, we need to modify the
2015 // constraint matrix based on the orientation of the faces (i.e.
2016 // if x and y direction are exchanged on the face)
2017
2018 // interior edges:
2019 for (unsigned int i = 0;
2020 i < n_internal_lines_on_children * fe.n_dofs_per_line();
2021 ++i)
2022 {
2023 // check if x and y are permuted on the parent's face
2024 if (mother_flip_xy)
2025 {
2026 // copy the constraints:
2027 std::vector<double> constraints_matrix_old(
2028 dofs_on_mother.size(), 0);
2029 for (unsigned int j = 0; j < dofs_on_mother.size(); ++j)
2030 {
2031 constraints_matrix_old[j] = constraints_matrix[i][j];
2032 }
2033
2034 unsigned int j_start =
2035 n_lines_on_mother * fe.n_dofs_per_line();
2036 for (unsigned block = 0; block < n_blocks; block++)
2037 {
2038 // Type 1
2039 for (unsigned int jy = 0; jy < degree - 1; ++jy)
2040 for (unsigned int jx = 0; jx < degree - 1; ++jx)
2041 {
2042 unsigned int j_old =
2043 j_start + jx + (jy * (degree - 1));
2044 unsigned int j_new =
2045 j_start + jy + (jx * (degree - 1));
2046 constraints_matrix[i][j_new] =
2047 constraints_matrix_old[j_old];
2048 }
2049 j_start += degree_square;
2050
2051 // Type 2
2052 for (unsigned int jy = 0; jy < degree - 1; ++jy)
2053 for (unsigned int jx = 0; jx < degree - 1; ++jx)
2054 {
2055 unsigned int j_old =
2056 j_start + jx + (jy * (degree - 1));
2057 unsigned int j_new =
2058 j_start + jy + (jx * (degree - 1));
2059 constraints_matrix[i][j_new] =
2060 -constraints_matrix_old[j_old];
2061 }
2062 j_start += degree_square;
2063
2064 // Type 3
2065 for (unsigned int j = j_start;
2066 j < j_start + (degree - 1);
2067 j++)
2068 {
2069 constraints_matrix[i][j] =
2070 constraints_matrix_old[j + (degree - 1)];
2071 constraints_matrix[i][j + (degree - 1)] =
2072 constraints_matrix_old[j];
2073 }
2074 j_start += 2 * (degree - 1);
2075 }
2076 }
2077 }
2078
2079 {
2080 // faces:
2081 const unsigned int deg = degree - 1;
2082
2083 // copy the constraints
2084 std::vector<std::vector<double>> constraints_matrix_old(
2085 4 * fe.n_dofs_per_quad(),
2086 std::vector<double>(fe.n_dofs_per_quad(), 0));
2087 for (unsigned int i = 0;
2088 i < n_children_per_face * fe.n_dofs_per_quad();
2089 ++i)
2090 for (unsigned int j = 0; j < fe.n_dofs_per_quad(); ++j)
2091 constraints_matrix_old[i][j] = constraints_matrix
2092 [i + (n_lines_on_children * fe.n_dofs_per_line())]
2093 [j + (n_lines_on_mother * fe.n_dofs_per_line())];
2094
2095 // permute rows (on child)
2096 for (unsigned int child = 0; child < n_children_per_face;
2097 ++child)
2098 {
2099 if (!child_flip_xy[child])
2100 continue;
2101
2102 unsigned int i_start_new =
2103 n_lines_on_children * fe.n_dofs_per_line() +
2104 (child * fe.n_dofs_per_quad());
2105 unsigned int i_start_old = child * fe.n_dofs_per_quad();
2106
2107 unsigned int j_start =
2108 n_lines_on_mother * fe.n_dofs_per_line();
2109
2110 for (unsigned int block = 0; block < n_blocks; block++)
2111 {
2112 // Type 1:
2113 for (unsigned int ix = 0; ix < deg; ++ix)
2114 {
2115 for (unsigned int iy = 0; iy < deg; ++iy)
2116 {
2117 for (unsigned int j = 0;
2118 j < fe.n_dofs_per_quad();
2119 ++j)
2120 constraints_matrix[i_start_new + iy +
2121 (ix * deg)][j + j_start] =
2122 constraints_matrix_old[i_start_old + ix +
2123 (iy * deg)][j];
2124 }
2125 }
2126 i_start_new += deg * deg;
2127 i_start_old += deg * deg;
2128
2129 // Type 2:
2130 for (unsigned int ix = 0; ix < deg; ++ix)
2131 {
2132 for (unsigned int iy = 0; iy < deg; ++iy)
2133 {
2134 for (unsigned int j = 0;
2135 j < fe.n_dofs_per_quad();
2136 j++)
2137 constraints_matrix[i_start_new + iy +
2138 (ix * deg)][j + j_start] =
2139 -constraints_matrix_old[i_start_old + ix +
2140 (iy * deg)][j];
2141 }
2142 }
2143 i_start_new += deg * deg;
2144 i_start_old += deg * deg;
2145
2146 // Type 3:
2147 for (unsigned int ix = 0; ix < deg; ++ix)
2148 {
2149 for (unsigned int j = 0; j < fe.n_dofs_per_quad();
2150 ++j)
2151 constraints_matrix[i_start_new + ix][j +
2152 j_start] =
2153 constraints_matrix_old[i_start_old + ix + deg]
2154 [j];
2155 for (unsigned int j = 0; j < fe.n_dofs_per_quad();
2156 ++j)
2157 constraints_matrix[i_start_new + ix +
2158 deg][j + j_start] =
2159 constraints_matrix_old[i_start_old + ix][j];
2160 } // rof: ix
2161
2162 i_start_new += 2 * deg;
2163 i_start_old += 2 * deg;
2164 }
2165 }
2166
2167 // update the constraints_old
2168 for (unsigned int i = 0;
2169 i < n_children_per_face * fe.n_dofs_per_quad();
2170 i++)
2171 for (unsigned int j = 0; j < fe.n_dofs_per_quad(); j++)
2172 constraints_matrix_old[i][j] = constraints_matrix
2173 [i + (n_lines_on_children * fe.n_dofs_per_line())]
2174 [j + (n_lines_on_mother * fe.n_dofs_per_line())];
2175
2176 // Mother
2177 if (mother_flip_xy)
2178 {
2179 unsigned int i_start =
2180 n_lines_on_children * fe.n_dofs_per_line();
2181
2182 unsigned int j_start_new =
2183 n_lines_on_mother * fe.n_dofs_per_line();
2184 unsigned int j_start_old = 0;
2185
2186 for (unsigned int block = 0; block < n_blocks; ++block)
2187 {
2188 // Type 1:
2189 for (unsigned int jx = 0; jx < deg; ++jx)
2190 {
2191 for (unsigned int jy = 0; jy < deg; ++jy)
2192 {
2193 for (unsigned int i = 0;
2194 i <
2195 n_children_per_face * fe.n_dofs_per_quad();
2196 ++i)
2197 constraints_matrix[i + i_start][j_start_new +
2198 jy +
2199 (jx * deg)] =
2200 constraints_matrix_old[i][j_start_old + jx +
2201 (jy * deg)];
2202 }
2203 }
2204 j_start_new += deg * deg;
2205 j_start_old += deg * deg;
2206
2207 // Type 2:
2208 for (unsigned int jx = 0; jx < deg; ++jx)
2209 {
2210 for (unsigned int jy = 0; jy < deg; ++jy)
2211 {
2212 for (unsigned int i = 0;
2213 i <
2214 n_children_per_face * fe.n_dofs_per_quad();
2215 ++i)
2216 constraints_matrix[i + i_start][j_start_new +
2217 jy +
2218 (jx * deg)] =
2219 -constraints_matrix_old[i][j_start_old +
2220 jx + (jy * deg)];
2221 }
2222 }
2223 j_start_new += deg * deg;
2224 j_start_old += deg * deg;
2225
2226 // Type 3:
2227 for (unsigned int jx = 0; jx < deg; ++jx)
2228 {
2229 for (unsigned int i = 0;
2230 i < n_children_per_face * fe.n_dofs_per_quad();
2231 ++i)
2232 {
2233 constraints_matrix[i + i_start][j_start_new +
2234 jx] =
2235 constraints_matrix_old[i][j_start_old + jx +
2236 deg];
2237 constraints_matrix[i + i_start][j_start_new +
2238 jx + deg] =
2239 constraints_matrix_old[i][j_start_old + jx];
2240 }
2241 }
2242 j_start_new += 2 * deg;
2243 j_start_old += 2 * deg;
2244 }
2245 }
2246 }
2247
2248 // For each row in the AffineConstraints object for
2249 // this line, add the constraint. We split this into the different
2250 // cases.
2251
2252 // internal edges:
2253 for (unsigned int line = 0; line < n_internal_lines_on_children;
2254 ++line)
2255 {
2256 unsigned int row_start = line * fe.n_dofs_per_line();
2257
2258 for (unsigned int row = 0; row < fe.n_dofs_per_line(); ++row)
2259 {
2260 constraints.add_line(dofs_on_children[row_start + row]);
2261 for (unsigned int i = 0; i < dofs_on_mother.size(); ++i)
2262 {
2263 constraints.add_entry(
2264 dofs_on_children[row_start + row],
2265 dofs_on_mother[i],
2266 constraints_matrix[row_start + row][i]);
2267 }
2268 constraints.set_inhomogeneity(
2269 dofs_on_children[row_start + row], 0.);
2270 }
2271 }
2272
2273 // Exterior edges
2274 for (unsigned int line = 0; line < n_external_lines_on_children;
2275 ++line)
2276 {
2277 unsigned int row_start =
2278 (4 * fe.n_dofs_per_line()) + (line * fe.n_dofs_per_line());
2279
2280 for (unsigned int row = 0; row < fe.n_dofs_per_line(); ++row)
2281 {
2282 constraints.add_line(dofs_on_children[row_start + row]);
2283 for (unsigned int i = 0; i < dofs_on_mother.size(); ++i)
2284 {
2285 constraints.add_entry(
2286 dofs_on_children[row_start + row],
2287 dofs_on_mother[i],
2288 constraints_matrix[row_start + row][i]);
2289 }
2290 constraints.set_inhomogeneity(
2291 dofs_on_children[row_start + row], 0.);
2292 }
2293 }
2294
2295 // Faces:
2296 for (unsigned int f = 0; f < n_children_per_face; ++f)
2297 {
2298 unsigned int row_start =
2299 (n_lines_on_children * fe.n_dofs_per_line()) +
2300 (f * fe.n_dofs_per_quad());
2301
2302 for (unsigned int row = 0; row < fe.n_dofs_per_quad(); ++row)
2303 {
2304 constraints.add_line(dofs_on_children[row_start + row]);
2305
2306 for (unsigned int i = 0; i < dofs_on_mother.size(); ++i)
2307 {
2308 constraints.add_entry(
2309 dofs_on_children[row_start + row],
2310 dofs_on_mother[i],
2311 constraints_matrix[row_start + row][i]);
2312 }
2313
2314 constraints.set_inhomogeneity(
2315 dofs_on_children[row_start + row], 0.);
2316 }
2317 }
2318 }
2319 }
2320 }
2321
2322
2323 template <int dim, int spacedim, typename number>
2324 void
2326 const DoFHandler<dim, spacedim> &dof_handler,
2327 AffineConstraints<number> &constraints)
2328 {
2329 // note: this function is going to be hard to understand if you haven't
2330 // read the hp-paper. however, we try to follow the notation laid out
2331 // there, so go read the paper before you try to understand what is going
2332 // on here
2333
2334
2335 // a matrix to be used for constraints below. declared here and simply
2336 // resized down below to avoid permanent re-allocation of memory
2337 FullMatrix<double> constraint_matrix;
2338
2339 // similarly have arrays that will hold primary and dependent dof numbers,
2340 // as well as a scratch array needed for the complicated case below
2341 std::vector<types::global_dof_index> primary_dofs;
2342 std::vector<types::global_dof_index> dependent_dofs;
2343 std::vector<types::global_dof_index> scratch_dofs;
2344
2345 // caches for the face and subface interpolation matrices between
2346 // different (or the same) finite elements. we compute them only once,
2347 // namely the first time they are needed, and then just reuse them
2348 Table<2, std::unique_ptr<FullMatrix<double>>> face_interpolation_matrices(
2349 n_finite_elements(dof_handler), n_finite_elements(dof_handler));
2351 subface_interpolation_matrices(
2352 n_finite_elements(dof_handler),
2353 n_finite_elements(dof_handler),
2355
2356 // similarly have a cache for the matrices that are split into their
2357 // primary and dependent parts, and for which the primary part is
2358 // inverted. these two matrices are derived from the face interpolation
2359 // matrix
2360 // as described in the @ref hp_paper "hp-paper"
2361 Table<2,
2362 std::unique_ptr<std::pair<FullMatrix<double>, FullMatrix<double>>>>
2363 split_face_interpolation_matrices(n_finite_elements(dof_handler),
2364 n_finite_elements(dof_handler));
2365
2366 // finally, for each pair of finite elements, have a mask that states
2367 // which of the degrees of freedom on the coarse side of a refined face
2368 // will act as primary dofs.
2370 n_finite_elements(dof_handler), n_finite_elements(dof_handler));
2371
2372 // loop over all faces
2373 //
2374 // note that even though we may visit a face twice if the neighboring
2375 // cells are equally refined, we can only visit each face with hanging
2376 // nodes once
2377 for (const auto &cell : dof_handler.active_cell_iterators())
2378 {
2379 // artificial cells can at best neighbor ghost cells, but we're not
2380 // interested in these interfaces
2381 if (cell->is_artificial())
2382 continue;
2383
2384 for (const unsigned int face : cell->face_indices())
2385 if (cell->face(face)->has_children())
2386 {
2387 // first of all, make sure that we treat a case which is
2388 // possible, i.e. either no dofs on the face at all or no
2389 // anisotropic refinement
2390 if (cell->get_fe().n_dofs_per_face(face) == 0)
2391 continue;
2392
2393 Assert(cell->face(face)->refinement_case() ==
2396
2397 // so now we've found a face of an active cell that has
2398 // children. that means that there are hanging nodes here.
2399
2400 // in any case, faces can have at most two sets of active FE
2401 // indices, but here the face can have only one (namely the same
2402 // as that from the cell we're sitting on), and each of the
2403 // children can have only one as well. check this
2404 Assert(cell->face(face)->n_active_fe_indices() == 1,
2406 Assert(cell->face(face)->fe_index_is_active(
2407 cell->active_fe_index()) == true,
2409 for (unsigned int c = 0; c < cell->face(face)->n_children();
2410 ++c)
2411 if (!cell->neighbor_child_on_subface(face, c)
2412 ->is_artificial())
2413 Assert(cell->face(face)->child(c)->n_active_fe_indices() ==
2414 1,
2416
2417 // first find out whether we can constrain each of the subfaces
2418 // to the mother face. in the lingo of the hp-paper, this would
2419 // be the simple case. note that we can short-circuit this
2420 // decision if the dof_handler doesn't support hp at all
2421 //
2422 // ignore all interfaces with artificial cells
2423 FiniteElementDomination::Domination mother_face_dominates =
2425
2426 // auxiliary variable which holds FE indices of the mother face
2427 // and its subfaces. This knowledge will be needed in hp-case
2428 // with neither_element_dominates.
2429 std::set<types::fe_index> fe_ind_face_subface;
2430 fe_ind_face_subface.insert(cell->active_fe_index());
2431
2432 if (dof_handler.has_hp_capabilities())
2433 for (unsigned int c = 0;
2434 c < cell->face(face)->n_active_descendants();
2435 ++c)
2436 {
2437 const auto subcell =
2438 cell->neighbor_child_on_subface(face, c);
2439 if (!subcell->is_artificial())
2440 {
2441 mother_face_dominates =
2442 mother_face_dominates &
2443 (cell->get_fe().compare_for_domination(
2444 subcell->get_fe(), /*codim=*/1));
2445 fe_ind_face_subface.insert(
2446 subcell->active_fe_index());
2447 }
2448 }
2449
2450 switch (mother_face_dominates)
2451 {
2454 {
2455 // Case 1 (the simple case and the only case that can
2456 // happen for non-hp-DoFHandlers): The coarse element
2457 // dominates the elements on the subfaces (or they are
2458 // all the same)
2459 //
2460 // so we are going to constrain the DoFs on the face
2461 // children against the DoFs on the face itself
2462 primary_dofs.resize(
2463 cell->get_fe().n_dofs_per_face(face));
2464
2465 cell->face(face)->get_dof_indices(
2466 primary_dofs, cell->active_fe_index());
2467
2468 // Now create constraints for the subfaces and
2469 // assemble it. ignore all interfaces with artificial
2470 // cells because we can only get to such interfaces if
2471 // the current cell is a ghost cell
2472 for (unsigned int c = 0;
2473 c < cell->face(face)->n_children();
2474 ++c)
2475 {
2476 if (cell->neighbor_child_on_subface(face, c)
2477 ->is_artificial())
2478 continue;
2479
2480 const typename DoFHandler<dim, spacedim>::
2481 active_face_iterator subface =
2482 cell->face(face)->child(c);
2483
2484 Assert(subface->n_active_fe_indices() == 1,
2486
2487 const types::fe_index subface_fe_index =
2488 subface->nth_active_fe_index(0);
2489
2490 // we sometime run into the situation where for
2491 // example on one big cell we have a FE_Q(1) and on
2492 // the subfaces we have a mixture of FE_Q(1) and
2493 // FE_Nothing. In that case, the face domination is
2494 // either_element_can_dominate for the whole
2495 // collection of subfaces, but on the particular
2496 // subface between FE_Q(1) and FE_Nothing, there are
2497 // no constraints that we need to take care of. in
2498 // that case, just continue
2499 if (cell->get_fe().compare_for_domination(
2500 subface->get_fe(subface_fe_index),
2501 /*codim=*/1) ==
2503 continue;
2504
2505 // Same procedure as for the mother cell. Extract
2506 // the face DoFs from the cell DoFs.
2507 dependent_dofs.resize(
2508 subface->get_fe(subface_fe_index)
2509 .n_dofs_per_face(face, c));
2510 subface->get_dof_indices(dependent_dofs,
2511 subface_fe_index);
2512
2513 for (const types::global_dof_index dependent_dof :
2514 dependent_dofs)
2515 {
2516 (void)dependent_dof;
2517 Assert(dependent_dof !=
2520 }
2521
2522 // Now create the element constraint for this
2523 // subface.
2524 //
2525 // As a side remark, one may wonder the following:
2526 // neighbor_child is clearly computed correctly,
2527 // i.e. taking into account face_orientation (just
2528 // look at the implementation of that function).
2529 // however, we don't care about this here, when we
2530 // ask for subface_interpolation on subface c. the
2531 // question rather is: do we have to translate 'c'
2532 // here as well?
2533 //
2534 // the answer is in fact 'no'. if one does that,
2535 // results are wrong: constraints are added twice
2536 // for the same pair of nodes but with differing
2537 // weights. in addition, one can look at the
2538 // deal.II/project_*_03 tests that look at exactly
2539 // this case: there, we have a mesh with at least
2540 // one face_orientation==false and hanging nodes,
2541 // and the results of those tests show that the
2542 // result of projection verifies the approximation
2543 // properties of a finite element onto that mesh
2544 ensure_existence_of_subface_matrix(
2545 cell->get_fe(),
2546 subface->get_fe(subface_fe_index),
2547 c,
2548 subface_interpolation_matrices
2549 [cell->active_fe_index()][subface_fe_index][c]);
2550
2551 // Add constraints to global AffineConstraints
2552 // object.
2553 filter_constraints(primary_dofs,
2554 dependent_dofs,
2555 *(subface_interpolation_matrices
2556 [cell->active_fe_index()]
2557 [subface_fe_index][c]),
2558 constraints);
2559 } // loop over subfaces
2560
2561 break;
2562 } // Case 1
2563
2566 {
2567 // Case 2 (the "complex" case): at least one (the
2568 // neither_... case) of the finer elements or all of
2569 // them (the other_... case) is dominating. See the hp-
2570 // paper for a way how to deal with this situation
2571 //
2572 // since this is something that can only happen for hp-
2573 // dof handlers, add a check here...
2574 Assert(dof_handler.has_hp_capabilities() == true,
2576
2577 const ::hp::FECollection<dim, spacedim>
2578 &fe_collection = dof_handler.get_fe_collection();
2579 // we first have to find the finite element that is able
2580 // to generate a space that all the other ones can be
2581 // constrained to. At this point we potentially have
2582 // different scenarios:
2583 //
2584 // 1) sub-faces dominate mother face and there is a
2585 // dominating FE among sub faces. We could loop over sub
2586 // faces to find the needed FE index. However, this will
2587 // not work in the case when ...
2588 //
2589 // 2) there is no dominating FE among sub faces (e.g.
2590 // Q1xQ2 vs Q2xQ1), but subfaces still dominate mother
2591 // face (e.g. Q2xQ2). To cover this case we would have
2592 // to find the least dominating element amongst all
2593 // finite elements on sub faces.
2594 //
2595 // 3) Finally, it could happen that we got here because
2596 // neither_element_dominates (e.g. Q1xQ1xQ2 and Q1xQ2xQ1
2597 // for subfaces and Q2xQ1xQ1 for mother face). This
2598 // requires finding the least dominating element amongst
2599 // all finite elements on sub faces and the mother face.
2600 //
2601 // Note that the last solution covers the first two
2602 // scenarios, thus we stick with it assuming that we
2603 // won't lose much time/efficiency.
2604 // TODO: Change set to types::fe_index
2605 const types::fe_index dominating_fe_index =
2606 fe_collection.find_dominating_fe_extended(
2607 {fe_ind_face_subface.begin(),
2608 fe_ind_face_subface.end()},
2609 /*codim=*/1);
2610
2612 dominating_fe_index != numbers::invalid_fe_index,
2613 ExcMessage(
2614 "Could not find a least face dominating FE."));
2615
2616 const FiniteElement<dim, spacedim> &dominating_fe =
2617 dof_handler.get_fe(dominating_fe_index);
2618
2619 // first get the interpolation matrix from the mother to
2620 // the virtual dofs
2621 Assert(dominating_fe.n_dofs_per_face(face) <=
2622 cell->get_fe().n_dofs_per_face(face),
2624
2625 ensure_existence_of_face_matrix(
2626 dominating_fe,
2627 cell->get_fe(),
2628 face_interpolation_matrices[dominating_fe_index]
2629 [cell->active_fe_index()]);
2630
2631 // split this matrix into primary and dependent
2632 // components. invert the primary component
2633 ensure_existence_of_primary_dof_mask(
2634 cell->get_fe(),
2635 dominating_fe,
2636 (*face_interpolation_matrices
2637 [dominating_fe_index][cell->active_fe_index()]),
2638 primary_dof_masks[dominating_fe_index]
2639 [cell->active_fe_index()]);
2640
2641 ensure_existence_of_split_face_matrix(
2642 *face_interpolation_matrices[dominating_fe_index]
2643 [cell->active_fe_index()],
2644 (*primary_dof_masks[dominating_fe_index]
2645 [cell->active_fe_index()]),
2646 split_face_interpolation_matrices
2647 [dominating_fe_index][cell->active_fe_index()]);
2648
2649 const FullMatrix<double>
2650 &restrict_mother_to_virtual_primary_inv =
2651 (split_face_interpolation_matrices
2652 [dominating_fe_index][cell->active_fe_index()]
2653 ->first);
2654
2655 const FullMatrix<double>
2656 &restrict_mother_to_virtual_dependent =
2657 (split_face_interpolation_matrices
2658 [dominating_fe_index][cell->active_fe_index()]
2659 ->second);
2660
2661 // now compute the constraint matrix as the product
2662 // between the inverse matrix and the dependent part
2663 constraint_matrix.reinit(
2664 cell->get_fe().n_dofs_per_face(face) -
2665 dominating_fe.n_dofs_per_face(face),
2666 dominating_fe.n_dofs_per_face(face));
2667 restrict_mother_to_virtual_dependent.mmult(
2668 constraint_matrix,
2669 restrict_mother_to_virtual_primary_inv);
2670
2671 // then figure out the global numbers of primary and
2672 // dependent dofs and apply constraints
2673 scratch_dofs.resize(
2674 cell->get_fe().n_dofs_per_face(face));
2675 cell->face(face)->get_dof_indices(
2676 scratch_dofs, cell->active_fe_index());
2677
2678 // split dofs into primary and dependent components
2679 primary_dofs.clear();
2680 dependent_dofs.clear();
2681 for (unsigned int i = 0;
2682 i < cell->get_fe().n_dofs_per_face(face);
2683 ++i)
2684 if ((*primary_dof_masks[dominating_fe_index]
2685 [cell
2686 ->active_fe_index()])[i] ==
2687 true)
2688 primary_dofs.push_back(scratch_dofs[i]);
2689 else
2690 dependent_dofs.push_back(scratch_dofs[i]);
2691
2692 AssertDimension(primary_dofs.size(),
2693 dominating_fe.n_dofs_per_face(face));
2694 AssertDimension(dependent_dofs.size(),
2695 cell->get_fe().n_dofs_per_face(face) -
2696 dominating_fe.n_dofs_per_face(face));
2697
2698 filter_constraints(primary_dofs,
2699 dependent_dofs,
2700 constraint_matrix,
2701 constraints);
2702
2703
2704
2705 // next we have to deal with the subfaces. do as
2706 // discussed in the hp-paper
2707 for (unsigned int sf = 0;
2708 sf < cell->face(face)->n_children();
2709 ++sf)
2710 {
2711 // ignore interfaces with artificial cells as well
2712 // as interfaces between ghost cells in 2d
2713 if (cell->neighbor_child_on_subface(face, sf)
2714 ->is_artificial() ||
2715 (dim == 2 && cell->is_ghost() &&
2716 cell->neighbor_child_on_subface(face, sf)
2717 ->is_ghost()))
2718 continue;
2719
2720 Assert(cell->face(face)
2721 ->child(sf)
2722 ->n_active_fe_indices() == 1,
2724
2725 const types::fe_index subface_fe_index =
2726 cell->face(face)->child(sf)->nth_active_fe_index(
2727 0);
2728 const FiniteElement<dim, spacedim> &subface_fe =
2729 dof_handler.get_fe(subface_fe_index);
2730
2731 // first get the interpolation matrix from the
2732 // subface to the virtual dofs
2733 Assert(dominating_fe.n_dofs_per_face(face) <=
2734 subface_fe.n_dofs_per_face(face),
2736 ensure_existence_of_subface_matrix(
2737 dominating_fe,
2738 subface_fe,
2739 sf,
2740 subface_interpolation_matrices
2741 [dominating_fe_index][subface_fe_index][sf]);
2742
2743 const FullMatrix<double>
2744 &restrict_subface_to_virtual = *(
2745 subface_interpolation_matrices
2746 [dominating_fe_index][subface_fe_index][sf]);
2747
2748 constraint_matrix.reinit(
2749 subface_fe.n_dofs_per_face(face),
2750 dominating_fe.n_dofs_per_face(face));
2751
2752 restrict_subface_to_virtual.mmult(
2753 constraint_matrix,
2754 restrict_mother_to_virtual_primary_inv);
2755
2756 dependent_dofs.resize(
2757 subface_fe.n_dofs_per_face(face));
2758 cell->face(face)->child(sf)->get_dof_indices(
2759 dependent_dofs, subface_fe_index);
2760
2761 filter_constraints(primary_dofs,
2762 dependent_dofs,
2763 constraint_matrix,
2764 constraints);
2765 } // loop over subfaces
2766
2767 break;
2768 } // Case 2
2769
2771 // there are no continuity requirements between the two
2772 // elements. record no constraints
2773 break;
2774
2775 default:
2776 // we shouldn't get here
2778 }
2779 }
2780 else
2781 {
2782 // this face has no children, but it could still be that it is
2783 // shared by two cells that use a different FE index
2784 Assert(cell->face(face)->fe_index_is_active(
2785 cell->active_fe_index()) == true,
2787
2788 // see if there is a neighbor that is an artificial cell. in
2789 // that case, we're not interested in this interface. we test
2790 // this case first since artificial cells may not have an
2791 // active FE index set, etc
2792 if (!cell->at_boundary(face) &&
2793 cell->neighbor(face)->is_artificial())
2794 continue;
2795
2796 // Only if there is a neighbor with a different active FE index
2797 // and the same h-level, some action has to be taken.
2798 if ((dof_handler.has_hp_capabilities()) &&
2799 !cell->face(face)->at_boundary() &&
2800 (cell->neighbor(face)->active_fe_index() !=
2801 cell->active_fe_index()) &&
2802 (!cell->face(face)->has_children() &&
2803 !cell->neighbor_is_coarser(face)))
2804 {
2805 const typename DoFHandler<dim,
2806 spacedim>::level_cell_iterator
2807 neighbor = cell->neighbor(face);
2808
2809 // see which side of the face we have to constrain
2810 switch (
2811 cell->get_fe().compare_for_domination(neighbor->get_fe(),
2812 /*codim=*/1))
2813 {
2815 {
2816 // Get DoFs on dominating and dominated side of the
2817 // face
2818 primary_dofs.resize(
2819 cell->get_fe().n_dofs_per_face(face));
2820 cell->face(face)->get_dof_indices(
2821 primary_dofs, cell->active_fe_index());
2822
2823 // break if the n_primary_dofs == 0, because we are
2824 // attempting to constrain to an element that has no
2825 // face dofs
2826 if (primary_dofs.empty())
2827 break;
2828
2829 dependent_dofs.resize(
2830 neighbor->get_fe().n_dofs_per_face(face));
2831 cell->face(face)->get_dof_indices(
2832 dependent_dofs, neighbor->active_fe_index());
2833
2834 // make sure the element constraints for this face
2835 // are available
2836 ensure_existence_of_face_matrix(
2837 cell->get_fe(),
2838 neighbor->get_fe(),
2839 face_interpolation_matrices
2840 [cell->active_fe_index()]
2841 [neighbor->active_fe_index()]);
2842
2843 // Add constraints to global constraint matrix.
2844 filter_constraints(
2845 primary_dofs,
2846 dependent_dofs,
2847 *(face_interpolation_matrices
2848 [cell->active_fe_index()]
2849 [neighbor->active_fe_index()]),
2850 constraints);
2851
2852 break;
2853 }
2854
2856 {
2857 // we don't do anything here since we will come back
2858 // to this face from the other cell, at which time
2859 // we will fall into the first case clause above
2860 break;
2861 }
2862
2865 {
2866 // it appears as if neither element has any
2867 // constraints on its neighbor. this may be because
2868 // neither element has any DoFs on faces at all. or
2869 // that the two elements are actually the same,
2870 // although they happen to run under different
2871 // fe_indices (this is what happens in
2872 // hp/hp_hanging_nodes_01 for example).
2873 //
2874 // another possibility is what happens in crash_13.
2875 // there, we have FESystem(FE_Q(1),FE_DGQ(0)) vs.
2876 // FESystem(FE_Q(1),FE_DGQ(1)). neither of them
2877 // dominates the other.
2878 //
2879 // a final possibility is that we have something
2880 // like FESystem(FE_Q(1),FE_Q(1)) vs
2881 // FESystem(FE_Q(1),FE_Nothing()), see
2882 // hp/fe_nothing_18/19.
2883 //
2884 // in any case, the point is that it doesn't matter.
2885 // there is nothing to do here.
2886 break;
2887 }
2888
2890 {
2891 // make sure we don't get here twice from each cell
2892 if (cell < neighbor)
2893 break;
2894
2895 // our best bet is to find the common space among
2896 // other FEs in FECollection and then constrain both
2897 // FEs to that one. More precisely, we follow the
2898 // strategy outlined on page 17 of the hp-paper:
2899 // First we find the dominant FE space S. Then we
2900 // divide our dofs in primary and dependent such
2901 // that I^{face,primary}_{S^{face}->S} is
2902 // invertible. And finally constrain dependent dofs
2903 // to primary dofs based on the interpolation
2904 // matrix.
2905
2906 const types::fe_index this_fe_index =
2907 cell->active_fe_index();
2908 const types::fe_index neighbor_fe_index =
2909 neighbor->active_fe_index();
2910 std::set<types::fe_index> fes;
2911 fes.insert(this_fe_index);
2912 fes.insert(neighbor_fe_index);
2913 const ::hp::FECollection<dim, spacedim>
2914 &fe_collection = dof_handler.get_fe_collection();
2915
2916 // TODO: Change set to types::fe_index
2917 const types::fe_index dominating_fe_index =
2918 fe_collection.find_dominating_fe_extended(
2919 {fes.begin(), fes.end()}, /*codim=*/1);
2920
2922 dominating_fe_index != numbers::invalid_fe_index,
2923 ExcMessage(
2924 "Could not find the dominating FE for " +
2925 cell->get_fe().get_name() + " and " +
2926 neighbor->get_fe().get_name() +
2927 " inside FECollection."));
2928
2929 const FiniteElement<dim, spacedim> &dominating_fe =
2930 fe_collection[dominating_fe_index];
2931
2932 // TODO: until we hit the second face, the code is a
2933 // copy-paste from h-refinement case...
2934
2935 // first get the interpolation matrix from main FE
2936 // to the virtual dofs
2937 Assert(dominating_fe.n_dofs_per_face(face) <=
2938 cell->get_fe().n_dofs_per_face(face),
2940
2941 ensure_existence_of_face_matrix(
2942 dominating_fe,
2943 cell->get_fe(),
2944 face_interpolation_matrices
2945 [dominating_fe_index][cell->active_fe_index()]);
2946
2947 // split this matrix into primary and dependent
2948 // components. invert the primary component
2949 ensure_existence_of_primary_dof_mask(
2950 cell->get_fe(),
2951 dominating_fe,
2952 (*face_interpolation_matrices
2953 [dominating_fe_index]
2954 [cell->active_fe_index()]),
2955 primary_dof_masks[dominating_fe_index]
2956 [cell->active_fe_index()]);
2957
2958 ensure_existence_of_split_face_matrix(
2959 *face_interpolation_matrices
2960 [dominating_fe_index][cell->active_fe_index()],
2961 (*primary_dof_masks[dominating_fe_index]
2962 [cell->active_fe_index()]),
2963 split_face_interpolation_matrices
2964 [dominating_fe_index][cell->active_fe_index()]);
2965
2966 const FullMatrix<
2967 double> &restrict_mother_to_virtual_primary_inv =
2968 (split_face_interpolation_matrices
2969 [dominating_fe_index][cell->active_fe_index()]
2970 ->first);
2971
2972 const FullMatrix<
2973 double> &restrict_mother_to_virtual_dependent =
2974 (split_face_interpolation_matrices
2975 [dominating_fe_index][cell->active_fe_index()]
2976 ->second);
2977
2978 // now compute the constraint matrix as the product
2979 // between the inverse matrix and the dependent part
2980 constraint_matrix.reinit(
2981 cell->get_fe().n_dofs_per_face(face) -
2982 dominating_fe.n_dofs_per_face(face),
2983 dominating_fe.n_dofs_per_face(face));
2984 restrict_mother_to_virtual_dependent.mmult(
2985 constraint_matrix,
2986 restrict_mother_to_virtual_primary_inv);
2987
2988 // then figure out the global numbers of primary and
2989 // dependent dofs and apply constraints
2990 scratch_dofs.resize(
2991 cell->get_fe().n_dofs_per_face(face));
2992 cell->face(face)->get_dof_indices(
2993 scratch_dofs, cell->active_fe_index());
2994
2995 // split dofs into primary and dependent components
2996 primary_dofs.clear();
2997 dependent_dofs.clear();
2998 for (unsigned int i = 0;
2999 i < cell->get_fe().n_dofs_per_face(face);
3000 ++i)
3001 if ((*primary_dof_masks[dominating_fe_index]
3002 [cell->active_fe_index()])
3003 [i] == true)
3004 primary_dofs.push_back(scratch_dofs[i]);
3005 else
3006 dependent_dofs.push_back(scratch_dofs[i]);
3007
3008 AssertDimension(primary_dofs.size(),
3009 dominating_fe.n_dofs_per_face(
3010 face));
3012 dependent_dofs.size(),
3013 cell->get_fe().n_dofs_per_face(face) -
3014 dominating_fe.n_dofs_per_face(face));
3015
3016 filter_constraints(primary_dofs,
3017 dependent_dofs,
3018 constraint_matrix,
3019 constraints);
3020
3021 // now do the same for another FE this is pretty
3022 // much the same we do above to resolve h-refinement
3023 // constraints
3024 Assert(dominating_fe.n_dofs_per_face(face) <=
3025 neighbor->get_fe().n_dofs_per_face(face),
3027
3028 ensure_existence_of_face_matrix(
3029 dominating_fe,
3030 neighbor->get_fe(),
3031 face_interpolation_matrices
3032 [dominating_fe_index]
3033 [neighbor->active_fe_index()]);
3034
3035 const FullMatrix<double>
3036 &restrict_secondface_to_virtual =
3037 *(face_interpolation_matrices
3038 [dominating_fe_index]
3039 [neighbor->active_fe_index()]);
3040
3041 constraint_matrix.reinit(
3042 neighbor->get_fe().n_dofs_per_face(face),
3043 dominating_fe.n_dofs_per_face(face));
3044
3045 restrict_secondface_to_virtual.mmult(
3046 constraint_matrix,
3047 restrict_mother_to_virtual_primary_inv);
3048
3049 dependent_dofs.resize(
3050 neighbor->get_fe().n_dofs_per_face(face));
3051 cell->face(face)->get_dof_indices(
3052 dependent_dofs, neighbor->active_fe_index());
3053
3054 filter_constraints(primary_dofs,
3055 dependent_dofs,
3056 constraint_matrix,
3057 constraints);
3058
3059 break;
3060 }
3061
3063 {
3064 // nothing to do here
3065 break;
3066 }
3067
3068 default:
3069 // we shouldn't get here
3071 }
3072 }
3073 }
3074 }
3075 }
3076 } // namespace internal
3077
3078
3079
3080 template <int dim, int spacedim, typename number>
3081 void
3083 AffineConstraints<number> &constraints)
3084 {
3085 Assert(dof_handler.has_active_dofs(),
3086 ExcMessage(
3087 "The given DoFHandler does not have any DoFs. Did you forget to "
3088 "call dof_handler.distribute_dofs()?"));
3089
3090 // Decide whether to use make_hanging_node_constraints_nedelec,
3091 // the new or old make_hanging_node_constraints
3092 // function. If all the FiniteElement or all elements in a FECollection
3093 // support the new face constraint matrix, the new code will be used.
3094 // Otherwise, the old implementation is used for the moment.
3095 if (dof_handler.get_fe().get_name().find("FE_NedelecSZ") !=
3096 std::string::npos)
3098 dof_handler, constraints, std::integral_constant<int, dim>());
3099 else if (dof_handler.get_fe_collection().hp_constraints_are_implemented())
3100 internal::make_hp_hanging_node_constraints(dof_handler, constraints);
3101 else
3103 dof_handler, constraints, std::integral_constant<int, dim>());
3104 }
3105
3106
3107
3108 namespace internal
3109 {
3110 template <typename FaceIterator, typename number>
3111 void
3113 const FaceIterator &face_1,
3115 const FullMatrix<double> &transformation,
3116 AffineConstraints<number> &affine_constraints,
3117 const ComponentMask &component_mask,
3118 const types::geometric_orientation combined_orientation,
3119 const number periodicity_factor,
3120 const unsigned int level)
3121 {
3122 static const int dim = FaceIterator::AccessorType::dimension;
3123 static const int spacedim = FaceIterator::AccessorType::space_dimension;
3124
3125 const bool use_mg = (level != numbers::invalid_unsigned_int);
3126
3127 // If we don't use multigrid, we should be in the case where face_1 is
3128 // active, i.e. has no children. In the case of multigrid, constraints
3129 // between cells on the same level are set up.
3130
3131 Assert(use_mg || (!face_1->has_children()), ExcInternalError());
3132
3133 Assert(face_1->n_active_fe_indices() == 1, ExcInternalError());
3134
3135 // TODO: the implementation makes the assumption that all faces have the
3136 // same number of dofs
3138 face_1->get_fe(face_1->nth_active_fe_index(0)).n_unique_faces(), 1);
3140 face_2->get_fe(face_2->nth_active_fe_index(0)).n_unique_faces(), 1);
3141 const unsigned int face_no = 0;
3142
3143 // If we don't use multigrid and face_2 does have children,
3144 // then we need to iterate over these children and set periodic
3145 // constraints in the inverse direction. In the case of multigrid,
3146 // we don't need to do this, since constraints between cells on
3147 // the same level are set up.
3148
3149 if ((!use_mg) && face_2->has_children())
3150 {
3151 Assert(face_2->n_children() ==
3154
3155 const unsigned int dofs_per_face =
3156 face_1->get_fe(face_1->nth_active_fe_index(0))
3157 .n_dofs_per_face(face_no);
3158
3159 // Skip further recursion if face_1 carries invalid dof indices,
3160 // i.e., it is on an artificial cell.
3161 std::vector<types::global_dof_index> dofs_1(dofs_per_face);
3162 face_1->get_dof_indices(dofs_1, face_1->nth_active_fe_index(0));
3163 for (unsigned int i = 0; i < dofs_per_face; ++i)
3164 if (dofs_1[i] == numbers::invalid_dof_index)
3165 {
3166 return;
3167 }
3168
3169 FullMatrix<double> child_transformation(dofs_per_face, dofs_per_face);
3170 FullMatrix<double> subface_interpolation(dofs_per_face,
3171 dofs_per_face);
3172
3173 for (unsigned int c = 0; c < face_2->n_children(); ++c)
3174 {
3175 // get the interpolation matrix recursively from the one that
3176 // interpolated from face_1 to face_2 by multiplying from the left
3177 // with the one that interpolates from face_2 to its child
3178 const auto &fe = face_1->get_fe(face_1->nth_active_fe_index(0));
3179 fe.get_subface_interpolation_matrix(fe,
3180 c,
3181 subface_interpolation,
3182 face_no);
3183 subface_interpolation.mmult(child_transformation, transformation);
3184
3186 face_2->child(c),
3187 child_transformation,
3188 affine_constraints,
3189 component_mask,
3190 combined_orientation,
3191 periodicity_factor);
3192 }
3193 return;
3194 }
3195
3196 //
3197 // If we reached this point then both faces are active. Now all
3198 // that is left is to match the corresponding DoFs of both faces.
3199 //
3200
3201 const types::fe_index face_1_index = face_1->nth_active_fe_index(0);
3202 const types::fe_index face_2_index = face_2->nth_active_fe_index(0);
3203 Assert(face_1->get_fe(face_1_index) == face_2->get_fe(face_2_index),
3204 ExcMessage(
3205 "Matching periodic cells need to use the same finite element"));
3206
3207 const FiniteElement<dim, spacedim> &fe = face_1->get_fe(face_1_index);
3208
3209 Assert(component_mask.represents_n_components(fe.n_components()),
3210 ExcMessage(
3211 "The number of components in the mask has to be either "
3212 "zero or equal to the number of components in the finite "
3213 "element."));
3214
3215 const unsigned int dofs_per_face = fe.n_dofs_per_face(face_no);
3216
3217 std::vector<types::global_dof_index> dofs_1(dofs_per_face);
3218 std::vector<types::global_dof_index> dofs_2(dofs_per_face);
3219
3220 if (use_mg)
3221 face_1->get_mg_dof_indices(level, dofs_1, face_1_index);
3222 else
3223 face_1->get_dof_indices(dofs_1, face_1_index);
3224
3225 if (use_mg)
3226 face_2->get_mg_dof_indices(level, dofs_2, face_2_index);
3227 else
3228 face_2->get_dof_indices(dofs_2, face_2_index);
3229
3230 // If either of the two faces has an invalid dof index, stop. This is
3231 // so that there is no attempt to match artificial cells of parallel
3232 // distributed triangulations.
3233 //
3234 // While it seems like we ought to be able to avoid even calling
3235 // set_periodicity_constraints for artificial faces, this situation
3236 // can arise when a face that is being made periodic is only
3237 // partially touched by the local subdomain.
3238 // make_periodicity_constraints will be called recursively even for
3239 // the section of the face that is not touched by the local
3240 // subdomain.
3241 //
3242 // Until there is a better way to determine if the cells that
3243 // neighbor a face are artificial, we simply test to see if the face
3244 // does not have a valid dof initialization.
3245
3246 for (unsigned int i = 0; i < dofs_per_face; ++i)
3247 if (dofs_1[i] == numbers::invalid_dof_index ||
3248 dofs_2[i] == numbers::invalid_dof_index)
3249 {
3250 return;
3251 }
3252
3253 // In the case of shared Triangulation with artificial cells all
3254 // cells have valid DoF indices, i.e., the check above does not work.
3255 if (const auto tria = dynamic_cast<
3257 &face_1->get_triangulation()))
3258 if (tria->with_artificial_cells() &&
3259 (affine_constraints.get_local_lines().size() != 0))
3260 for (unsigned int i = 0; i < dofs_per_face; ++i)
3261 if ((affine_constraints.get_local_lines().is_element(dofs_1[i]) ==
3262 false) ||
3263 (affine_constraints.get_local_lines().is_element(dofs_2[i]) ==
3264 false))
3265 {
3266 return;
3267 }
3268
3269 // Well, this is a hack:
3270 //
3271 // There is no
3272 // face_to_face_index(face_index,
3273 // face_orientation,
3274 // face_flip,
3275 // face_rotation)
3276 // function in FiniteElementData, so we have to use
3277 // face_to_cell_index(face_index, face
3278 // face_orientation,
3279 // face_flip,
3280 // face_rotation)
3281 // But this will give us an index on a cell - something we cannot work
3282 // with directly. But luckily we can match them back :-]
3283
3284 std::map<unsigned int, unsigned int> cell_to_rotated_face_index;
3285
3286 // Build up a cell to face index for face_2:
3287 for (unsigned int i = 0; i < dofs_per_face; ++i)
3288 {
3289 const unsigned int cell_index = fe.face_to_cell_index(
3290 i,
3291 // It doesn't really matter, just assume we're on the first face...
3292 0);
3293 cell_to_rotated_face_index[cell_index] = i;
3294 }
3295
3296 // Build constraints in a vector of pairs that can be
3297 // arbitrarily large, but that holds up to 25 elements without
3298 // external memory allocation. This is good enough for hanging
3299 // node constraints of Q4 elements in 3d, so covers most
3300 // common cases.
3301 boost::container::small_vector<
3302 std::pair<typename AffineConstraints<number>::size_type, number>,
3303 25>
3304 constraint_entries;
3305
3306 //
3307 // Loop over all dofs on face 2 and constrain them against all
3308 // matching dofs on face 1:
3309 //
3310 for (unsigned int i = 0; i < dofs_per_face; ++i)
3311 {
3312 // Obey the component mask
3313 if ((component_mask.n_selected_components(fe.n_components()) !=
3314 fe.n_components()) &&
3315 !component_mask[fe.face_system_to_component_index(i, face_no)
3316 .first])
3317 continue;
3318
3319 // We have to be careful to treat so called "identity
3320 // constraints" special. These are constraints of the form
3321 // x1 == constraint_factor * x_2. In this case, if the constraint
3322 // x2 == 1./constraint_factor * x1 already exists we are in trouble.
3323 //
3324 // Consequently, we have to check that we have indeed such an
3325 // "identity constraint". We do this by looping over all entries
3326 // of the row of the transformation matrix and check whether we
3327 // find exactly one nonzero entry. If this is the case, set
3328 // "is_identity_constrained" to true and record the corresponding
3329 // index and constraint_factor.
3330
3331 bool is_identity_constrained = false;
3332 unsigned int target = numbers::invalid_unsigned_int;
3333 number constraint_factor = periodicity_factor;
3334
3335 constexpr double eps = 1.e-13;
3336 for (unsigned int jj = 0; jj < dofs_per_face; ++jj)
3337 {
3338 const auto entry = transformation(i, jj);
3339 if (std::abs(entry) > eps)
3340 {
3341 if (is_identity_constrained)
3342 {
3343 // We did encounter more than one nonzero entry, so
3344 // the dof is not identity constrained. Set the
3345 // boolean to false and break out of the for loop.
3346 is_identity_constrained = false;
3347 break;
3348 }
3349 is_identity_constrained = true;
3350 target = jj;
3351 constraint_factor = entry * periodicity_factor;
3352 }
3353 }
3354
3355 // Next, we work on all constraints that are not identity
3356 // constraints, i.e., constraints that involve an interpolation
3357 // step that constrains the current dof (on face 2) to more than
3358 // one dof on face 1.
3359
3360 if (!is_identity_constrained)
3361 {
3362 // The current dof is already constrained. There is nothing
3363 // left to do.
3364 if (affine_constraints.is_constrained(dofs_2[i]))
3365 continue;
3366
3367 constraint_entries.clear();
3368 constraint_entries.reserve(dofs_per_face);
3369
3370 for (unsigned int jj = 0; jj < dofs_per_face; ++jj)
3371 {
3372 // Get the correct dof index on face_1 respecting the
3373 // given orientation:
3374 const unsigned int j =
3375 cell_to_rotated_face_index[fe.face_to_cell_index(
3376 jj, 0, combined_orientation)];
3377
3378 if (std::abs(transformation(i, jj)) > eps)
3379 constraint_entries.emplace_back(dofs_1[j],
3380 transformation(i, jj));
3381 }
3382
3383 // Enter the constraint::
3384 affine_constraints.add_constraint(dofs_2[i],
3385 constraint_entries,
3386 0.);
3387
3388
3389 // Continue with next dof.
3390 continue;
3391 }
3392
3393 // We are left with an "identity constraint".
3394
3395 // Get the correct dof index on face_1 respecting the given
3396 // orientation:
3397 const unsigned int j =
3398 cell_to_rotated_face_index[fe.face_to_cell_index(
3399 target, 0, combined_orientation)];
3400
3401 auto dof_left = dofs_1[j];
3402 auto dof_right = dofs_2[i];
3403
3404 // If dof_left is already constrained, or dof_left < dof_right we
3405 // flip the order to ensure that dofs are constrained in a stable
3406 // manner on different MPI processes.
3407 if (affine_constraints.is_constrained(dof_left) ||
3408 (dof_left < dof_right &&
3409 !affine_constraints.is_constrained(dof_right)))
3410 {
3411 std::swap(dof_left, dof_right);
3412 constraint_factor = 1. / constraint_factor;
3413 }
3414
3415 // Next, we try to enter the constraint
3416 // dof_left = constraint_factor * dof_right;
3417
3418 // If both degrees of freedom are constrained, there is nothing we
3419 // can do. Simply continue with the next dof.
3420 if (affine_constraints.is_constrained(dof_left) &&
3421 affine_constraints.is_constrained(dof_right))
3422 continue;
3423
3424 // We have to be careful that adding the current identity
3425 // constraint does not create a constraint cycle. Thus, check for
3426 // a dependency cycle:
3427
3428 bool constraints_are_cyclic = true;
3429 number cycle_constraint_factor = constraint_factor;
3430
3431 for (auto test_dof = dof_right; test_dof != dof_left;)
3432 {
3433 if (!affine_constraints.is_constrained(test_dof))
3434 {
3435 constraints_are_cyclic = false;
3436 break;
3437 }
3438
3439 const auto &constraint_entries =
3440 *affine_constraints.get_constraint_entries(test_dof);
3441 if (constraint_entries.size() == 1)
3442 {
3443 test_dof = constraint_entries[0].first;
3444 cycle_constraint_factor *= constraint_entries[0].second;
3445 }
3446 else
3447 {
3448 constraints_are_cyclic = false;
3449 break;
3450 }
3451 }
3452
3453 // In case of a dependency cycle we, either
3454 // - do nothing if cycle_constraint_factor == 1. In this case all
3455 // degrees
3456 // of freedom are already periodically constrained,
3457 // - otherwise, force all dofs to zero (by setting dof_left to
3458 // zero). The reasoning behind this is the fact that
3459 // cycle_constraint_factor != 1 occurs in situations such as
3460 // x1 == x2 and x2 == -1. * x1. This system is only solved by
3461 // x_1 = x_2 = 0.
3462
3463 if (constraints_are_cyclic)
3464 {
3465 if (std::abs(cycle_constraint_factor - number(1.)) > eps)
3466 affine_constraints.constrain_dof_to_zero(dof_left);
3467 }
3468 else
3469 {
3470 affine_constraints.add_constraint(
3471 dof_left, {{dof_right, constraint_factor}}, 0.);
3472 // The number 1e10 in the assert below is arbitrary. If the
3473 // absolute value of constraint_factor is too large, then probably
3474 // the absolute value of periodicity_factor is too large or too
3475 // small. This would be equivalent to an evanescent wave that has
3476 // a very small wavelength. A quick calculation shows that if
3477 // |periodicity_factor| > 1e10 -> |np.exp(ikd)|> 1e10, therefore k
3478 // is imaginary (evanescent wave) and the evanescent wavelength is
3479 // 0.27 times smaller than the dimension of the structure,
3480 // lambda=((2*pi)/log(1e10))*d. Imaginary wavenumbers can be
3481 // interesting in some cases
3482 // (https://doi.org/10.1103/PhysRevA.94.033813).In order to
3483 // implement the case of in which the wavevector can be imaginary
3484 // it would be necessary to rewrite this function and the dof
3485 // ordering method should be modified.
3486 // Let's take the following constraint a*x1 + b*x2 = 0. You could
3487 // just always pick x1 = b/a*x2, but in practice this is not so
3488 // stable if a could be a small number -- intended to be zero, but
3489 // just very small due to roundoff. Of course, constraining x2 in
3490 // terms of x1 has the same problem. So one chooses x1 = b/a*x2 if
3491 // |b|<|a|, and x2 = a/b*x1 if |a|<|b|.
3492 Assert(std::abs(constraint_factor) < 1e10,
3493 ExcMessage("The periodicity constraint is too large. "
3494 "The parameter periodicity_factor might "
3495 "be too large or too small."));
3496 }
3497 } /* for dofs_per_face */
3498 }
3499 } // namespace internal
3500
3501
3502 namespace
3503 {
3504 // Internally used in make_periodicity_constraints.
3505 //
3506 // Build up a (possibly rotated) interpolation matrix that is used in
3507 // set_periodicity_constraints with the help of user supplied matrix and
3508 // first_vector_components.
3509 template <int dim, int spacedim>
3511 compute_transformation(
3513 const FullMatrix<double> &matrix,
3514 const std::vector<unsigned int> &first_vector_components)
3515 {
3516 // TODO: the implementation makes the assumption that all faces have the
3517 // same number of dofs
3519 const unsigned int face_no = 0;
3520
3521 Assert(matrix.m() == matrix.n(), ExcInternalError());
3522
3523 const unsigned int n_dofs_per_face = fe.n_dofs_per_face(face_no);
3524
3525 if (matrix.m() == n_dofs_per_face)
3526 {
3527 // In case of m == n == n_dofs_per_face the supplied matrix is already
3528 // an interpolation matrix, so we use it directly:
3529 return matrix;
3530 }
3531
3532 if (first_vector_components.empty() && matrix.m() == 0)
3533 {
3534 // Just the identity matrix in case no rotation is specified:
3535 return IdentityMatrix(n_dofs_per_face);
3536 }
3537
3538 // The matrix describes a rotation and we have to build a transformation
3539 // matrix, we assume that for a 0* rotation we would have to build the
3540 // identity matrix
3541
3542 Assert(matrix.m() == spacedim, ExcInternalError());
3543
3544 const Quadrature<dim - 1> quadrature(
3545 fe.get_unit_face_support_points(face_no));
3546
3547 // have an array that stores the location of each vector-dof tuple we want
3548 // to rotate.
3549 using DoFTuple = std::array<unsigned int, spacedim>;
3550
3551 // start with a pristine interpolation matrix...
3552 FullMatrix<double> transformation = IdentityMatrix(n_dofs_per_face);
3553
3554 for (unsigned int i = 0; i < n_dofs_per_face; ++i)
3555 {
3556 std::vector<unsigned int>::const_iterator comp_it =
3557 std::find(first_vector_components.begin(),
3558 first_vector_components.end(),
3559 fe.face_system_to_component_index(i, face_no).first);
3560 if (comp_it != first_vector_components.end())
3561 {
3562 const unsigned int first_vector_component = *comp_it;
3563
3564 // find corresponding other components of vector
3565 DoFTuple vector_dofs;
3566 vector_dofs[0] = i;
3567 unsigned int n_found = 1;
3568
3569 Assert(
3570 *comp_it + spacedim <= fe.n_components(),
3571 ExcMessage(
3572 "Error: the finite element does not have enough components "
3573 "to define rotated periodic boundaries."));
3574
3575 for (unsigned int k = 0; k < n_dofs_per_face; ++k)
3576 if ((k != i) && (quadrature.point(k) == quadrature.point(i)) &&
3577 (fe.face_system_to_component_index(k, face_no).first >=
3578 first_vector_component) &&
3579 (fe.face_system_to_component_index(k, face_no).first <
3580 first_vector_component + spacedim))
3581 {
3582 vector_dofs[fe.face_system_to_component_index(k, face_no)
3583 .first -
3584 first_vector_component] = k;
3585 ++n_found;
3586 if (n_found == dim)
3587 break;
3588 }
3589
3590 // ... and rotate all dofs belonging to vector valued components
3591 // that are selected by first_vector_components:
3592 for (unsigned int i = 0; i < spacedim; ++i)
3593 {
3594 transformation[vector_dofs[i]][vector_dofs[i]] = 0.;
3595 for (unsigned int j = 0; j < spacedim; ++j)
3596 transformation[vector_dofs[i]][vector_dofs[j]] =
3597 matrix[i][j];
3598 }
3599 }
3600 }
3601 return transformation;
3602 }
3603 } /*namespace*/
3604
3605
3606 // Low level interface:
3607
3608
3609 template <typename FaceIterator, typename number>
3610 void
3612 const FaceIterator &face_1,
3614 AffineConstraints<number> &affine_constraints,
3615 const ComponentMask &component_mask,
3616 const types::geometric_orientation combined_orientation,
3617 const FullMatrix<double> &matrix,
3618 const std::vector<unsigned int> &first_vector_components,
3619 const number periodicity_factor)
3620 {
3621 static const int dim = FaceIterator::AccessorType::dimension;
3622 static const int spacedim = FaceIterator::AccessorType::space_dimension;
3623
3624 if constexpr (running_in_debug_mode())
3625 {
3626 const auto [orientation, rotation, flip] =
3627 ::internal::split_face_orientation(combined_orientation);
3628
3629 Assert((dim != 1) ||
3630 (orientation == true && flip == false && rotation == false),
3631 ExcMessage(
3632 "The supplied orientation (orientation, rotation, flip) "
3633 "is invalid for 1d"));
3634
3635 Assert((dim != 2) || (flip == false && rotation == false),
3636 ExcMessage(
3637 "The supplied orientation (orientation, rotation, flip) "
3638 "is invalid for 2d"));
3639
3640 Assert(face_1 != face_2,
3641 ExcMessage("face_1 and face_2 are equal! Cannot constrain DoFs "
3642 "on the very same face"));
3643
3644 Assert(face_1->at_boundary() && face_2->at_boundary(),
3645 ExcMessage("Faces for periodicity constraints must be on the "
3646 "boundary"));
3647
3648 Assert(matrix.m() == matrix.n(),
3649 ExcMessage(
3650 "The supplied (rotation or interpolation) matrix must "
3651 "be a square matrix"));
3652
3653 Assert(first_vector_components.empty() || matrix.m() == spacedim,
3654 ExcMessage("first_vector_components is nonempty, so matrix must "
3655 "be a rotation matrix exactly of size spacedim"));
3656
3657 if (!face_1->has_children())
3658 {
3659 // TODO: the implementation makes the assumption that all faces have
3660 // the same number of dofs
3662 face_1->get_fe(face_1->nth_active_fe_index(0)).n_unique_faces(),
3663 1);
3664 const unsigned int face_no = 0;
3665
3666 Assert(face_1->n_active_fe_indices() == 1, ExcInternalError());
3667 const unsigned int n_dofs_per_face =
3668 face_1->get_fe(face_1->nth_active_fe_index(0))
3669 .n_dofs_per_face(face_no);
3670
3671 Assert(matrix.m() == 0 ||
3672 (first_vector_components.empty() &&
3673 matrix.m() == n_dofs_per_face) ||
3674 (!first_vector_components.empty() &&
3675 matrix.m() == spacedim),
3676 ExcMessage(
3677 "The matrix must have either size 0 or spacedim "
3678 "(if first_vector_components is nonempty) "
3679 "or the size must be equal to the # of DoFs on the face "
3680 "(if first_vector_components is empty)."));
3681 }
3682
3683 if (!face_2->has_children())
3684 {
3685 // TODO: the implementation makes the assumption that all faces have
3686 // the same number of dofs
3688 face_2->get_fe(face_2->nth_active_fe_index(0)).n_unique_faces(),
3689 1);
3690 const unsigned int face_no = 0;
3691
3692 Assert(face_2->n_active_fe_indices() == 1, ExcInternalError());
3693 const unsigned int n_dofs_per_face =
3694 face_2->get_fe(face_2->nth_active_fe_index(0))
3695 .n_dofs_per_face(face_no);
3696
3697 Assert(matrix.m() == 0 ||
3698 (first_vector_components.empty() &&
3699 matrix.m() == n_dofs_per_face) ||
3700 (!first_vector_components.empty() &&
3701 matrix.m() == spacedim),
3702 ExcMessage(
3703 "The matrix must have either size 0 or spacedim "
3704 "(if first_vector_components is nonempty) "
3705 "or the size must be equal to the # of DoFs on the face "
3706 "(if first_vector_components is empty)."));
3707 }
3708 }
3709
3710 if (face_1->has_children() && face_2->has_children())
3711 {
3712 // In the case that both faces have children, we loop over all children
3713 // and apply make_periodicity_constraints() recursively:
3714
3715 Assert(face_1->n_children() ==
3717 face_2->n_children() ==
3720
3721 for (unsigned int i = 0; i < GeometryInfo<dim>::max_children_per_face;
3722 ++i)
3723 {
3724 // We need to access the subface indices without knowing the face
3725 // number. Hence, we pick the lowest-value face: i.e., face 2 in 2D
3726 // has subfaces {0, 1} and face 4 in 3D has subfaces {0, 1, 2, 3}.
3727 const unsigned int face_no = dim == 2 ? 2 : 4;
3728
3729 // Lookup the index for the second face. Like the assertions above,
3730 // this is only presently valid for hypercube meshes.
3731 const auto reference_cell = ReferenceCells::get_hypercube<dim>();
3732 const unsigned int j =
3733 reference_cell.child_cell_on_face(face_no,
3734 i,
3735 combined_orientation);
3736
3737 make_periodicity_constraints(face_1->child(i),
3738 face_2->child(j),
3739 affine_constraints,
3740 component_mask,
3741 combined_orientation,
3742 matrix,
3743 first_vector_components,
3744 periodicity_factor);
3745 }
3746 }
3747 else
3748 {
3749 // Otherwise at least one of the two faces is active and we need to do
3750 // some work and enter the constraints!
3751
3752 // The finite element that matters is the one on the active face:
3754 face_1->has_children() ?
3755 face_2->get_fe(face_2->nth_active_fe_index(0)) :
3756 face_1->get_fe(face_1->nth_active_fe_index(0));
3757
3758 // TODO: the implementation makes the assumption that all faces have the
3759 // same number of dofs
3761 const unsigned int face_no = 0;
3762
3763 const unsigned int n_dofs_per_face = fe.n_dofs_per_face(face_no);
3764
3765 // Sometimes we just have nothing to do (for all finite elements, or
3766 // systems which accidentally don't have any dofs on the boundary).
3767 if (n_dofs_per_face == 0)
3768 return;
3769
3770 const FullMatrix<double> transformation =
3771 compute_transformation(fe, matrix, first_vector_components);
3772
3773 if (!face_2->has_children())
3774 {
3775 // Performance hack: We do not need to compute an inverse if the
3776 // matrix is the identity matrix.
3777 if (first_vector_components.empty() && matrix.m() == 0)
3778 {
3780 face_1,
3781 transformation,
3782 affine_constraints,
3783 component_mask,
3784 combined_orientation,
3785 periodicity_factor);
3786 }
3787 else
3788 {
3789 FullMatrix<double> inverse(transformation.m());
3790 inverse.invert(transformation);
3791
3793 face_1,
3794 inverse,
3795 affine_constraints,
3796 component_mask,
3797 combined_orientation,
3798 periodicity_factor);
3799 }
3800 }
3801 else
3802 {
3803 Assert(!face_1->has_children(), ExcInternalError());
3804
3805 // Important note:
3806 // In 3d we have to take care of the fact that face_rotation gives
3807 // the relative rotation of face_1 to face_2, i.e. we have to invert
3808 // the rotation when constraining face_2 to face_1. Therefore
3809 // face_flip has to be toggled if face_rotation is true: In case of
3810 // inverted orientation, nothing has to be done.
3811
3812 const auto face_reference_cell = face_1->reference_cell();
3814 face_1,
3815 face_2,
3816 transformation,
3817 affine_constraints,
3818 component_mask,
3819 face_reference_cell.get_inverse_combined_orientation(
3820 combined_orientation),
3821 periodicity_factor);
3822 }
3823 }
3824 }
3825
3826
3827
3828 template <int dim, int spacedim, typename number>
3829 void
3831 const std::vector<GridTools::PeriodicFacePair<
3832 typename DoFHandler<dim, spacedim>::cell_iterator>> &periodic_faces,
3833 AffineConstraints<number> &constraints,
3834 const ComponentMask &component_mask,
3835 const std::vector<unsigned int> &first_vector_components,
3836 const number periodicity_factor)
3837 {
3838 // Loop over all periodic faces...
3839 for (auto &pair : periodic_faces)
3840 {
3841 using FaceIterator = typename DoFHandler<dim, spacedim>::face_iterator;
3842 const FaceIterator face_1 = pair.cell[0]->face(pair.face_idx[0]);
3843 const FaceIterator face_2 = pair.cell[1]->face(pair.face_idx[1]);
3844
3845 Assert(face_1->at_boundary() && face_2->at_boundary(),
3847
3848 Assert(face_1 != face_2, ExcInternalError());
3849
3850 // ... and apply the low level make_periodicity_constraints function to
3851 // every matching pair:
3853 face_2,
3854 constraints,
3855 component_mask,
3856 pair.orientation,
3857 pair.matrix,
3858 first_vector_components,
3859 periodicity_factor);
3860 }
3861 }
3862
3863
3864 // High level interface variants:
3865
3866
3867 template <int dim, int spacedim, typename number>
3868 void
3870 const types::boundary_id b_id1,
3871 const types::boundary_id b_id2,
3872 const unsigned int direction,
3873 ::AffineConstraints<number> &constraints,
3874 const ComponentMask &component_mask,
3875 const number periodicity_factor)
3876 {
3877 AssertIndexRange(direction, spacedim);
3878
3879 Assert(b_id1 != b_id2,
3880 ExcMessage("The boundary indicators b_id1 and b_id2 must be "
3881 "different to denote different boundaries."));
3882
3883 std::vector<GridTools::PeriodicFacePair<
3885 matched_faces;
3886
3887 // Collect matching periodic cells on the coarsest level:
3889 dof_handler, b_id1, b_id2, direction, matched_faces);
3890
3891 make_periodicity_constraints<dim, spacedim>(matched_faces,
3892 constraints,
3893 component_mask,
3894 std::vector<unsigned int>(),
3895 periodicity_factor);
3896 }
3897
3898
3899
3900 template <int dim, int spacedim, typename number>
3901 void
3903 const types::boundary_id b_id,
3904 const unsigned int direction,
3905 AffineConstraints<number> &constraints,
3906 const ComponentMask &component_mask,
3907 const number periodicity_factor)
3908 {
3909 AssertIndexRange(direction, spacedim);
3910
3911 Assert(dim == spacedim, ExcNotImplemented());
3912
3913 std::vector<GridTools::PeriodicFacePair<
3915 matched_faces;
3916
3917 // Collect matching periodic cells on the coarsest level:
3919 b_id,
3920 direction,
3921 matched_faces);
3922
3923 make_periodicity_constraints<dim, spacedim>(matched_faces,
3924 constraints,
3925 component_mask,
3926 std::vector<unsigned int>(),
3927 periodicity_factor);
3928 }
3929
3930
3931
3932 namespace internal
3933 {
3934 namespace Assembler
3935 {
3936 // We don't actually need a scratch object, so use an empty class for it.
3937 struct Scratch
3938 {};
3939
3940
3941 template <int dim, int spacedim>
3943 {
3944 unsigned int dofs_per_cell;
3945 std::vector<types::global_dof_index> parameter_dof_indices;
3946#ifdef DEAL_II_WITH_MPI
3947 std::vector<::LinearAlgebra::distributed::Vector<double>>
3949#else
3950 std::vector<::Vector<double>> global_parameter_representation;
3951#endif
3952 };
3953 } // namespace Assembler
3954
3955 namespace
3956 {
3962 template <int dim, int spacedim>
3963 void
3964 compute_intergrid_weights_3(
3967 const unsigned int coarse_component,
3968 const FiniteElement<dim, spacedim> &coarse_fe,
3969 const InterGridMap<DoFHandler<dim, spacedim>> &coarse_to_fine_grid_map,
3970 const std::vector<::Vector<double>> &parameter_dofs)
3971 {
3972 // for each cell on the parameter grid: find out which degrees of
3973 // freedom on the fine grid correspond in which way to the degrees of
3974 // freedom on the parameter grid
3975 //
3976 // since for continuous FEs some dofs exist on more than one cell, we
3977 // have to track which ones were already visited. the problem is that if
3978 // we visit a dof first on one cell and compute its weight with respect
3979 // to some global dofs to be non-zero, and later visit the dof again on
3980 // another cell and (since we are on another cell) recompute the weights
3981 // with respect to the same dofs as above to be zero now, we have to
3982 // preserve them. we therefore overwrite all weights if they are nonzero
3983 // and do not enforce zero weights since that might be only due to the
3984 // fact that we are on another cell.
3985 //
3986 // example:
3987 // coarse grid
3988 // | | |
3989 // *-----*-----*
3990 // | cell|cell |
3991 // | 1 | 2 |
3992 // | | |
3993 // 0-----1-----*
3994 //
3995 // fine grid
3996 // | | | | |
3997 // *--*--*--*--*
3998 // | | | | |
3999 // *--*--*--*--*
4000 // | | | | |
4001 // *--x--y--*--*
4002 //
4003 // when on cell 1, we compute the weights of dof 'x' to be 1/2 from
4004 // parameter dofs 0 and 1, respectively. however, when later we are on
4005 // cell 2, we again compute the prolongation of shape function 1
4006 // restricted to cell 2 to the global grid and find that the weight of
4007 // global dof 'x' now is zero. however, we should not overwrite the old
4008 // value.
4009 //
4010 // we therefore always only set nonzero values. why adding up is not
4011 // useful: dof 'y' would get weight 1 from parameter dof 1 on both cells
4012 // 1 and 2, but the correct weight is nevertheless only 1.
4013
4014 // vector to hold the representation of a single degree of freedom on
4015 // the coarse grid (for the selected fe) on the fine grid
4016
4017 copy_data.dofs_per_cell = coarse_fe.n_dofs_per_cell();
4018 copy_data.parameter_dof_indices.resize(copy_data.dofs_per_cell);
4019
4020 // get the global indices of the parameter dofs on this parameter grid
4021 // cell
4022 cell->get_dof_indices(copy_data.parameter_dof_indices);
4023
4024 // loop over all dofs on this cell and check whether they are
4025 // interesting for us
4026 for (unsigned int local_dof = 0; local_dof < copy_data.dofs_per_cell;
4027 ++local_dof)
4028 if (coarse_fe.system_to_component_index(local_dof).first ==
4029 coarse_component)
4030 {
4031 // the how-many-th parameter is this on this cell?
4032 const unsigned int local_parameter_dof =
4033 coarse_fe.system_to_component_index(local_dof).second;
4034
4035 copy_data.global_parameter_representation[local_parameter_dof] =
4036 0.;
4037
4038 // distribute the representation of @p{local_parameter_dof} on the
4039 // parameter grid cell
4040 // @p{cell} to the global data space
4041 coarse_to_fine_grid_map[cell]->set_dof_values_by_interpolation(
4042 parameter_dofs[local_parameter_dof],
4043 copy_data.global_parameter_representation[local_parameter_dof]);
4044 }
4045 }
4046
4047
4048
4054 template <int dim, int spacedim>
4055 void
4056 copy_intergrid_weights_3(
4057 const Assembler::CopyData<dim, spacedim> &copy_data,
4058 const unsigned int coarse_component,
4059 const FiniteElement<dim, spacedim> &coarse_fe,
4060 const std::vector<types::global_dof_index> &weight_mapping,
4061 const bool is_called_in_parallel,
4062 std::vector<std::map<types::global_dof_index, float>> &weights)
4063 {
4064 unsigned int pos = 0;
4065 for (unsigned int local_dof = 0; local_dof < copy_data.dofs_per_cell;
4066 ++local_dof)
4067 if (coarse_fe.system_to_component_index(local_dof).first ==
4068 coarse_component)
4069 {
4070 // now that we've got the global representation of each parameter
4071 // dof, we've only got to clobber the non-zero entries in that
4072 // vector and store the result
4073 //
4074 // what we have learned: if entry @p{i} of the global vector holds
4075 // the value @p{v[i]}, then this is the weight with which the
4076 // present dof contributes to @p{i}. there may be several such
4077 // @p{i}s and their weights' sum should be one. Then, @p{v[i]}
4078 // should be equal to @p{\sum_j w_{ij} p[j]} with @p{p[j]} be the
4079 // values of the degrees of freedom on the coarse grid. we can
4080 // thus compute constraints which link the degrees of freedom
4081 // @p{v[i]} on the fine grid to those on the coarse grid,
4082 // @p{p[j]}. Now to use these as real constraints, rather than as
4083 // additional equations, we have to identify representants among
4084 // the @p{i} for each @p{j}. this will be done by simply taking
4085 // the first @p{i} for which @p{w_{ij}==1}.
4086 //
4087 // guard modification of the weights array by a Mutex. since it
4088 // should happen rather rarely that there are several threads
4089 // operating on different intergrid weights, have only one mutex
4090 // for all of them
4091 for (types::global_dof_index i = 0;
4092 i < copy_data.global_parameter_representation[pos].size();
4093 ++i)
4094 // set this weight if it belongs to a parameter dof.
4095 if (weight_mapping[i] != numbers::invalid_dof_index)
4096 {
4097 // only overwrite old value if not by zero
4098 if (copy_data.global_parameter_representation[pos](i) != 0)
4099 {
4101 wi = copy_data.parameter_dof_indices[local_dof],
4102 wj = weight_mapping[i];
4103 weights[wi][wj] =
4104 copy_data.global_parameter_representation[pos](i);
4105 }
4106 }
4107 else if (!is_called_in_parallel)
4108 {
4109 // Note that when this function operates with distributed
4110 // fine grid, this assertion is switched off since the
4111 // condition does not necessarily hold
4112 Assert(copy_data.global_parameter_representation[pos](i) ==
4113 0,
4115 }
4116
4117 ++pos;
4118 }
4119 }
4120
4121
4122
4128 template <int dim, int spacedim>
4129 void
4130 compute_intergrid_weights_2(
4131 const DoFHandler<dim, spacedim> &coarse_grid,
4132 const unsigned int coarse_component,
4133 const InterGridMap<DoFHandler<dim, spacedim>> &coarse_to_fine_grid_map,
4134 const std::vector<::Vector<double>> &parameter_dofs,
4135 const std::vector<types::global_dof_index> &weight_mapping,
4136 std::vector<std::map<types::global_dof_index, float>> &weights)
4137 {
4138 Assembler::CopyData<dim, spacedim> copy_data;
4139
4140 unsigned int n_interesting_dofs = 0;
4141 for (unsigned int local_dof = 0;
4142 local_dof < coarse_grid.get_fe().n_dofs_per_cell();
4143 ++local_dof)
4144 if (coarse_grid.get_fe().system_to_component_index(local_dof).first ==
4145 coarse_component)
4146 ++n_interesting_dofs;
4147
4148 copy_data.global_parameter_representation.resize(n_interesting_dofs);
4149
4150 bool is_called_in_parallel = false;
4151 for (std::size_t i = 0;
4152 i < copy_data.global_parameter_representation.size();
4153 ++i)
4154 {
4155#ifdef DEAL_II_WITH_MPI
4156 MPI_Comm communicator = MPI_COMM_SELF;
4157 try
4158 {
4159 const typename ::parallel::TriangulationBase<dim,
4160 spacedim>
4161 &tria = dynamic_cast<const typename ::parallel::
4162 TriangulationBase<dim, spacedim> &>(
4163 coarse_to_fine_grid_map.get_destination_grid()
4164 .get_triangulation());
4165 communicator = tria.get_mpi_communicator();
4166 is_called_in_parallel = true;
4167 }
4168 catch (std::bad_cast &)
4169 {
4170 // Nothing bad happened: the user used serial Triangulation
4171 }
4172
4173
4174 const IndexSet locally_relevant_dofs =
4176 coarse_to_fine_grid_map.get_destination_grid());
4177
4178 copy_data.global_parameter_representation[i].reinit(
4179 coarse_to_fine_grid_map.get_destination_grid()
4180 .locally_owned_dofs(),
4181 locally_relevant_dofs,
4182 communicator);
4183#else
4184 const types::global_dof_index n_fine_dofs = weight_mapping.size();
4185 copy_data.global_parameter_representation[i].reinit(n_fine_dofs);
4186#endif
4187 }
4188
4189 auto worker =
4190 [coarse_component,
4191 &coarse_grid,
4192 &coarse_to_fine_grid_map,
4193 &parameter_dofs](
4195 &cell,
4196 const Assembler::Scratch &,
4197 Assembler::CopyData<dim, spacedim> &copy_data) {
4198 compute_intergrid_weights_3<dim, spacedim>(cell,
4199 copy_data,
4200 coarse_component,
4201 coarse_grid.get_fe(),
4202 coarse_to_fine_grid_map,
4203 parameter_dofs);
4204 };
4205
4206 auto copier =
4207 [coarse_component,
4208 &coarse_grid,
4209 &weight_mapping,
4210 is_called_in_parallel,
4211 &weights](const Assembler::CopyData<dim, spacedim> &copy_data) {
4212 copy_intergrid_weights_3<dim, spacedim>(copy_data,
4213 coarse_component,
4214 coarse_grid.get_fe(),
4215 weight_mapping,
4216 is_called_in_parallel,
4217 weights);
4218 };
4219
4220 WorkStream::run(coarse_grid.begin_active(),
4221 coarse_grid.end(),
4222 worker,
4223 copier,
4224 Assembler::Scratch(),
4225 copy_data);
4226
4227#ifdef DEAL_II_WITH_MPI
4228 for (std::size_t i = 0;
4229 i < copy_data.global_parameter_representation.size();
4230 ++i)
4231 copy_data.global_parameter_representation[i].update_ghost_values();
4232#endif
4233 }
4234
4235
4236
4242 template <int dim, int spacedim>
4243 unsigned int
4244 compute_intergrid_weights_1(
4245 const DoFHandler<dim, spacedim> &coarse_grid,
4246 const unsigned int coarse_component,
4247 const DoFHandler<dim, spacedim> &fine_grid,
4248 const unsigned int fine_component,
4249 const InterGridMap<DoFHandler<dim, spacedim>> &coarse_to_fine_grid_map,
4250 std::vector<std::map<types::global_dof_index, float>> &weights,
4251 std::vector<types::global_dof_index> &weight_mapping)
4252 {
4253 // aliases to the finite elements used by the dof handlers:
4254 const FiniteElement<dim, spacedim> &coarse_fe = coarse_grid.get_fe(),
4255 &fine_fe = fine_grid.get_fe();
4256
4257 // global numbers of dofs
4258 const types::global_dof_index n_coarse_dofs = coarse_grid.n_dofs(),
4259 n_fine_dofs = fine_grid.n_dofs();
4260
4261 // local numbers of dofs
4262 const unsigned int fine_dofs_per_cell = fine_fe.n_dofs_per_cell();
4263
4264 // alias the number of dofs per cell belonging to the coarse_component
4265 // which is to be the restriction of the fine grid:
4266 const unsigned int coarse_dofs_per_cell_component =
4267 coarse_fe
4268 .base_element(
4269 coarse_fe.component_to_base_index(coarse_component).first)
4270 .n_dofs_per_cell();
4271
4272
4273 // Try to find out whether the grids stem from the same coarse grid.
4274 // This is a rather crude test, but better than nothing
4275 Assert(coarse_grid.get_triangulation().n_cells(0) ==
4276 fine_grid.get_triangulation().n_cells(0),
4278
4279 // check whether the map correlates the right objects
4280 Assert(&coarse_to_fine_grid_map.get_source_grid() == &coarse_grid,
4282 Assert(&coarse_to_fine_grid_map.get_destination_grid() == &fine_grid,
4284
4285
4286 // check whether component numbers are valid
4287 AssertIndexRange(coarse_component, coarse_fe.n_components());
4288 AssertIndexRange(fine_component, fine_fe.n_components());
4289
4290 // check whether respective finite elements are equal
4291 Assert(coarse_fe.base_element(
4292 coarse_fe.component_to_base_index(coarse_component).first) ==
4293 fine_fe.base_element(
4294 fine_fe.component_to_base_index(fine_component).first),
4296
4297 if constexpr (running_in_debug_mode())
4298 {
4299 // if in debug mode, check whether the coarse grid is indeed coarser
4300 // everywhere than the fine grid
4301 for (const auto &cell : coarse_grid.active_cell_iterators())
4302 Assert(cell->level() <= coarse_to_fine_grid_map[cell]->level(),
4304 }
4305
4306 /*
4307 * From here on: the term `parameter' refers to the selected component
4308 * on the coarse grid and its analogon on the fine grid. The naming of
4309 * variables containing this term is due to the fact that
4310 * `selected_component' is longer, but also due to the fact that the
4311 * code of this function was initially written for a program where the
4312 * component which we wanted to match between grids was actually the
4313 * `parameter' variable.
4314 *
4315 * Likewise, the terms `parameter grid' and `state grid' refer to the
4316 * coarse and fine grids, respectively.
4317 *
4318 * Changing the names of variables would in principle be a good idea,
4319 * but would not make things simpler and would be another source of
4320 * errors. If anyone feels like doing so: patches would be welcome!
4321 */
4322
4323
4324
4325 // set up vectors of cell-local data; each vector represents one degree
4326 // of freedom of the coarse-grid variable in the fine-grid element
4327 std::vector<::Vector<double>> parameter_dofs(
4328 coarse_dofs_per_cell_component,
4329 ::Vector<double>(fine_dofs_per_cell));
4330 // for each coarse dof: find its position within the fine element and
4331 // set this value to one in the respective vector (all other values are
4332 // zero by construction)
4333 for (unsigned int local_coarse_dof = 0;
4334 local_coarse_dof < coarse_dofs_per_cell_component;
4335 ++local_coarse_dof)
4336 for (unsigned int fine_dof = 0; fine_dof < fine_fe.n_dofs_per_cell();
4337 ++fine_dof)
4338 if (fine_fe.system_to_component_index(fine_dof) ==
4339 std::make_pair(fine_component, local_coarse_dof))
4340 {
4341 parameter_dofs[local_coarse_dof](fine_dof) = 1.;
4342 break;
4343 }
4344
4345
4346 // find out how many DoFs there are on the grids belonging to the
4347 // components we want to match
4348 unsigned int n_parameters_on_fine_grid = 0;
4349 {
4350 // have a flag for each dof on the fine grid and set it to true if
4351 // this is an interesting dof. finally count how many true's there
4352 std::vector<bool> dof_is_interesting(fine_grid.n_dofs(), false);
4353 std::vector<types::global_dof_index> local_dof_indices(
4354 fine_fe.n_dofs_per_cell());
4355
4356 for (const auto &cell : fine_grid.active_cell_iterators() |
4357 IteratorFilters::LocallyOwnedCell())
4358 {
4359 cell->get_dof_indices(local_dof_indices);
4360 for (unsigned int i = 0; i < fine_fe.n_dofs_per_cell(); ++i)
4361 if (fine_fe.system_to_component_index(i).first ==
4362 fine_component)
4363 dof_is_interesting[local_dof_indices[i]] = true;
4364 }
4365
4366 n_parameters_on_fine_grid = std::count(dof_is_interesting.begin(),
4367 dof_is_interesting.end(),
4368 true);
4369 }
4370
4371
4372 // set up the weights mapping
4373 weights.clear();
4374 weights.resize(n_coarse_dofs);
4375
4376 weight_mapping.clear();
4377 weight_mapping.resize(n_fine_dofs, numbers::invalid_dof_index);
4378
4379 {
4380 std::vector<types::global_dof_index> local_dof_indices(
4381 fine_fe.n_dofs_per_cell());
4382 unsigned int next_free_index = 0;
4383 for (const auto &cell : fine_grid.active_cell_iterators() |
4384 IteratorFilters::LocallyOwnedCell())
4385 {
4386 cell->get_dof_indices(local_dof_indices);
4387 for (unsigned int i = 0; i < fine_fe.n_dofs_per_cell(); ++i)
4388 // if this DoF is a parameter dof and has not yet been
4389 // numbered, then do so
4390 if ((fine_fe.system_to_component_index(i).first ==
4391 fine_component) &&
4392 (weight_mapping[local_dof_indices[i]] ==
4394 {
4395 weight_mapping[local_dof_indices[i]] = next_free_index;
4396 ++next_free_index;
4397 }
4398 }
4399
4400 Assert(next_free_index == n_parameters_on_fine_grid,
4402 }
4403
4404
4405 // for each cell on the parameter grid: find out which degrees of
4406 // freedom on the fine grid correspond in which way to the degrees of
4407 // freedom on the parameter grid
4408 //
4409 // do this in a separate function to allow for multithreading there. see
4410 // this function also if you want to read more information on the
4411 // algorithm used.
4412 compute_intergrid_weights_2(coarse_grid,
4413 coarse_component,
4414 coarse_to_fine_grid_map,
4415 parameter_dofs,
4416 weight_mapping,
4417 weights);
4418
4419
4420 // ok, now we have all weights for each dof on the fine grid. if in
4421 // debug mode lets see if everything went smooth, i.e. each dof has sum
4422 // of weights one
4423 //
4424 // in other words this means that if the sum of all shape functions on
4425 // the parameter grid is one (which is always the case), then the
4426 // representation on the state grid should be as well (division of
4427 // unity)
4428 //
4429 // if the parameter grid has more than one component, then the
4430 // respective dofs of the other components have sum of weights zero, of
4431 // course. we do not explicitly ask which component a dof belongs to,
4432 // but this at least tests some errors
4433 if constexpr (running_in_debug_mode())
4434 {
4435 for (unsigned int col = 0; col < n_parameters_on_fine_grid; ++col)
4436 {
4437 double sum = 0;
4438 for (types::global_dof_index row = 0; row < n_coarse_dofs;
4439 ++row)
4440 if (weights[row].find(col) != weights[row].end())
4441 sum += weights[row][col];
4442 Assert((std::fabs(sum - 1) < 1.e-12) ||
4443 ((coarse_fe.n_components() > 1) && (sum == 0)),
4445 }
4446 }
4447
4448
4449 return n_parameters_on_fine_grid;
4450 }
4451
4452
4453 } // namespace
4454 } // namespace internal
4455
4456
4457
4458 template <int dim, int spacedim>
4459 void
4461 const DoFHandler<dim, spacedim> &coarse_grid,
4462 const unsigned int coarse_component,
4463 const DoFHandler<dim, spacedim> &fine_grid,
4464 const unsigned int fine_component,
4465 const InterGridMap<DoFHandler<dim, spacedim>> &coarse_to_fine_grid_map,
4466 AffineConstraints<double> &constraints)
4467 {
4468 Assert(coarse_grid.get_fe_collection().size() == 1 &&
4469 fine_grid.get_fe_collection().size() == 1,
4470 ExcMessage("This function is not yet implemented for DoFHandlers "
4471 "using hp-capabilities."));
4472 // store the weights with which a dof on the parameter grid contributes to a
4473 // dof on the fine grid. see the long doc below for more info
4474 //
4475 // allocate as many rows as there are parameter dofs on the coarse grid and
4476 // as many columns as there are parameter dofs on the fine grid.
4477 //
4478 // weight_mapping is used to map the global (fine grid) parameter dof
4479 // indices to the columns
4480 //
4481 // in the original implementation, the weights array was actually of
4482 // FullMatrix<double> type. this wasted huge amounts of memory, but was
4483 // fast. nonetheless, since the memory consumption was quadratic in the
4484 // number of degrees of freedom, this was not very practical, so we now use
4485 // a vector of rows of the matrix, and in each row a vector of pairs
4486 // (colnum,value). this seems like the best tradeoff between memory and
4487 // speed, as it is now linear in memory and still fast enough.
4488 //
4489 // to save some memory and since the weights are usually (negative) powers
4490 // of 2, we choose the value type of the matrix to be @p{float} rather than
4491 // @p{double}.
4492 std::vector<std::map<types::global_dof_index, float>> weights;
4493
4494 // this is this mapping. there is one entry for each dof on the fine grid;
4495 // if it is a parameter dof, then its value is the column in weights for
4496 // that parameter dof, if it is any other dof, then its value is -1,
4497 // indicating an error
4498 std::vector<types::global_dof_index> weight_mapping;
4499
4500 const unsigned int n_parameters_on_fine_grid =
4501 internal::compute_intergrid_weights_1(coarse_grid,
4502 coarse_component,
4503 fine_grid,
4504 fine_component,
4505 coarse_to_fine_grid_map,
4506 weights,
4507 weight_mapping);
4508 (void)n_parameters_on_fine_grid;
4509
4510 // global numbers of dofs
4511 const types::global_dof_index n_coarse_dofs = coarse_grid.n_dofs(),
4512 n_fine_dofs = fine_grid.n_dofs();
4513
4514
4515 // get an array in which we store which dof on the coarse grid is a
4516 // parameter and which is not
4517 IndexSet coarse_dof_is_parameter;
4518 {
4519 std::vector<bool> mask(coarse_grid.get_fe(0).n_components(), false);
4520 mask[coarse_component] = true;
4521
4522 coarse_dof_is_parameter =
4523 extract_dofs<dim, spacedim>(coarse_grid, ComponentMask(mask));
4524 }
4525
4526 // now we know that the weights in each row constitute a constraint. enter
4527 // this into the constraints object
4528 //
4529 // first task: for each parameter dof on the parameter grid, find a
4530 // representant on the fine, global grid. this is possible since we use
4531 // conforming finite element. we take this representant to be the first
4532 // element in this row with weight identical to one. the representant will
4533 // become an unconstrained degree of freedom, while all others will be
4534 // constrained to this dof (and possibly others)
4535 std::vector<types::global_dof_index> representants(
4536 n_coarse_dofs, numbers::invalid_dof_index);
4537 for (types::global_dof_index parameter_dof = 0;
4538 parameter_dof < n_coarse_dofs;
4539 ++parameter_dof)
4540 if (coarse_dof_is_parameter.is_element(parameter_dof))
4541 {
4542 // if this is the line of a parameter dof on the coarse grid, then it
4543 // should have at least one dependent node on the fine grid
4544 Assert(weights[parameter_dof].size() > 0, ExcInternalError());
4545
4546 // find the column where the representant is mentioned
4547 std::map<types::global_dof_index, float>::const_iterator i =
4548 weights[parameter_dof].begin();
4549 for (; i != weights[parameter_dof].end(); ++i)
4550 if (i->second == 1)
4551 break;
4552 Assert(i != weights[parameter_dof].end(), ExcInternalError());
4553 const types::global_dof_index column = i->first;
4554
4555 // now we know in which column of weights the representant is, but we
4556 // don't know its global index. get it using the inverse operation of
4557 // the weight_mapping
4558 types::global_dof_index global_dof = 0;
4559 for (; global_dof < weight_mapping.size(); ++global_dof)
4560 if (weight_mapping[global_dof] ==
4561 static_cast<types::global_dof_index>(column))
4562 break;
4563 Assert(global_dof < weight_mapping.size(), ExcInternalError());
4564
4565 // now enter the representants global index into our list
4566 representants[parameter_dof] = global_dof;
4567 }
4568 else
4569 {
4570 // consistency check: if this is no parameter dof on the coarse grid,
4571 // then the respective row must be empty!
4572 Assert(weights[parameter_dof].empty(), ExcInternalError());
4573 }
4574
4575
4576
4577 // note for people that want to optimize this function: the largest part of
4578 // the computing time is spent in the following, rather innocent block of
4579 // code. basically, it must be the AffineConstraints::add_entry call which
4580 // takes the bulk of the time, but it is not known to the author how to make
4581 // it faster...
4582 std::vector<std::pair<types::global_dof_index, double>> constraint_line;
4583 for (types::global_dof_index global_dof = 0; global_dof < n_fine_dofs;
4584 ++global_dof)
4585 if (weight_mapping[global_dof] != numbers::invalid_dof_index)
4586 // this global dof is a parameter dof, so it may carry a constraint note
4587 // that for each global dof, the sum of weights shall be one, so we can
4588 // find out whether this dof is constrained in the following way: if the
4589 // only weight in this row is a one, and the representant for the
4590 // parameter dof of the line in which this one is is the present dof,
4591 // then we consider this dof to be unconstrained. otherwise, all other
4592 // dofs are constrained
4593 {
4594 const types::global_dof_index col = weight_mapping[global_dof];
4595 Assert(col < n_parameters_on_fine_grid, ExcInternalError());
4596
4597 types::global_dof_index first_used_row = 0;
4598
4599 {
4600 Assert(weights.size() > 0, ExcInternalError());
4601 std::map<types::global_dof_index, float>::const_iterator col_entry =
4602 weights[0].end();
4603 for (; first_used_row < n_coarse_dofs; ++first_used_row)
4604 {
4605 col_entry = weights[first_used_row].find(col);
4606 if (col_entry != weights[first_used_row].end())
4607 break;
4608 }
4609
4610 Assert(col_entry != weights[first_used_row].end(),
4612
4613 if ((col_entry->second == 1) &&
4614 (representants[first_used_row] == global_dof))
4615 // dof unconstrained or constrained to itself (in case this cell
4616 // is mapped to itself, rather than to children of itself)
4617 continue;
4618 }
4619
4620
4621 // otherwise enter all constraints
4622 constraint_line.clear();
4623 for (types::global_dof_index row = first_used_row;
4624 row < n_coarse_dofs;
4625 ++row)
4626 {
4627 const std::map<types::global_dof_index, float>::const_iterator j =
4628 weights[row].find(col);
4629 if ((j != weights[row].end()) && (j->second != 0))
4630 constraint_line.emplace_back(representants[row], j->second);
4631 }
4632
4633 constraints.add_constraint(global_dof, constraint_line, 0.);
4634 }
4635 }
4636
4637
4638
4639 template <int dim, int spacedim>
4640 void
4642 const DoFHandler<dim, spacedim> &coarse_grid,
4643 const unsigned int coarse_component,
4644 const DoFHandler<dim, spacedim> &fine_grid,
4645 const unsigned int fine_component,
4646 const InterGridMap<DoFHandler<dim, spacedim>> &coarse_to_fine_grid_map,
4647 std::vector<std::map<types::global_dof_index, float>>
4648 &transfer_representation)
4649 {
4650 Assert(coarse_grid.get_fe_collection().size() == 1 &&
4651 fine_grid.get_fe_collection().size() == 1,
4652 ExcMessage("This function is not yet implemented for DoFHandlers "
4653 "using hp-capabilities."));
4654 // store the weights with which a dof on the parameter grid contributes to a
4655 // dof on the fine grid. see the long doc below for more info
4656 //
4657 // allocate as many rows as there are parameter dofs on the coarse grid and
4658 // as many columns as there are parameter dofs on the fine grid.
4659 //
4660 // weight_mapping is used to map the global (fine grid) parameter dof
4661 // indices to the columns
4662 //
4663 // in the original implementation, the weights array was actually of
4664 // FullMatrix<double> type. this wasted huge amounts of memory, but was
4665 // fast. nonetheless, since the memory consumption was quadratic in the
4666 // number of degrees of freedom, this was not very practical, so we now use
4667 // a vector of rows of the matrix, and in each row a vector of pairs
4668 // (colnum,value). this seems like the best tradeoff between memory and
4669 // speed, as it is now linear in memory and still fast enough.
4670 //
4671 // to save some memory and since the weights are usually (negative) powers
4672 // of 2, we choose the value type of the matrix to be @p{float} rather than
4673 // @p{double}.
4674 std::vector<std::map<types::global_dof_index, float>> weights;
4675
4676 // this is this mapping. there is one entry for each dof on the fine grid;
4677 // if it is a parameter dof, then its value is the column in weights for
4678 // that parameter dof, if it is any other dof, then its value is -1,
4679 // indicating an error
4680 std::vector<types::global_dof_index> weight_mapping;
4681
4682 internal::compute_intergrid_weights_1(coarse_grid,
4683 coarse_component,
4684 fine_grid,
4685 fine_component,
4686 coarse_to_fine_grid_map,
4687 weights,
4688 weight_mapping);
4689
4690 // now compute the requested representation
4691 const types::global_dof_index n_global_parm_dofs =
4692 std::count_if(weight_mapping.begin(),
4693 weight_mapping.end(),
4694 [](const types::global_dof_index dof) {
4695 return dof != numbers::invalid_dof_index;
4696 });
4697
4698 // first construct the inverse mapping of weight_mapping
4699 std::vector<types::global_dof_index> inverse_weight_mapping(
4700 n_global_parm_dofs, numbers::invalid_dof_index);
4701 for (types::global_dof_index i = 0; i < weight_mapping.size(); ++i)
4702 {
4703 const types::global_dof_index parameter_dof = weight_mapping[i];
4704 // if this global dof is a parameter
4705 if (parameter_dof != numbers::invalid_dof_index)
4706 {
4707 Assert(parameter_dof < n_global_parm_dofs, ExcInternalError());
4708 Assert((inverse_weight_mapping[parameter_dof] ==
4711
4712 inverse_weight_mapping[parameter_dof] = i;
4713 }
4714 }
4715
4716 // next copy over weights array and replace respective numbers
4717 const types::global_dof_index n_rows = weight_mapping.size();
4718
4719 transfer_representation.clear();
4720 transfer_representation.resize(n_rows);
4721
4722 const types::global_dof_index n_coarse_dofs = coarse_grid.n_dofs();
4723 for (types::global_dof_index i = 0; i < n_coarse_dofs; ++i)
4724 {
4725 std::map<types::global_dof_index, float>::const_iterator j =
4726 weights[i].begin();
4727 for (; j != weights[i].end(); ++j)
4728 {
4729 const types::global_dof_index p = inverse_weight_mapping[j->first];
4730 Assert(p < n_rows, ExcInternalError());
4731
4732 transfer_representation[p][i] = j->second;
4733 }
4734 }
4735 }
4736
4737
4738
4739 template <int dim, int spacedim, typename number>
4740 void
4742 const DoFHandler<dim, spacedim> &dof,
4743 const types::boundary_id boundary_id,
4744 AffineConstraints<number> &zero_boundary_constraints,
4745 const ComponentMask &component_mask)
4746 {
4747 Assert(component_mask.represents_n_components(dof.get_fe(0).n_components()),
4748 ExcMessage("The number of components in the mask has to be either "
4749 "zero or equal to the number of components in the finite "
4750 "element."));
4751
4752 const unsigned int n_components = dof.get_fe_collection().n_components();
4753
4754 Assert(component_mask.n_selected_components(n_components) > 0,
4756
4757 // a field to store the indices on the face
4758 std::vector<types::global_dof_index> face_dofs;
4759 face_dofs.reserve(dof.get_fe_collection().max_dofs_per_face());
4760 // a field to store the indices on the cell
4761 std::vector<types::global_dof_index> cell_dofs;
4762 cell_dofs.reserve(dof.get_fe_collection().max_dofs_per_cell());
4763
4764 // In looping over faces, we will encounter some DoFs multiple
4765 // times (namely, the ones on vertices and (in 3d) edges shared
4766 // between multiple boundary faces. Keep track of which DoFs we
4767 // have already encountered, so that we do not have to consider
4768 // them a second time.
4769 std::set<types::global_dof_index> dofs_already_treated;
4770
4771 for (const auto &cell : dof.active_cell_iterators())
4772 if (!cell->is_artificial() && cell->at_boundary())
4773 {
4774 const FiniteElement<dim, spacedim> &fe = cell->get_fe();
4775
4776 // get global indices of dofs on the cell
4777 cell_dofs.resize(fe.n_dofs_per_cell());
4778 cell->get_dof_indices(cell_dofs);
4779
4780 for (const auto face_no : cell->face_indices())
4781 {
4782 const typename DoFHandler<dim, spacedim>::face_iterator face =
4783 cell->face(face_no);
4784
4785 // if face is on the boundary and satisfies the correct boundary
4786 // id property
4787 if (face->at_boundary() &&
4788 ((boundary_id == numbers::invalid_boundary_id) ||
4789 (face->boundary_id() == boundary_id)))
4790 {
4791 // get indices and physical location on this face
4792 face_dofs.resize(fe.n_dofs_per_face(face_no));
4793 face->get_dof_indices(face_dofs, cell->active_fe_index());
4794
4795 // enter those dofs into the list that match the component
4796 // signature.
4797 for (const types::global_dof_index face_dof : face_dofs)
4798 if (dofs_already_treated.find(face_dof) ==
4799 dofs_already_treated.end())
4800 {
4801 // Find out if a dof has a contribution in this
4802 // component, and if so, add it to the list
4803 const std::vector<types::global_dof_index>::iterator
4804 it_index_on_cell = std::find(cell_dofs.begin(),
4805 cell_dofs.end(),
4806 face_dof);
4807 Assert(it_index_on_cell != cell_dofs.end(),
4809 const unsigned int index_on_cell =
4810 std::distance(cell_dofs.begin(), it_index_on_cell);
4811 const ComponentMask &nonzero_component_array =
4812 cell->get_fe().get_nonzero_components(index_on_cell);
4813
4814 bool nonzero = false;
4815 for (unsigned int c = 0; c < n_components; ++c)
4816 if (nonzero_component_array[c] && component_mask[c])
4817 {
4818 nonzero = true;
4819 break;
4820 }
4821
4822 if (nonzero)
4823 {
4824 // Check that either (i) the DoF is not
4825 // yet constrained, or (ii) if it is, its
4826 // inhomogeneity is zero:
4827 if (zero_boundary_constraints.is_constrained(
4828 face_dof) == false)
4829 zero_boundary_constraints.constrain_dof_to_zero(
4830 face_dof);
4831 else
4832 Assert(zero_boundary_constraints
4833 .is_inhomogeneously_constrained(
4834 face_dof) == false,
4836 }
4837
4838 // We already dealt with this DoF. Make sure we
4839 // don't touch it again.
4840 dofs_already_treated.insert(face_dof);
4841 }
4842 }
4843 }
4844 }
4845 }
4846
4847
4848
4849 template <int dim, int spacedim, typename number>
4850 void
4852 const DoFHandler<dim, spacedim> &dof,
4853 AffineConstraints<number> &zero_boundary_constraints,
4854 const ComponentMask &component_mask)
4855 {
4858 zero_boundary_constraints,
4859 component_mask);
4860 }
4861
4862
4863} // end of namespace DoFTools
4864
4865
4866
4867// explicit instantiations
4868
4869#include "dofs/dof_tools_constraints.inst"
4870
4871
4872
void add_line(const size_type line_n)
void add_constraint(const size_type constrained_dof, const ArrayView< const std::pair< size_type, number > > &dependencies, const number inhomogeneity=0)
void add_entry(const size_type constrained_dof_index, const size_type column, const number weight)
const IndexSet & get_local_lines() const
void set_inhomogeneity(const size_type constrained_dof_index, const number value)
bool is_constrained(const size_type line_n) const
const std::vector< std::pair< size_type, number > > * get_constraint_entries(const size_type line_n) const
void constrain_dof_to_zero(const size_type constrained_dof)
bool represents_n_components(const unsigned int n) const
unsigned int n_selected_components(const unsigned int overall_number_of_components=numbers::invalid_unsigned_int) const
const hp::FECollection< dim, spacedim > & get_fe_collection() const
bool has_active_dofs() const
const FiniteElement< dim, spacedim > & get_fe(const types::fe_index index=0) const
const Triangulation< dim, spacedim > & get_triangulation() const
bool has_hp_capabilities() const
types::global_dof_index n_dofs() const
unsigned int n_dofs_per_vertex() const
const unsigned int degree
Definition fe_data.h:452
unsigned int n_dofs_per_cell() const
unsigned int n_dofs_per_line() const
unsigned int n_dofs_per_face(unsigned int face_no=0, unsigned int child=0) const
unsigned int n_components() const
unsigned int n_unique_faces() const
unsigned int n_dofs_per_quad(unsigned int face_no=0) const
virtual std::string get_name() const =0
virtual const FiniteElement< dim, spacedim > & base_element(const unsigned int index) const
std::pair< unsigned int, unsigned int > component_to_base_index(const unsigned int component) const
const std::vector< Point< dim - 1 > > & get_unit_face_support_points(const unsigned int face_no=0) const
virtual void get_subface_interpolation_matrix(const FiniteElement< dim, spacedim > &source, const unsigned int subface, FullMatrix< double > &matrix, const unsigned int face_no=0) const
std::pair< unsigned int, unsigned int > system_to_component_index(const unsigned int index) const
const FullMatrix< double > & constraints(const ::internal::SubfaceCase< dim > &subface_case=::internal::SubfaceCase< dim >::case_isotropic) const
virtual unsigned int face_to_cell_index(const unsigned int face_dof_index, const unsigned int face, const types::geometric_orientation combined_orientation=numbers::default_geometric_orientation) const
virtual void get_face_interpolation_matrix(const FiniteElement< dim, spacedim > &source, FullMatrix< double > &matrix, const unsigned int face_no=0) const
std::pair< unsigned int, unsigned int > face_system_to_component_index(const unsigned int index, const unsigned int face_no=0) const
void mmult(FullMatrix< number2 > &C, const FullMatrix< number2 > &B, const bool adding=false) const
size_type n() const
void invert(const FullMatrix< number2 > &M)
size_type m() const
size_type size() const
Definition index_set.h:1787
bool is_element(const size_type index) const
Definition index_set.h:1905
bool get_anisotropic_refinement_flag() const
unsigned int n_cells() const
unsigned int size() const
Definition collection.h:316
unsigned int find_dominating_fe_extended(const std::set< unsigned int > &fes, const unsigned int codim=0) const
bool hp_constraints_are_implemented() const
unsigned int max_dofs_per_face() const
unsigned int n_components() const
unsigned int max_dofs_per_cell() const
#define DEAL_II_NAMESPACE_OPEN
Definition config.h:40
constexpr bool running_in_debug_mode()
Definition config.h:78
#define DEAL_II_NAMESPACE_CLOSE
Definition config.h:41
#define DEAL_II_ASSERT_UNREACHABLE()
unsigned int level
Definition grid_out.cc:4635
unsigned int cell_index
IteratorRange< active_cell_iterator > active_cell_iterators() const
static ::ExceptionBase & ExcInvalidIterator()
static ::ExceptionBase & ExcGridNotCoarser()
static ::ExceptionBase & ExcNotImplemented()
#define Assert(cond, exc)
#define AssertDimension(dim1, dim2)
#define AssertIndexRange(index, range)
static ::ExceptionBase & ExcInternalError()
static ::ExceptionBase & ExcFiniteElementsDontMatch()
static ::ExceptionBase & ExcNoComponentSelected()
static ::ExceptionBase & ExcDimensionMismatch(std::size_t arg1, std::size_t arg2)
static ::ExceptionBase & ExcGridsDontMatch()
static ::ExceptionBase & ExcMessage(std::string arg1)
#define AssertThrow(cond, exc)
typename ActiveSelector::cell_iterator cell_iterator
typename ActiveSelector::line_iterator line_iterator
typename ActiveSelector::face_iterator face_iterator
typename ActiveSelector::active_cell_iterator active_cell_iterator
void compute_intergrid_transfer_representation(const DoFHandler< dim, spacedim > &coarse_grid, const unsigned int coarse_component, const DoFHandler< dim, spacedim > &fine_grid, const unsigned int fine_component, const InterGridMap< DoFHandler< dim, spacedim > > &coarse_to_fine_grid_map, std::vector< std::map< types::global_dof_index, float > > &transfer_representation)
void make_hanging_node_constraints(const DoFHandler< dim, spacedim > &dof_handler, AffineConstraints< number > &constraints)
void compute_intergrid_constraints(const DoFHandler< dim, spacedim > &coarse_grid, const unsigned int coarse_component, const DoFHandler< dim, spacedim > &fine_grid, const unsigned int fine_component, const InterGridMap< DoFHandler< dim, spacedim > > &coarse_to_fine_grid_map, AffineConstraints< double > &constraints)
void make_zero_boundary_constraints(const DoFHandler< dim, spacedim > &dof, const types::boundary_id boundary_id, AffineConstraints< number > &zero_boundary_constraints, const ComponentMask &component_mask={})
std::size_t size
Definition mpi.cc:745
Expression fabs(const Expression &x)
void make_hp_hanging_node_constraints(const DoFHandler< 1, spacedim > &, AffineConstraints< number > &)
void set_periodicity_constraints(const FaceIterator &face_1, const std_cxx20::type_identity_t< FaceIterator > &face_2, const FullMatrix< double > &transformation, AffineConstraints< number > &affine_constraints, const ComponentMask &component_mask, const types::geometric_orientation combined_orientation, const number periodicity_factor, const unsigned int level=numbers::invalid_unsigned_int)
void make_hanging_node_constraints_nedelec(const ::DoFHandler< 1, spacedim > &, AffineConstraints< number > &, std::integral_constant< int, 1 >)
void make_oldstyle_hanging_node_constraints(const DoFHandler< 1, spacedim > &, AffineConstraints< number > &, std::integral_constant< int, 1 >)
IndexSet extract_locally_relevant_dofs(const DoFHandler< dim, spacedim > &dof_handler)
void make_periodicity_constraints(const FaceIterator &face_1, const std_cxx20::type_identity_t< FaceIterator > &face_2, AffineConstraints< number > &constraints, const ComponentMask &component_mask={}, const types::geometric_orientation combined_orientation=numbers::default_geometric_orientation, const FullMatrix< double > &matrix=FullMatrix< double >(), const std::vector< unsigned int > &first_vector_components=std::vector< unsigned int >(), const number periodicity_factor=1.)
void collect_periodic_faces(const MeshType &mesh, const types::boundary_id b_id1, const types::boundary_id b_id2, const unsigned int direction, std::vector< PeriodicFacePair< typename MeshType::cell_iterator > > &matched_pairs, const Tensor< 1, MeshType::space_dimension > &offset=::Tensor< 1, MeshType::space_dimension >(), const FullMatrix< double > &matrix=FullMatrix< double >())
@ matrix
Contents is actually a matrix.
constexpr char N
SymmetricTensor< 2, dim, Number > e(const Tensor< 2, dim, Number > &F)
Tensor< 2, dim, Number > l(const Tensor< 2, dim, Number > &F, const Tensor< 2, dim, Number > &dF_dt)
T sum(const T &t, const MPI_Comm mpi_communicator)
void run(const std::vector< std::vector< Iterator > > &colored_iterators, Worker worker, Copier copier, const ScratchData &sample_scratch_data, const CopyData &sample_copy_data, const unsigned int queue_length=2 *MultithreadInfo::n_threads(), const unsigned int chunk_size=8)
std::tuple< bool, bool, bool > split_face_orientation(const types::geometric_orientation combined_face_orientation)
constexpr types::global_dof_index invalid_dof_index
Definition types.h:263
constexpr unsigned int invalid_unsigned_int
Definition types.h:232
constexpr types::boundary_id invalid_boundary_id
Definition types.h:303
constexpr types::fe_index invalid_fe_index
Definition types.h:254
typename type_identity< T >::type type_identity_t
Definition type_traits.h:95
STL namespace.
::VectorizedArray< Number, width > abs(const ::VectorizedArray< Number, width > &)
unsigned short int fe_index
Definition types.h:68
unsigned char geometric_orientation
Definition types.h:40
std::vector<::LinearAlgebra::distributed::Vector< double > > global_parameter_representation
std::vector< types::global_dof_index > parameter_dof_indices
static unsigned int n_children(const RefinementCase< dim > &refinement_case)