deal.II version GIT relicensing-6834-g5b78e6bcdf 2026-10-01 11:20:01+00:00
\(\newcommand{\dealvcentcolon}{\mathrel{\mathop{:}}}\) \(\newcommand{\dealcoloneq}{\dealvcentcolon\mathrel{\mkern-1.2mu}=}\) \(\newcommand{\jump}[1]{\left[\!\left[ #1 \right]\!\right]}\) \(\newcommand{\average}[1]{\left\{\!\left\{ #1 \right\}\!\right\}}\)
Loading...
Searching...
No Matches
fe_raviart_thomas_nodal.cc
Go to the documentation of this file.
1// -----------------------------------------------------------------------------
2//
3// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception OR LGPL-2.1-or-later
4// Copyright (C) 2005 - 2026 by the deal.II authors
5//
6// This file is part of the deal.II library.
7//
8// Detailed license information governing the source code and contributions
9// can be found in LICENSE.md and CONTRIBUTING.md at the top level directory.
10//
11// -----------------------------------------------------------------------------
12
13
18
20
21#include <deal.II/fe/fe.h>
24#include <deal.II/fe/fe_tools.h>
25#include <deal.II/fe/mapping.h>
26
27#include <memory>
28#include <sstream>
29
30
32
33
34// ---------------- polynomial class for FE_RaviartThomasNodal ---------------
35
36namespace
37{
38 // Return a vector of "dofs per object" where the components of the returned
39 // vector refer to:
40 // 0 = vertex
41 // 1 = edge
42 // 2 = face (which is a cell in 2d)
43 // 3 = cell
44 std::vector<unsigned int>
45 get_rt_dpo_vector(const unsigned int dim, const unsigned int degree)
46 {
47 std::vector<unsigned int> dpo(dim + 1);
48 dpo[0] = 0;
49 dpo[1] = 0;
50 unsigned int dofs_per_face = 1;
51 for (unsigned int d = 1; d < dim; ++d)
52 dofs_per_face *= (degree + 1);
53
54 dpo[dim - 1] = dofs_per_face;
55 dpo[dim] = dim * degree * dofs_per_face;
56
57 return dpo;
58 }
59} // namespace
60
61
62
63// --------------------- actual implementation of element --------------------
64
65template <int dim, int spacedim>
67 const unsigned int degree)
68 : FE_PolyTensor<dim, spacedim>(
70 degree + 1,
71 degree,
72 FE_RaviartThomas<dim>::get_lexicographic_numbering(degree)),
73 FiniteElementData<dim>(get_rt_dpo_vector(dim, degree),
74 spacedim,
75 degree + 1,
76 FiniteElementData<dim>::Hdiv),
77 std::vector<bool>(1, false),
78 std::vector<ComponentMask>(
79 PolynomialsVectorAnisotropic<dim>::n_polynomials(degree + 1, degree),
80 ComponentMask(std::vector<bool>(spacedim, true))))
81{
82 Assert(dim >= 2, ExcImpossibleInDim(dim));
83
85
86 const std::vector<unsigned int> numbering =
88
89 // First, initialize the generalized support points and quadrature weights,
90 // since they are required for interpolation.
95 this->n_dofs_per_cell());
96
97 const unsigned int face_no = 0;
98 if (dim > 1)
99 this->generalized_face_support_points[face_no] =
100 degree == 0 ? QGauss<dim - 1>(1).get_points() :
101 QGaussLobatto<dim - 1>(degree + 1).get_points();
102
103 // The hanging-node interface constraints are computed via
104 // FETools::compute_face_embedding_matrices(), which is currently only
105 // implemented for the case dim == spacedim. For embedded surfaces
106 // (dim < spacedim) we skip this step for now; the resulting element can be
107 // used on (possibly globally refined) surface meshes without hanging nodes.
108 if constexpr (dim == spacedim)
109 {
112 for (unsigned int i = 0; i < GeometryInfo<dim>::max_children_per_face;
113 ++i)
114 face_embeddings[i].reinit(this->n_dofs_per_face(face_no),
115 this->n_dofs_per_face(face_no));
116 FETools::compute_face_embedding_matrices<dim, double>(*this,
117 face_embeddings,
118 0,
119 0);
120 this->interface_constraints.reinit(
122 this->n_dofs_per_face(face_no),
123 this->n_dofs_per_face(face_no));
124 unsigned int target_row = 0;
125 for (unsigned int d = 0; d < GeometryInfo<dim>::max_children_per_face;
126 ++d)
127 for (unsigned int i = 0; i < face_embeddings[d].m(); ++i)
128 {
129 for (unsigned int j = 0; j < face_embeddings[d].n(); ++j)
130 this->interface_constraints(target_row, j) =
131 face_embeddings[d](i, j);
132 ++target_row;
133 }
134 }
135
136 // We need to initialize the dof permutation table and the one for the sign
137 // change.
139}
140
141
142
143template <int dim, int spacedim>
144std::string
146{
147 // note that the FETools::get_fe_by_name function depends on the particular
148 // format of the string this function returns, so they have to be kept in
149 // synch
150
151 // note that this->degree is the maximal polynomial degree and is thus one
152 // higher than the argument given to the constructor
153 return "FE_RaviartThomasNodal<" + Utilities::dim_string(dim, spacedim) +
154 ">(" + std::to_string(this->degree - 1) + ")";
155}
156
157
158template <int dim, int spacedim>
159std::unique_ptr<FiniteElement<dim, spacedim>>
161{
162 return std::make_unique<FE_RaviartThomasNodal<dim, spacedim>>(*this);
163}
164
165
166//---------------------------------------------------------------------------
167// Auxiliary and internal functions
168//---------------------------------------------------------------------------
169
170
171
172template <int dim, int spacedim>
173void
176{
177 // for 1d and 2d, do nothing
178 if (dim < 3)
179 return;
180
181 const unsigned int n = this->degree;
182 const unsigned int face_no = 0;
183 Assert(n * n == this->n_dofs_per_quad(face_no), ExcInternalError());
184 for (unsigned int local = 0; local < this->n_dofs_per_quad(face_no); ++local)
185 // face support points are in lexicographic ordering with x running
186 // fastest. invert that (y running fastest)
187 {
188 unsigned int i = local % n, j = local / n;
189
190 // face_orientation=false, face_rotation=false, face_flip=false
191 this->adjust_quad_dof_index_for_face_orientation_table[face_no](
192 local, internal::combined_face_orientation(false, false, false)) =
193 j + i * n - local;
194 // face_orientation=false, face_rotation=true, face_flip=false
195 this->adjust_quad_dof_index_for_face_orientation_table[face_no](
196 local, internal::combined_face_orientation(false, true, false)) =
197 i + (n - 1 - j) * n - local;
198 // face_orientation=false, face_rotation=false, face_flip=true
199 this->adjust_quad_dof_index_for_face_orientation_table[face_no](
200 local, internal::combined_face_orientation(false, false, true)) =
201 (n - 1 - j) + (n - 1 - i) * n - local;
202 // face_orientation=false, face_rotation=true, face_flip=true
203 this->adjust_quad_dof_index_for_face_orientation_table[face_no](
204 local, internal::combined_face_orientation(false, true, true)) =
205 (n - 1 - i) + j * n - local;
206 // face_orientation=true, face_rotation=false, face_flip=false
207 this->adjust_quad_dof_index_for_face_orientation_table[face_no](
208 local, internal::combined_face_orientation(true, false, false)) = 0;
209 // face_orientation=true, face_rotation=true, face_flip=false
210 this->adjust_quad_dof_index_for_face_orientation_table[face_no](
211 local, internal::combined_face_orientation(true, true, false)) =
212 j + (n - 1 - i) * n - local;
213 // face_orientation=true, face_rotation=false, face_flip=true
214 this->adjust_quad_dof_index_for_face_orientation_table[face_no](
215 local, internal::combined_face_orientation(true, false, true)) =
216 (n - 1 - i) + (n - 1 - j) * n - local;
217 // face_orientation=true, face_rotation=true, face_flip=true
218 this->adjust_quad_dof_index_for_face_orientation_table[face_no](
219 local, internal::combined_face_orientation(true, true, true)) =
220 (n - 1 - j) + i * n - local;
221
222 // for face_orientation == false, we need to switch the sign
223 for (const bool rotation : {false, true})
224 for (const bool flip : {false, true})
225 this->adjust_quad_dof_sign_for_face_orientation_table[face_no](
226 local, internal::combined_face_orientation(false, rotation, flip)) =
227 1;
228 }
229}
230
231
232
233template <int dim, int spacedim>
234bool
236 const unsigned int shape_index,
237 const unsigned int face_index) const
238{
239 AssertIndexRange(shape_index, this->n_dofs_per_cell());
241
242 // The first degrees of freedom are on the faces and each face has degree
243 // degrees.
244 const unsigned int support_face = shape_index / this->n_dofs_per_face();
245
246 // The only thing we know for sure is that shape functions with support on
247 // one face are zero on the opposite face.
248 if (support_face < GeometryInfo<dim>::faces_per_cell)
249 return (face_index != GeometryInfo<dim>::opposite_face[support_face]);
250
251 // In all other cases, return true, which is safe
252 return true;
253}
254
255
256
257template <int dim, int spacedim>
258void
261 const std::vector<Vector<double>> &support_point_values,
262 std::vector<double> &nodal_values) const
263{
264 if constexpr (dim != spacedim)
265 {
266 // The computation below assumes that support_point_values are given
267 // in the dim-dimensional reference tangent space, which would require
268 // applying the inverse surface Piola transform first. This is not
269 // implemented for the codimension-one case.
270 (void)support_point_values;
271 (void)nodal_values;
273 }
274
275 Assert(support_point_values.size() == this->generalized_support_points.size(),
276 ExcDimensionMismatch(support_point_values.size(),
277 this->generalized_support_points.size()));
278 Assert(nodal_values.size() == this->n_dofs_per_cell(),
279 ExcDimensionMismatch(nodal_values.size(), this->n_dofs_per_cell()));
280 Assert(support_point_values[0].size() == this->n_components(),
281 ExcDimensionMismatch(support_point_values[0].size(),
282 this->n_components()));
283
284 // First do interpolation on faces. There, the component evaluated depends
285 // on the face direction and orientation.
286 unsigned int fbase = 0;
287 unsigned int f = 0;
288 for (; f < GeometryInfo<dim>::faces_per_cell;
289 ++f, fbase += this->n_dofs_per_face(f))
290 {
291 for (unsigned int i = 0; i < this->n_dofs_per_face(f); ++i)
292 {
293 nodal_values[fbase + i] = support_point_values[fbase + i](
295 }
296 }
297
298 // The remaining points form dim chunks, one for each component
299 const unsigned int istep = (this->n_dofs_per_cell() - fbase) / dim;
300 Assert((this->n_dofs_per_cell() - fbase) % dim == 0, ExcInternalError());
301
302 f = 0;
303 while (fbase < this->n_dofs_per_cell())
304 {
305 for (unsigned int i = 0; i < istep; ++i)
306 {
307 nodal_values[fbase + i] = support_point_values[fbase + i](f);
308 }
309 fbase += istep;
310 ++f;
311 }
312 Assert(fbase == this->n_dofs_per_cell(), ExcInternalError());
313}
314
315
316
317// TODO: There are tests that check that the following few functions don't
318// produce assertion failures, but none that actually check whether they do the
319// right thing. one example for such a test would be to project a function onto
320// an hp-space and make sure that the convergence order is correct with regard
321// to the lowest used polynomial degree
322
323template <int dim, int spacedim>
324bool
329
330
331template <int dim, int spacedim>
332std::vector<std::pair<unsigned int, unsigned int>>
334 const FiniteElement<dim, spacedim> &fe_other) const
335{
336 // we can presently only compute these identities if both FEs are
337 // FE_RaviartThomasNodals or the other is FE_Nothing. In either case, no
338 // dofs are assigned on the vertex, so we shouldn't be getting here at all.
339 if (dynamic_cast<const FE_RaviartThomasNodal<dim, spacedim> *>(&fe_other) !=
340 nullptr)
341 return std::vector<std::pair<unsigned int, unsigned int>>();
342 else if (dynamic_cast<const FE_Nothing<dim, spacedim> *>(&fe_other) !=
343 nullptr)
344 return std::vector<std::pair<unsigned int, unsigned int>>();
345 else
346 {
348 return std::vector<std::pair<unsigned int, unsigned int>>();
349 }
350}
351
352
353
354template <int dim, int spacedim>
355std::vector<std::pair<unsigned int, unsigned int>>
357 const FiniteElement<dim, spacedim> &fe_other) const
358{
359 // we can presently only compute these identities if both FEs are
360 // FE_RaviartThomasNodals or if the other one is FE_Nothing
361 if (const FE_RaviartThomasNodal<dim, spacedim> *fe_q_other =
362 dynamic_cast<const FE_RaviartThomasNodal<dim, spacedim> *>(&fe_other))
363 {
364 // dofs are located on faces; these are only lines in 2d
365 if (dim != 2)
366 return std::vector<std::pair<unsigned int, unsigned int>>();
367
368 // dofs are located along lines, so two dofs are identical only if in
369 // the following two cases (remember that the face support points are
370 // Gauss points):
371 // 1. this->degree = fe_q_other->degree,
372 // in the case, all the dofs on the line are identical
373 // 2. this->degree-1 and fe_q_other->degree-1
374 // are both even, i.e. this->dof_per_line and fe_q_other->dof_per_line
375 // are both odd, there exists only one point (the middle one) such
376 // that dofs are identical on this point
377 //
378 // to understand this, note that this->degree is the *maximal*
379 // polynomial degree, and is thus one higher than the argument given to
380 // the constructor
381 const unsigned int p = this->degree - 1;
382 const unsigned int q = fe_q_other->degree - 1;
383
384 std::vector<std::pair<unsigned int, unsigned int>> identities;
385
386 if (p == q)
387 for (unsigned int i = 0; i < p + 1; ++i)
388 identities.emplace_back(i, i);
389
390 else if (p % 2 == 0 && q % 2 == 0)
391 identities.emplace_back(p / 2, q / 2);
392
393 return identities;
394 }
395 else if (dynamic_cast<const FE_Nothing<dim, spacedim> *>(&fe_other) !=
396 nullptr)
397 {
398 // the FE_Nothing has no degrees of freedom, so there are no
399 // equivalencies to be recorded
400 return std::vector<std::pair<unsigned int, unsigned int>>();
401 }
402 else
403 {
405 return std::vector<std::pair<unsigned int, unsigned int>>();
406 }
407}
408
409
410template <int dim, int spacedim>
411std::vector<std::pair<unsigned int, unsigned int>>
413 const FiniteElement<dim, spacedim> &fe_other,
414 const unsigned int face_no) const
415{
416 // we can presently only compute these identities if both FEs are
417 // FE_RaviartThomasNodals or if the other one is FE_Nothing
418 if (const FE_RaviartThomasNodal<dim, spacedim> *fe_q_other =
419 dynamic_cast<const FE_RaviartThomasNodal<dim, spacedim> *>(&fe_other))
420 {
421 // dofs are located on faces; these are only quads in 3d
422 if (dim != 3)
423 return std::vector<std::pair<unsigned int, unsigned int>>();
424
425 // this works exactly like the line case above
426 const unsigned int p = this->n_dofs_per_quad(face_no);
427
428 AssertDimension(fe_q_other->n_unique_faces(), 1);
429 const unsigned int q = fe_q_other->n_dofs_per_quad(0);
430
431 std::vector<std::pair<unsigned int, unsigned int>> identities;
432
433 if (p == q)
434 for (unsigned int i = 0; i < p; ++i)
435 identities.emplace_back(i, i);
436
437 else if (p % 2 != 0 && q % 2 != 0)
438 identities.emplace_back(p / 2, q / 2);
439
440 return identities;
441 }
442 else if (dynamic_cast<const FE_Nothing<dim, spacedim> *>(&fe_other) !=
443 nullptr)
444 {
445 // the FE_Nothing has no degrees of freedom, so there are no
446 // equivalencies to be recorded
447 return std::vector<std::pair<unsigned int, unsigned int>>();
448 }
449 else
450 {
452 return std::vector<std::pair<unsigned int, unsigned int>>();
453 }
454}
455
456
457template <int dim, int spacedim>
460 const FiniteElement<dim, spacedim> &fe_other,
461 const unsigned int codim) const
462{
463 Assert(codim <= dim, ExcImpossibleInDim(dim));
464 (void)codim;
465
466 // vertex/line/face/cell domination
467 // --------------------------------
468 if (const FE_RaviartThomasNodal<dim, spacedim> *fe_rt_nodal_other =
469 dynamic_cast<const FE_RaviartThomasNodal<dim, spacedim> *>(&fe_other))
470 {
471 if (this->degree < fe_rt_nodal_other->degree)
473 else if (this->degree == fe_rt_nodal_other->degree)
475 else
477 }
478 else if (const FE_Nothing<dim, spacedim> *fe_nothing =
479 dynamic_cast<const FE_Nothing<dim, spacedim> *>(&fe_other))
480 {
481 if (fe_nothing->is_dominating())
483 else
484 // the FE_Nothing has no degrees of freedom and it is typically used
485 // in a context where we don't require any continuity along the
486 // interface
488 }
489
492}
493
494
495
496template <>
497void
499 const FiniteElement<1, 1> & /*x_source_fe*/,
500 FullMatrix<double> & /*interpolation_matrix*/,
501 const unsigned int) const
502{
503 Assert(false, ExcImpossibleInDim(1));
504}
505
506
507template <>
508void
510 const FiniteElement<1, 1> & /*x_source_fe*/,
511 const unsigned int /*subface*/,
512 FullMatrix<double> & /*interpolation_matrix*/,
513 const unsigned int) const
514{
515 Assert(false, ExcImpossibleInDim(1));
516}
517
518
519
520template <int dim, int spacedim>
521void
523 const FiniteElement<dim, spacedim> &x_source_fe,
524 FullMatrix<double> &interpolation_matrix,
525 const unsigned int face_no) const
526{
527 // this is only implemented, if the
528 // source FE is also a
529 // RaviartThomasNodal element
531 (x_source_fe.get_name().find("FE_RaviartThomasNodal<") == 0) ||
532 (dynamic_cast<const FE_RaviartThomasNodal<dim, spacedim> *>(
533 &x_source_fe) != nullptr),
535
536 Assert(interpolation_matrix.n() == this->n_dofs_per_face(face_no),
537 ExcDimensionMismatch(interpolation_matrix.n(),
538 this->n_dofs_per_face(face_no)));
539 Assert(interpolation_matrix.m() == x_source_fe.n_dofs_per_face(face_no),
540 ExcDimensionMismatch(interpolation_matrix.m(),
541 x_source_fe.n_dofs_per_face(face_no)));
542
543 // ok, source is a RaviartThomasNodal element, so we will be able to do the
544 // work
545 const FE_RaviartThomasNodal<dim, spacedim> &source_fe =
546 dynamic_cast<const FE_RaviartThomasNodal<dim, spacedim> &>(x_source_fe);
547
548 // Make sure that the element for which the DoFs should be constrained is
549 // the one with the higher polynomial degree. Actually the procedure will
550 // work also if this assertion is not satisfied. But the matrices produced
551 // in that case might lead to problems in the hp-procedures, which use this
552 // method.
553 Assert(
554 this->n_dofs_per_face(face_no) <= source_fe.n_dofs_per_face(face_no),
556
557 // generate a quadrature with the generalized support points. This is later
558 // based as a basis for the QProjector, which returns the support points on
559 // the face.
560 Quadrature<dim - 1> quad_face_support(
561 source_fe.generalized_face_support_points[face_no]);
562
563 // Rule of thumb for FP accuracy, that can be expected for a given
564 // polynomial degree. This value is used to cut off values close to zero.
565 double eps = 2e-13 * this->degree * (dim - 1);
566
567 // compute the interpolation matrix by simply taking the value at the
568 // support points.
569 const Quadrature<dim> face_projection =
570 QProjector<dim>::project_to_face(this->reference_cell(),
571 quad_face_support,
572 0,
574
575 for (unsigned int i = 0; i < source_fe.n_dofs_per_face(face_no); ++i)
576 {
577 const Point<dim> &p = face_projection.point(i);
578
579 for (unsigned int j = 0; j < this->n_dofs_per_face(face_no); ++j)
580 {
581 double matrix_entry =
582 this->shape_value_component(this->face_to_cell_index(j, 0), p, 0);
583
584 // Correct the interpolated value. I.e. if it is close to 1 or 0,
585 // make it exactly 1 or 0. Unfortunately, this is required to avoid
586 // problems with higher order elements.
587 if (std::fabs(matrix_entry - 1.0) < eps)
588 matrix_entry = 1.0;
589 if (std::fabs(matrix_entry) < eps)
590 matrix_entry = 0.0;
591
592 interpolation_matrix(i, j) = matrix_entry;
593 }
594 }
595
596 if constexpr (running_in_debug_mode())
597 {
598 // make sure that the row sum of each of the matrices is 1 at this
599 // point. this must be so since the shape functions sum up to 1
600 for (unsigned int j = 0; j < source_fe.n_dofs_per_face(face_no); ++j)
601 {
602 double sum = 0.;
603
604 for (unsigned int i = 0; i < this->n_dofs_per_face(face_no); ++i)
605 sum += interpolation_matrix(j, i);
606
607 Assert(std::fabs(sum - 1) < 2e-13 * this->degree * (dim - 1),
609 }
610 }
611}
612
613
614template <int dim, int spacedim>
615void
617 const FiniteElement<dim, spacedim> &x_source_fe,
618 const unsigned int subface,
619 FullMatrix<double> &interpolation_matrix,
620 const unsigned int face_no) const
621{
622 // this is only implemented, if the source FE is also a RaviartThomasNodal
623 // element
625 (x_source_fe.get_name().find("FE_RaviartThomasNodal<") == 0) ||
626 (dynamic_cast<const FE_RaviartThomasNodal<dim, spacedim> *>(
627 &x_source_fe) != nullptr),
629
630 Assert(interpolation_matrix.n() == this->n_dofs_per_face(face_no),
631 ExcDimensionMismatch(interpolation_matrix.n(),
632 this->n_dofs_per_face(face_no)));
633 Assert(interpolation_matrix.m() == x_source_fe.n_dofs_per_face(face_no),
634 ExcDimensionMismatch(interpolation_matrix.m(),
635 x_source_fe.n_dofs_per_face(face_no)));
636
637 // ok, source is a RaviartThomasNodal element, so we will be able to do the
638 // work
639 const FE_RaviartThomasNodal<dim, spacedim> &source_fe =
640 dynamic_cast<const FE_RaviartThomasNodal<dim, spacedim> &>(x_source_fe);
641
642 // Make sure that the element for which the DoFs should be constrained is
643 // the one with the higher polynomial degree. Actually the procedure will
644 // work also if this assertion is not satisfied. But the matrices produced
645 // in that case might lead to problems in the hp-procedures, which use this
646 // method.
647 Assert(
648 this->n_dofs_per_face(face_no) <= source_fe.n_dofs_per_face(face_no),
650
651 // generate a quadrature with the generalized support points. This is later
652 // based as a basis for the QProjector, which returns the support points on
653 // the face.
654 Quadrature<dim - 1> quad_face_support(
655 source_fe.generalized_face_support_points[face_no]);
656
657 // Rule of thumb for FP accuracy, that can be expected for a given
658 // polynomial degree. This value is used to cut off values close to zero.
659 double eps = 2e-13 * this->degree * (dim - 1);
660
661 // compute the interpolation matrix by simply taking the value at the
662 // support points.
663 const Quadrature<dim> subface_projection =
665 this->reference_cell(),
666 quad_face_support,
667 0,
668 subface,
671
672 for (unsigned int i = 0; i < source_fe.n_dofs_per_face(face_no); ++i)
673 {
674 const Point<dim> &p = subface_projection.point(i);
675
676 for (unsigned int j = 0; j < this->n_dofs_per_face(face_no); ++j)
677 {
678 double matrix_entry =
679 this->shape_value_component(this->face_to_cell_index(j, 0), p, 0);
680
681 // Correct the interpolated value. I.e. if it is close to 1 or 0,
682 // make it exactly 1 or 0. Unfortunately, this is required to avoid
683 // problems with higher order elements.
684 if (std::fabs(matrix_entry - 1.0) < eps)
685 matrix_entry = 1.0;
686 if (std::fabs(matrix_entry) < eps)
687 matrix_entry = 0.0;
688
689 interpolation_matrix(i, j) = matrix_entry;
690 }
691 }
692
693 if constexpr (running_in_debug_mode())
694 {
695 // make sure that the row sum of each of the matrices is 1 at this
696 // point. this must be so since the shape functions sum up to 1
697 for (unsigned int j = 0; j < source_fe.n_dofs_per_face(face_no); ++j)
698 {
699 double sum = 0.;
700
701 for (unsigned int i = 0; i < this->n_dofs_per_face(face_no); ++i)
702 sum += interpolation_matrix(j, i);
703
704 Assert(std::fabs(sum - 1) < 2e-13 * this->degree * (dim - 1),
706 }
707 }
708}
709
710
711
712template <int dim, int spacedim>
713const FullMatrix<double> &
715 const unsigned int child,
716 const RefinementCase<dim> &refinement_case) const
717{
718 AssertIndexRange(refinement_case,
722 "Prolongation matrices are only available for refined cells!"));
723 AssertIndexRange(child, this->reference_cell().n_children(refinement_case));
724
725 // initialization upon first request
726 if (this->prolongation[refinement_case - 1][child].n() == 0)
727 {
728 std::scoped_lock lock(prolongation_matrix_mutex);
729
730 // if matrix got updated while waiting for the lock
731 if (this->prolongation[refinement_case - 1][child].n() ==
732 this->n_dofs_per_cell())
733 return this->prolongation[refinement_case - 1][child];
734
735 // now do the work. need to get a non-const version of data in order to
736 // be able to modify them inside a const function
738 const_cast<FE_RaviartThomasNodal<dim, spacedim> &>(*this);
739 if (refinement_case == RefinementCase<dim>::isotropic_refinement)
740 {
741 std::vector<std::vector<FullMatrix<double>>> isotropic_matrices(
743 isotropic_matrices.back().resize(
744 this->reference_cell().n_children(
745 RefinementCase<dim>(refinement_case)),
746 FullMatrix<double>(this->n_dofs_per_cell(),
747 this->n_dofs_per_cell()));
748 FETools::compute_embedding_matrices(*this, isotropic_matrices, true);
749 this_nonconst.prolongation[refinement_case - 1] =
750 std::move(isotropic_matrices.back());
751 }
752 else
753 {
754 // must compute both restriction and prolongation matrices because
755 // we only check for their size and the reinit call initializes them
756 // all
759 this_nonconst.prolongation);
761 this_nonconst.restriction);
762 }
763 }
764
765 // finally return the matrix
766 return this->prolongation[refinement_case - 1][child];
767}
768
769
770
771template <int dim, int spacedim>
772const FullMatrix<double> &
774 const unsigned int child,
775 const RefinementCase<dim> &refinement_case) const
776{
777 AssertIndexRange(refinement_case,
781 "Restriction matrices are only available for refined cells!"));
782 AssertIndexRange(child, this->reference_cell().n_children(refinement_case));
783
784 // initialization upon first request
785 if (this->restriction[refinement_case - 1][child].n() == 0)
786 {
787 std::scoped_lock lock(restriction_matrix_mutex);
788
789 // if matrix got updated while waiting for the lock...
790 if (this->restriction[refinement_case - 1][child].n() ==
791 this->n_dofs_per_cell())
792 return this->restriction[refinement_case - 1][child];
793
794 // now do the work. need to get a non-const version of data in order to
795 // be able to modify them inside a const function
797 const_cast<FE_RaviartThomasNodal<dim, spacedim> &>(*this);
798 if (refinement_case == RefinementCase<dim>::isotropic_refinement)
799 {
800 std::vector<std::vector<FullMatrix<double>>> isotropic_matrices(
802 isotropic_matrices.back().resize(
803 this->reference_cell().n_children(
804 RefinementCase<dim>(refinement_case)),
805 FullMatrix<double>(this->n_dofs_per_cell(),
806 this->n_dofs_per_cell()));
807 FETools::compute_projection_matrices(*this, isotropic_matrices, true);
808 this_nonconst.restriction[refinement_case - 1] =
809 std::move(isotropic_matrices.back());
810 }
811 else
812 {
813 // must compute both restriction and prolongation matrices because
814 // we only check for their size and the reinit call initializes them
815 // all
818 this_nonconst.prolongation);
820 this_nonconst.restriction);
821 }
822 }
823
824 // finally return the matrix
825 return this->restriction[refinement_case - 1][child];
826}
827
828
829
830// explicit instantiations
831#include "fe/fe_raviart_thomas_nodal.inst"
832
833
std::vector< MappingKind > mapping_kind
virtual std::vector< std::pair< unsigned int, unsigned int > > hp_quad_dof_identities(const FiniteElement< dim, spacedim > &fe_other, const unsigned int face_no=0) const override
virtual bool has_support_on_face(const unsigned int shape_index, const unsigned int face_index) const override
virtual std::unique_ptr< FiniteElement< dim, spacedim > > clone() const override
virtual bool hp_constraints_are_implemented() const override
void initialize_quad_dof_index_permutation_and_sign_change()
virtual std::vector< std::pair< unsigned int, unsigned int > > hp_vertex_dof_identities(const FiniteElement< dim, spacedim > &fe_other) const override
virtual std::vector< std::pair< unsigned int, unsigned int > > hp_line_dof_identities(const FiniteElement< dim, spacedim > &fe_other) const override
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 override
virtual const FullMatrix< double > & get_prolongation_matrix(const unsigned int child, const RefinementCase< dim > &refinement_case=RefinementCase< dim >::isotropic_refinement) const override
virtual std::string get_name() const override
FE_RaviartThomasNodal(const unsigned int p)
virtual void convert_generalized_support_point_values_to_dof_values(const std::vector< Vector< double > > &support_point_values, std::vector< double > &nodal_values) const override
virtual void get_face_interpolation_matrix(const FiniteElement< dim, spacedim > &source, FullMatrix< double > &matrix, const unsigned int face_no=0) const override
virtual const FullMatrix< double > & get_restriction_matrix(const unsigned int child, const RefinementCase< dim > &refinement_case=RefinementCase< dim >::isotropic_refinement) const override
virtual FiniteElementDomination::Domination compare_for_domination(const FiniteElement< dim, spacedim > &fe_other, const unsigned int codim=0) const override final
static std::vector< unsigned int > get_lexicographic_numbering(const unsigned int degree)
const unsigned int degree
Definition fe_data.h:450
unsigned int n_dofs_per_face(unsigned int face_no=0, unsigned int child=0) const
virtual std::string get_name() const =0
std::vector< std::vector< FullMatrix< double > > > restriction
Definition fe.h:2547
void reinit_restriction_and_prolongation_matrices(const bool isotropic_restriction_only=false, const bool isotropic_prolongation_only=false)
std::vector< std::vector< Point< dim - 1 > > > generalized_face_support_points
Definition fe.h:2604
FullMatrix< double > interface_constraints
Definition fe.h:2573
std::vector< Point< dim > > generalized_support_points
Definition fe.h:2598
std::vector< std::vector< FullMatrix< double > > > prolongation
Definition fe.h:2561
size_type n() const
size_type m() const
Definition point.h:111
std::vector< Point< dim > > get_polynomial_support_points() const
static Quadrature< dim > project_to_face(const ReferenceCell< dim > &reference_cell, const SubQuadrature &quadrature, const unsigned int face_no, const types::geometric_orientation combined_orientation)
static Quadrature< dim > project_to_subface(const ReferenceCell< dim > &reference_cell, const SubQuadrature &quadrature, const unsigned int face_no, const unsigned int subface_no, const types::geometric_orientation combined_orientation, const RefinementCase< dim - 1 > &ref_case)
#define DEAL_II_NAMESPACE_OPEN
Definition config.h:38
constexpr bool running_in_debug_mode()
Definition config.h:76
#define DEAL_II_NAMESPACE_CLOSE
Definition config.h:39
#define DEAL_II_NOT_IMPLEMENTED()
#define Assert(cond, exc)
static ::ExceptionBase & ExcImpossibleInDim(int arg1)
#define AssertDimension(dim1, dim2)
#define AssertIndexRange(index, range)
static ::ExceptionBase & ExcInternalError()
static ::ExceptionBase & ExcDimensionMismatch(std::size_t arg1, std::size_t arg2)
static ::ExceptionBase & ExcMessage(std::string arg1)
#define AssertThrow(cond, exc)
@ mapping_raviart_thomas
Definition mapping.h:134
std::size_t size
Definition mpi.cc:733
void compute_embedding_matrices(const FiniteElement< dim, spacedim > &fe, std::vector< std::vector< FullMatrix< number > > > &matrices, const bool isotropic_only=false, const double threshold=1.e-12)
void compute_projection_matrices(const FiniteElement< dim, spacedim > &fe, std::vector< std::vector< FullMatrix< number > > > &matrices, const bool isotropic_only=false)
SymmetricTensor< 2, dim, Number > d(const Tensor< 2, dim, Number > &F, const Tensor< 2, dim, Number > &dF_dt)
std::string dim_string(const int dim, const int spacedim)
Definition utilities.cc:547
types::geometric_orientation combined_face_orientation(const bool face_orientation, const bool face_rotation, const bool face_flip)
constexpr types::geometric_orientation default_geometric_orientation
Definition types.h:342
STL namespace.