Reference documentation for deal.II version 9.4.0
\(\newcommand{\dealvcentcolon}{\mathrel{\mathop{:}}}\) \(\newcommand{\dealcoloneq}{\dealvcentcolon\mathrel{\mkern-1.2mu}=}\) \(\newcommand{\jump}[1]{\left[\!\left[ #1 \right]\!\right]}\) \(\newcommand{\average}[1]{\left\{\!\left\{ #1 \right\}\!\right\}}\)
fe_raviart_thomas_nodal.cc
Go to the documentation of this file.
1 // ---------------------------------------------------------------------
2 //
3 // Copyright (C) 2003 - 2022 by the deal.II authors
4 //
5 // This file is part of the deal.II library.
6 //
7 // The deal.II library is free software; you can use it, redistribute
8 // it, and/or modify it under the terms of the GNU Lesser General
9 // Public License as published by the Free Software Foundation; either
10 // version 2.1 of the License, or (at your option) any later version.
11 // The full text of the license can be found in the file LICENSE.md at
12 // the top level directory of deal.II.
13 //
14 // ---------------------------------------------------------------------
15 
16 
21 
23 
24 #include <deal.II/fe/fe.h>
25 #include <deal.II/fe/fe_nothing.h>
27 #include <deal.II/fe/fe_tools.h>
28 #include <deal.II/fe/mapping.h>
29 
30 #include <memory>
31 #include <sstream>
32 
33 
35 
36 
37 // ---------------- polynomial class for FE_RaviartThomasNodal ---------------
38 
39 namespace
40 {
41  template <int dim>
42  class PolynomialsRaviartThomasNodal : public TensorPolynomialsBase<dim>
43  {
44  public:
45  PolynomialsRaviartThomasNodal(const unsigned int degree);
46 
54  void
55  evaluate(const Point<dim> & unit_point,
56  std::vector<Tensor<1, dim>> &values,
57  std::vector<Tensor<2, dim>> &grads,
58  std::vector<Tensor<3, dim>> &grad_grads,
59  std::vector<Tensor<4, dim>> &third_derivatives,
60  std::vector<Tensor<5, dim>> &fourth_derivatives) const override;
61 
65  std::string
66  name() const override;
67 
73  static unsigned int
74  n_polynomials(const unsigned int degree);
75 
76  const std::vector<unsigned int> &
77  get_renumbering() const;
78 
82  virtual std::unique_ptr<TensorPolynomialsBase<dim>>
83  clone() const override;
84 
92  std::vector<Point<dim>>
93  get_polynomial_support_points() const;
94 
95  private:
99  const unsigned int degree;
100 
105  const AnisotropicPolynomials<dim> polynomial_space;
106 
110  std::vector<unsigned int> lexicographic_to_hierarchic;
111 
116  std::vector<unsigned int> hierarchic_to_lexicographic;
117 
121  std::array<std::vector<unsigned int>, dim> renumber_aniso;
122  };
123 
124 
125 
126  // Create nodal Raviart-Thomas polynomials as the tensor product of Lagrange
127  // polynomials on Gauss-Lobatto points of degree + 2 points in the
128  // continuous direction and degree + 1 points in the discontinuous
129  // directions (we could also choose Lagrange polynomials on Gauss points but
130  // those are slightly more expensive to handle in classes).
131  std::vector<std::vector<Polynomials::Polynomial<double>>>
132  create_rt_polynomials(const unsigned int dim, const unsigned int degree)
133  {
134  std::vector<std::vector<Polynomials::Polynomial<double>>> pols(dim);
136  QGaussLobatto<1>(degree + 2).get_points());
137  if (degree > 0)
138  for (unsigned int d = 1; d < dim; ++d)
140  QGaussLobatto<1>(degree + 1).get_points());
141  else
142  for (unsigned int d = 1; d < dim; ++d)
144  QMidpoint<1>().get_points());
145 
146  return pols;
147  }
148 
149 
150  template <int dim>
151  PolynomialsRaviartThomasNodal<dim>::PolynomialsRaviartThomasNodal(
152  const unsigned int degree)
153  : TensorPolynomialsBase<dim>(degree, n_polynomials(degree))
154  , degree(degree)
155  , polynomial_space(create_rt_polynomials(dim, degree))
156  {
157  // create renumbering of the unknowns from the lexicographic order to the
158  // actual order required by the finite element class with unknowns on
159  // faces placed first
160  const unsigned int n_pols = polynomial_space.n();
161  lexicographic_to_hierarchic =
162  internal::get_lexicographic_numbering_rt_nodal<dim>(degree + 1);
163 
164  hierarchic_to_lexicographic =
165  Utilities::invert_permutation(lexicographic_to_hierarchic);
166 
167  // since we only store an anisotropic polynomial for the first component,
168  // we set up a second numbering to switch out the actual coordinate
169  // direction
170  renumber_aniso[0].resize(n_pols);
171  for (unsigned int i = 0; i < n_pols; ++i)
172  renumber_aniso[0][i] = i;
173  if (dim == 2)
174  {
175  // switch x and y component (i and j loops)
176  renumber_aniso[1].resize(n_pols);
177  for (unsigned int j = 0; j < degree + 2; ++j)
178  for (unsigned int i = 0; i < degree + 1; ++i)
179  renumber_aniso[1][j * (degree + 1) + i] = j + i * (degree + 2);
180  }
181  if (dim == 3)
182  {
183  // switch x, y, and z component (i, j, k) -> (j, k, i)
184  renumber_aniso[1].resize(n_pols);
185  for (unsigned int k = 0; k < degree + 1; ++k)
186  for (unsigned int j = 0; j < degree + 2; ++j)
187  for (unsigned int i = 0; i < degree + 1; ++i)
188  renumber_aniso[1][(k * (degree + 2) + j) * (degree + 1) + i] =
189  j + k * (degree + 2) + i * (degree + 2) * (degree + 1);
190 
191  // switch x, y, and z component (i, j, k) -> (k, i, j)
192  renumber_aniso[2].resize(n_pols);
193  for (unsigned int k = 0; k < degree + 2; ++k)
194  for (unsigned int j = 0; j < degree + 1; ++j)
195  for (unsigned int i = 0; i < degree + 1; ++i)
196  renumber_aniso[2][(k * (degree + 1) + j) * (degree + 1) + i] =
197  k + i * (degree + 2) + j * (degree + 2) * (degree + 1);
198  }
199  }
200 
201 
202 
203  template <int dim>
204  void
205  PolynomialsRaviartThomasNodal<dim>::evaluate(
206  const Point<dim> & unit_point,
207  std::vector<Tensor<1, dim>> &values,
208  std::vector<Tensor<2, dim>> &grads,
209  std::vector<Tensor<3, dim>> &grad_grads,
210  std::vector<Tensor<4, dim>> &third_derivatives,
211  std::vector<Tensor<5, dim>> &fourth_derivatives) const
212  {
213  Assert(values.size() == this->n() || values.size() == 0,
214  ExcDimensionMismatch(values.size(), this->n()));
215  Assert(grads.size() == this->n() || grads.size() == 0,
216  ExcDimensionMismatch(grads.size(), this->n()));
217  Assert(grad_grads.size() == this->n() || grad_grads.size() == 0,
218  ExcDimensionMismatch(grad_grads.size(), this->n()));
219  Assert(third_derivatives.size() == this->n() ||
220  third_derivatives.size() == 0,
221  ExcDimensionMismatch(third_derivatives.size(), this->n()));
222  Assert(fourth_derivatives.size() == this->n() ||
223  fourth_derivatives.size() == 0,
224  ExcDimensionMismatch(fourth_derivatives.size(), this->n()));
225 
226  std::vector<double> p_values;
227  std::vector<Tensor<1, dim>> p_grads;
228  std::vector<Tensor<2, dim>> p_grad_grads;
229  std::vector<Tensor<3, dim>> p_third_derivatives;
230  std::vector<Tensor<4, dim>> p_fourth_derivatives;
231 
232  const unsigned int n_sub = polynomial_space.n();
233  p_values.resize((values.size() == 0) ? 0 : n_sub);
234  p_grads.resize((grads.size() == 0) ? 0 : n_sub);
235  p_grad_grads.resize((grad_grads.size() == 0) ? 0 : n_sub);
236  p_third_derivatives.resize((third_derivatives.size() == 0) ? 0 : n_sub);
237  p_fourth_derivatives.resize((fourth_derivatives.size() == 0) ? 0 : n_sub);
238 
239  for (unsigned int d = 0; d < dim; ++d)
240  {
241  // First we copy the point. The polynomial space for component d
242  // consists of polynomials of degree k in x_d and degree k+1 in the
243  // other variables. in order to simplify this, we use the same
244  // AnisotropicPolynomial space and simply rotate the coordinates
245  // through all directions.
246  Point<dim> p;
247  for (unsigned int c = 0; c < dim; ++c)
248  p(c) = unit_point((c + d) % dim);
249 
250  polynomial_space.evaluate(p,
251  p_values,
252  p_grads,
253  p_grad_grads,
254  p_third_derivatives,
255  p_fourth_derivatives);
256 
257  for (unsigned int i = 0; i < p_values.size(); ++i)
258  values[lexicographic_to_hierarchic[i + d * n_sub]][d] =
259  p_values[renumber_aniso[d][i]];
260 
261  for (unsigned int i = 0; i < p_grads.size(); ++i)
262  for (unsigned int d1 = 0; d1 < dim; ++d1)
263  grads[lexicographic_to_hierarchic[i + d * n_sub]][d]
264  [(d1 + d) % dim] = p_grads[renumber_aniso[d][i]][d1];
265 
266  for (unsigned int i = 0; i < p_grad_grads.size(); ++i)
267  for (unsigned int d1 = 0; d1 < dim; ++d1)
268  for (unsigned int d2 = 0; d2 < dim; ++d2)
269  grad_grads[lexicographic_to_hierarchic[i + d * n_sub]][d]
270  [(d1 + d) % dim][(d2 + d) % dim] =
271  p_grad_grads[renumber_aniso[d][i]][d1][d2];
272 
273  for (unsigned int i = 0; i < p_third_derivatives.size(); ++i)
274  for (unsigned int d1 = 0; d1 < dim; ++d1)
275  for (unsigned int d2 = 0; d2 < dim; ++d2)
276  for (unsigned int d3 = 0; d3 < dim; ++d3)
277  third_derivatives[lexicographic_to_hierarchic[i + d * n_sub]][d]
278  [(d1 + d) % dim][(d2 + d) % dim]
279  [(d3 + d) % dim] =
280  p_third_derivatives[renumber_aniso[d][i]][d1]
281  [d2][d3];
282 
283  for (unsigned int i = 0; i < p_fourth_derivatives.size(); ++i)
284  for (unsigned int d1 = 0; d1 < dim; ++d1)
285  for (unsigned int d2 = 0; d2 < dim; ++d2)
286  for (unsigned int d3 = 0; d3 < dim; ++d3)
287  for (unsigned int d4 = 0; d4 < dim; ++d4)
288  fourth_derivatives[lexicographic_to_hierarchic[i + d * n_sub]]
289  [d][(d1 + d) % dim][(d2 + d) % dim]
290  [(d3 + d) % dim][(d4 + d) % dim] =
291  p_fourth_derivatives[renumber_aniso[d][i]]
292  [d1][d2][d3][d4];
293  }
294  }
295 
296 
297 
298  template <int dim>
299  std::string
300  PolynomialsRaviartThomasNodal<dim>::name() const
301  {
302  return "PolynomialsRaviartThomasNodal";
303  }
304 
305 
306 
307  template <int dim>
308  unsigned int
309  PolynomialsRaviartThomasNodal<dim>::n_polynomials(unsigned int degree)
310  {
311  return dim * (degree + 2) * Utilities::pow(degree + 1, dim - 1);
312  }
313 
314 
315 
316  template <int dim>
317  std::unique_ptr<TensorPolynomialsBase<dim>>
318  PolynomialsRaviartThomasNodal<dim>::clone() const
319  {
320  return std::make_unique<PolynomialsRaviartThomasNodal<dim>>(*this);
321  }
322 
323 
324 
325  template <int dim>
326  std::vector<Point<dim>>
327  PolynomialsRaviartThomasNodal<dim>::get_polynomial_support_points() const
328  {
329  Assert(dim > 0 && dim <= 3, ExcImpossibleInDim(dim));
330  const Quadrature<1> low(
331  degree == 0 ? static_cast<Quadrature<1>>(QMidpoint<1>()) :
332  static_cast<Quadrature<1>>(QGaussLobatto<1>(degree + 1)));
333  const QGaussLobatto<1> high(degree + 2);
334  const QAnisotropic<dim> quad =
335  (dim == 1 ? QAnisotropic<dim>(high) :
336  (dim == 2 ? QAnisotropic<dim>(high, low) :
337  QAnisotropic<dim>(high, low, low)));
338 
339  const unsigned int n_sub = polynomial_space.n();
340  std::vector<Point<dim>> points(dim * n_sub);
341  points.resize(n_polynomials(degree));
342  for (unsigned int d = 0; d < dim; ++d)
343  for (unsigned int i = 0; i < n_sub; ++i)
344  for (unsigned int c = 0; c < dim; ++c)
345  points[lexicographic_to_hierarchic[i + d * n_sub]][(c + d) % dim] =
346  quad.point(renumber_aniso[d][i])[c];
347  return points;
348  }
349 
350 
351 
352  // Return a vector of "dofs per object" where the components of the returned
353  // vector refer to:
354  // 0 = vertex
355  // 1 = edge
356  // 2 = face (which is a cell in 2D)
357  // 3 = cell
358  std::vector<unsigned int>
359  get_rt_dpo_vector(const unsigned int dim, const unsigned int degree)
360  {
361  std::vector<unsigned int> dpo(dim + 1);
362  dpo[0] = 0;
363  dpo[1] = 0;
364  unsigned int dofs_per_face = 1;
365  for (unsigned int d = 1; d < dim; ++d)
366  dofs_per_face *= (degree + 1);
367 
368  dpo[dim - 1] = dofs_per_face;
369  dpo[dim] = dim * degree * dofs_per_face;
370 
371  return dpo;
372  }
373 } // namespace
374 
375 namespace internal
376 {
377  template <int dim>
378  std::vector<unsigned int>
379  get_lexicographic_numbering_rt_nodal(const unsigned int degree)
380  {
381  const unsigned int n_dofs_face = Utilities::pow(degree, dim - 1);
382  std::vector<unsigned int> lexicographic_numbering;
383  // component 1
384  for (unsigned int j = 0; j < n_dofs_face; j++)
385  {
386  lexicographic_numbering.push_back(j);
387  for (unsigned int i = n_dofs_face * 2 * dim;
388  i < n_dofs_face * 2 * dim + degree - 1;
389  i++)
390  lexicographic_numbering.push_back(i + j * (degree - 1));
391  lexicographic_numbering.push_back(n_dofs_face + j);
392  }
393 
394  // component 2
395  unsigned int layers = (dim == 3) ? degree : 1;
396  for (unsigned int k = 0; k < layers; k++)
397  {
398  unsigned int k_add = k * degree;
399  for (unsigned int j = n_dofs_face * 2; j < n_dofs_face * 2 + degree;
400  j++)
401  lexicographic_numbering.push_back(j + k_add);
402 
403  for (unsigned int i = n_dofs_face * (2 * dim + (degree - 1));
404  i < n_dofs_face * (2 * dim + (degree - 1)) + (degree - 1) * degree;
405  i++)
406  {
407  lexicographic_numbering.push_back(i + k_add * (degree - 1));
408  }
409  for (unsigned int j = n_dofs_face * 3; j < n_dofs_face * 3 + degree;
410  j++)
411  lexicographic_numbering.push_back(j + k_add);
412  }
413 
414  // component 3
415  if (dim == 3)
416  {
417  for (unsigned int i = 4 * n_dofs_face; i < 5 * n_dofs_face; i++)
418  lexicographic_numbering.push_back(i);
419  for (unsigned int i = 6 * n_dofs_face + n_dofs_face * 2 * (degree - 1);
420  i < 6 * n_dofs_face + n_dofs_face * 3 * (degree - 1);
421  i++)
422  lexicographic_numbering.push_back(i);
423  for (unsigned int i = 5 * n_dofs_face; i < 6 * n_dofs_face; i++)
424  lexicographic_numbering.push_back(i);
425  }
426 
427  return lexicographic_numbering;
428  }
429 } // namespace internal
430 
431 
432 
433 // --------------------- actual implementation of element --------------------
434 
435 template <int dim>
437  : FE_PolyTensor<dim>(PolynomialsRaviartThomasNodal<dim>(degree),
438  FiniteElementData<dim>(get_rt_dpo_vector(dim, degree),
439  dim,
440  degree + 1,
441  FiniteElementData<dim>::Hdiv),
442  std::vector<bool>(1, false),
443  std::vector<ComponentMask>(
444  PolynomialsRaviartThomasNodal<dim>::n_polynomials(
445  degree),
446  std::vector<bool>(dim, true)))
447 {
448  Assert(dim >= 2, ExcImpossibleInDim(dim));
449 
451 
452  // First, initialize the generalized support points and quadrature weights,
453  // since they are required for interpolation.
455  PolynomialsRaviartThomasNodal<dim>(degree).get_polynomial_support_points();
457  this->n_dofs_per_cell());
458 
459  const unsigned int face_no = 0;
460  if (dim > 1)
461  this->generalized_face_support_points[face_no] =
462  degree == 0 ? QGauss<dim - 1>(1).get_points() :
463  QGaussLobatto<dim - 1>(degree + 1).get_points();
464 
466  for (unsigned int i = 0; i < GeometryInfo<dim>::max_children_per_face; ++i)
467  face_embeddings[i].reinit(this->n_dofs_per_face(face_no),
468  this->n_dofs_per_face(face_no));
469  FETools::compute_face_embedding_matrices<dim, double>(*this,
470  face_embeddings,
471  0,
472  0);
474  this->n_dofs_per_face(face_no),
475  this->n_dofs_per_face(face_no));
476  unsigned int target_row = 0;
477  for (unsigned int d = 0; d < GeometryInfo<dim>::max_children_per_face; ++d)
478  for (unsigned int i = 0; i < face_embeddings[d].m(); ++i)
479  {
480  for (unsigned int j = 0; j < face_embeddings[d].n(); ++j)
481  this->interface_constraints(target_row, j) = face_embeddings[d](i, j);
482  ++target_row;
483  }
484 
485  // We need to initialize the dof permutation table and the one for the sign
486  // change.
488 }
489 
490 
491 
492 template <int dim>
493 std::string
495 {
496  // note that the FETools::get_fe_by_name function depends on the particular
497  // format of the string this function returns, so they have to be kept in
498  // synch
499 
500  // note that this->degree is the maximal polynomial degree and is thus one
501  // higher than the argument given to the constructor
502  return "FE_RaviartThomasNodal<" + std::to_string(dim) + ">(" +
503  std::to_string(this->degree - 1) + ")";
504 }
505 
506 
507 template <int dim>
508 std::unique_ptr<FiniteElement<dim, dim>>
510 {
511  return std::make_unique<FE_RaviartThomasNodal<dim>>(*this);
512 }
513 
514 
515 //---------------------------------------------------------------------------
516 // Auxiliary and internal functions
517 //---------------------------------------------------------------------------
518 
519 
520 
521 template <int dim>
522 void
524  dim>::initialize_quad_dof_index_permutation_and_sign_change()
525 {
526  // for 1D and 2D, do nothing
527  if (dim < 3)
528  return;
529 
530  const unsigned int n = this->degree;
531  const unsigned int face_no = 0;
532  Assert(n * n == this->n_dofs_per_quad(face_no), ExcInternalError());
533  for (unsigned int local = 0; local < this->n_dofs_per_quad(face_no); ++local)
534  // face support points are in lexicographic ordering with x running
535  // fastest. invert that (y running fastest)
536  {
537  unsigned int i = local % n, j = local / n;
538 
539  // face_orientation=false, face_flip=false, face_rotation=false
540  this->adjust_quad_dof_index_for_face_orientation_table[face_no](local,
541  0) =
542  j + i * n - local;
543  // face_orientation=false, face_flip=false, face_rotation=true
544  this->adjust_quad_dof_index_for_face_orientation_table[face_no](local,
545  1) =
546  i + (n - 1 - j) * n - local;
547  // face_orientation=false, face_flip=true, face_rotation=false
548  this->adjust_quad_dof_index_for_face_orientation_table[face_no](local,
549  2) =
550  (n - 1 - j) + (n - 1 - i) * n - local;
551  // face_orientation=false, face_flip=true, face_rotation=true
552  this->adjust_quad_dof_index_for_face_orientation_table[face_no](local,
553  3) =
554  (n - 1 - i) + j * n - local;
555  // face_orientation=true, face_flip=false, face_rotation=false
556  this->adjust_quad_dof_index_for_face_orientation_table[face_no](local,
557  4) = 0;
558  // face_orientation=true, face_flip=false, face_rotation=true
559  this->adjust_quad_dof_index_for_face_orientation_table[face_no](local,
560  5) =
561  j + (n - 1 - i) * n - local;
562  // face_orientation=true, face_flip=true, face_rotation=false
563  this->adjust_quad_dof_index_for_face_orientation_table[face_no](local,
564  6) =
565  (n - 1 - i) + (n - 1 - j) * n - local;
566  // face_orientation=true, face_flip=true, face_rotation=true
567  this->adjust_quad_dof_index_for_face_orientation_table[face_no](local,
568  7) =
569  (n - 1 - j) + i * n - local;
570 
571  // for face_orientation == false, we need to switch the sign
572  for (unsigned int i = 0; i < 4; ++i)
573  this->adjust_quad_dof_sign_for_face_orientation_table[face_no](local,
574  i) = 1;
575  }
576 }
577 
578 
579 
580 template <int dim>
581 bool
583  const unsigned int shape_index,
584  const unsigned int face_index) const
585 {
586  AssertIndexRange(shape_index, this->n_dofs_per_cell());
588 
589  // The first degrees of freedom are on the faces and each face has degree
590  // degrees.
591  const unsigned int support_face = shape_index / this->n_dofs_per_face();
592 
593  // The only thing we know for sure is that shape functions with support on
594  // one face are zero on the opposite face.
595  if (support_face < GeometryInfo<dim>::faces_per_cell)
596  return (face_index != GeometryInfo<dim>::opposite_face[support_face]);
597 
598  // In all other cases, return true, which is safe
599  return true;
600 }
601 
602 
603 
604 template <int dim>
605 void
608  const std::vector<Vector<double>> &support_point_values,
609  std::vector<double> & nodal_values) const
610 {
611  Assert(support_point_values.size() == this->generalized_support_points.size(),
612  ExcDimensionMismatch(support_point_values.size(),
613  this->generalized_support_points.size()));
614  Assert(nodal_values.size() == this->n_dofs_per_cell(),
615  ExcDimensionMismatch(nodal_values.size(), this->n_dofs_per_cell()));
616  Assert(support_point_values[0].size() == this->n_components(),
617  ExcDimensionMismatch(support_point_values[0].size(),
618  this->n_components()));
619 
620  // First do interpolation on faces. There, the component evaluated depends
621  // on the face direction and orientation.
622  unsigned int fbase = 0;
623  unsigned int f = 0;
624  for (; f < GeometryInfo<dim>::faces_per_cell;
625  ++f, fbase += this->n_dofs_per_face(f))
626  {
627  for (unsigned int i = 0; i < this->n_dofs_per_face(f); ++i)
628  {
629  nodal_values[fbase + i] = support_point_values[fbase + i](
631  }
632  }
633 
634  // The remaining points form dim chunks, one for each component
635  const unsigned int istep = (this->n_dofs_per_cell() - fbase) / dim;
636  Assert((this->n_dofs_per_cell() - fbase) % dim == 0, ExcInternalError());
637 
638  f = 0;
639  while (fbase < this->n_dofs_per_cell())
640  {
641  for (unsigned int i = 0; i < istep; ++i)
642  {
643  nodal_values[fbase + i] = support_point_values[fbase + i](f);
644  }
645  fbase += istep;
646  ++f;
647  }
648  Assert(fbase == this->n_dofs_per_cell(), ExcInternalError());
649 }
650 
651 
652 
653 // TODO: There are tests that check that the following few functions don't
654 // produce assertion failures, but none that actually check whether they do the
655 // right thing. one example for such a test would be to project a function onto
656 // an hp-space and make sure that the convergence order is correct with regard
657 // to the lowest used polynomial degree
658 
659 template <int dim>
660 bool
662 {
663  return true;
664 }
665 
666 
667 template <int dim>
668 std::vector<std::pair<unsigned int, unsigned int>>
670  const FiniteElement<dim> &fe_other) const
671 {
672  // we can presently only compute these identities if both FEs are
673  // FE_RaviartThomasNodals or the other is FE_Nothing. In either case, no
674  // dofs are assigned on the vertex, so we shouldn't be getting here at all.
675  if (dynamic_cast<const FE_RaviartThomasNodal<dim> *>(&fe_other) != nullptr)
676  return std::vector<std::pair<unsigned int, unsigned int>>();
677  else if (dynamic_cast<const FE_Nothing<dim> *>(&fe_other) != nullptr)
678  return std::vector<std::pair<unsigned int, unsigned int>>();
679  else
680  {
681  Assert(false, ExcNotImplemented());
682  return std::vector<std::pair<unsigned int, unsigned int>>();
683  }
684 }
685 
686 
687 
688 template <int dim>
689 std::vector<std::pair<unsigned int, unsigned int>>
691  const FiniteElement<dim> &fe_other) const
692 {
693  // we can presently only compute these identities if both FEs are
694  // FE_RaviartThomasNodals or if the other one is FE_Nothing
695  if (const FE_RaviartThomasNodal<dim> *fe_q_other =
696  dynamic_cast<const FE_RaviartThomasNodal<dim> *>(&fe_other))
697  {
698  // dofs are located on faces; these are only lines in 2d
699  if (dim != 2)
700  return std::vector<std::pair<unsigned int, unsigned int>>();
701 
702  // dofs are located along lines, so two dofs are identical only if in
703  // the following two cases (remember that the face support points are
704  // Gauss points):
705  // 1. this->degree = fe_q_other->degree,
706  // in the case, all the dofs on the line are identical
707  // 2. this->degree-1 and fe_q_other->degree-1
708  // are both even, i.e. this->dof_per_line and fe_q_other->dof_per_line
709  // are both odd, there exists only one point (the middle one) such
710  // that dofs are identical on this point
711  //
712  // to understand this, note that this->degree is the *maximal*
713  // polynomial degree, and is thus one higher than the argument given to
714  // the constructor
715  const unsigned int p = this->degree - 1;
716  const unsigned int q = fe_q_other->degree - 1;
717 
718  std::vector<std::pair<unsigned int, unsigned int>> identities;
719 
720  if (p == q)
721  for (unsigned int i = 0; i < p + 1; ++i)
722  identities.emplace_back(i, i);
723 
724  else if (p % 2 == 0 && q % 2 == 0)
725  identities.emplace_back(p / 2, q / 2);
726 
727  return identities;
728  }
729  else if (dynamic_cast<const FE_Nothing<dim> *>(&fe_other) != nullptr)
730  {
731  // the FE_Nothing has no degrees of freedom, so there are no
732  // equivalencies to be recorded
733  return std::vector<std::pair<unsigned int, unsigned int>>();
734  }
735  else
736  {
737  Assert(false, ExcNotImplemented());
738  return std::vector<std::pair<unsigned int, unsigned int>>();
739  }
740 }
741 
742 
743 template <int dim>
744 std::vector<std::pair<unsigned int, unsigned int>>
746  const FiniteElement<dim> &fe_other,
747  const unsigned int face_no) const
748 {
749  // we can presently only compute these identities if both FEs are
750  // FE_RaviartThomasNodals or if the other one is FE_Nothing
751  if (const FE_RaviartThomasNodal<dim> *fe_q_other =
752  dynamic_cast<const FE_RaviartThomasNodal<dim> *>(&fe_other))
753  {
754  // dofs are located on faces; these are only quads in 3d
755  if (dim != 3)
756  return std::vector<std::pair<unsigned int, unsigned int>>();
757 
758  // this works exactly like the line case above
759  const unsigned int p = this->n_dofs_per_quad(face_no);
760 
761  AssertDimension(fe_q_other->n_unique_faces(), 1);
762  const unsigned int q = fe_q_other->n_dofs_per_quad(0);
763 
764  std::vector<std::pair<unsigned int, unsigned int>> identities;
765 
766  if (p == q)
767  for (unsigned int i = 0; i < p; ++i)
768  identities.emplace_back(i, i);
769 
770  else if (p % 2 != 0 && q % 2 != 0)
771  identities.emplace_back(p / 2, q / 2);
772 
773  return identities;
774  }
775  else if (dynamic_cast<const FE_Nothing<dim> *>(&fe_other) != nullptr)
776  {
777  // the FE_Nothing has no degrees of freedom, so there are no
778  // equivalencies to be recorded
779  return std::vector<std::pair<unsigned int, unsigned int>>();
780  }
781  else
782  {
783  Assert(false, ExcNotImplemented());
784  return std::vector<std::pair<unsigned int, unsigned int>>();
785  }
786 }
787 
788 
789 template <int dim>
792  const FiniteElement<dim> &fe_other,
793  const unsigned int codim) const
794 {
795  Assert(codim <= dim, ExcImpossibleInDim(dim));
796  (void)codim;
797 
798  // vertex/line/face/cell domination
799  // --------------------------------
800  if (const FE_RaviartThomasNodal<dim> *fe_rt_nodal_other =
801  dynamic_cast<const FE_RaviartThomasNodal<dim> *>(&fe_other))
802  {
803  if (this->degree < fe_rt_nodal_other->degree)
805  else if (this->degree == fe_rt_nodal_other->degree)
807  else
809  }
810  else if (const FE_Nothing<dim> *fe_nothing =
811  dynamic_cast<const FE_Nothing<dim> *>(&fe_other))
812  {
813  if (fe_nothing->is_dominating())
815  else
816  // the FE_Nothing has no degrees of freedom and it is typically used
817  // in a context where we don't require any continuity along the
818  // interface
820  }
821 
822  Assert(false, ExcNotImplemented());
824 }
825 
826 
827 
828 template <>
829 void
831  const FiniteElement<1, 1> & /*x_source_fe*/,
832  FullMatrix<double> & /*interpolation_matrix*/,
833  const unsigned int) const
834 {
835  Assert(false, ExcImpossibleInDim(1));
836 }
837 
838 
839 template <>
840 void
842  const FiniteElement<1, 1> & /*x_source_fe*/,
843  const unsigned int /*subface*/,
844  FullMatrix<double> & /*interpolation_matrix*/,
845  const unsigned int) const
846 {
847  Assert(false, ExcImpossibleInDim(1));
848 }
849 
850 
851 
852 template <int dim>
853 void
855  const FiniteElement<dim> &x_source_fe,
856  FullMatrix<double> & interpolation_matrix,
857  const unsigned int face_no) const
858 {
859  // this is only implemented, if the
860  // source FE is also a
861  // RaviartThomasNodal element
862  AssertThrow((x_source_fe.get_name().find("FE_RaviartThomasNodal<") == 0) ||
863  (dynamic_cast<const FE_RaviartThomasNodal<dim> *>(
864  &x_source_fe) != nullptr),
866 
867  Assert(interpolation_matrix.n() == this->n_dofs_per_face(face_no),
868  ExcDimensionMismatch(interpolation_matrix.n(),
869  this->n_dofs_per_face(face_no)));
870  Assert(interpolation_matrix.m() == x_source_fe.n_dofs_per_face(face_no),
871  ExcDimensionMismatch(interpolation_matrix.m(),
872  x_source_fe.n_dofs_per_face(face_no)));
873 
874  // ok, source is a RaviartThomasNodal element, so we will be able to do the
875  // work
876  const FE_RaviartThomasNodal<dim> &source_fe =
877  dynamic_cast<const FE_RaviartThomasNodal<dim> &>(x_source_fe);
878 
879  // Make sure that the element for which the DoFs should be constrained is
880  // the one with the higher polynomial degree. Actually the procedure will
881  // work also if this assertion is not satisfied. But the matrices produced
882  // in that case might lead to problems in the hp-procedures, which use this
883  // method.
884  Assert(this->n_dofs_per_face(face_no) <= source_fe.n_dofs_per_face(face_no),
886 
887  // generate a quadrature with the generalized support points. This is later
888  // based as a basis for the QProjector, which returns the support points on
889  // the face.
890  Quadrature<dim - 1> quad_face_support(
891  source_fe.generalized_face_support_points[face_no]);
892 
893  // Rule of thumb for FP accuracy, that can be expected for a given
894  // polynomial degree. This value is used to cut off values close to zero.
895  double eps = 2e-13 * this->degree * (dim - 1);
896 
897  // compute the interpolation matrix by simply taking the value at the
898  // support points.
899  const Quadrature<dim> face_projection =
901  quad_face_support,
902  0);
903 
904  for (unsigned int i = 0; i < source_fe.n_dofs_per_face(face_no); ++i)
905  {
906  const Point<dim> &p = face_projection.point(i);
907 
908  for (unsigned int j = 0; j < this->n_dofs_per_face(face_no); ++j)
909  {
910  double matrix_entry =
911  this->shape_value_component(this->face_to_cell_index(j, 0), p, 0);
912 
913  // Correct the interpolated value. I.e. if it is close to 1 or 0,
914  // make it exactly 1 or 0. Unfortunately, this is required to avoid
915  // problems with higher order elements.
916  if (std::fabs(matrix_entry - 1.0) < eps)
917  matrix_entry = 1.0;
918  if (std::fabs(matrix_entry) < eps)
919  matrix_entry = 0.0;
920 
921  interpolation_matrix(i, j) = matrix_entry;
922  }
923  }
924 
925 #ifdef DEBUG
926  // make sure that the row sum of each of the matrices is 1 at this
927  // point. this must be so since the shape functions sum up to 1
928  for (unsigned int j = 0; j < source_fe.n_dofs_per_face(face_no); ++j)
929  {
930  double sum = 0.;
931 
932  for (unsigned int i = 0; i < this->n_dofs_per_face(face_no); ++i)
933  sum += interpolation_matrix(j, i);
934 
935  Assert(std::fabs(sum - 1) < 2e-13 * this->degree * (dim - 1),
936  ExcInternalError());
937  }
938 #endif
939 }
940 
941 
942 template <int dim>
943 void
945  const FiniteElement<dim> &x_source_fe,
946  const unsigned int subface,
947  FullMatrix<double> & interpolation_matrix,
948  const unsigned int face_no) const
949 {
950  // this is only implemented, if the source FE is also a RaviartThomasNodal
951  // element
952  AssertThrow((x_source_fe.get_name().find("FE_RaviartThomasNodal<") == 0) ||
953  (dynamic_cast<const FE_RaviartThomasNodal<dim> *>(
954  &x_source_fe) != nullptr),
956 
957  Assert(interpolation_matrix.n() == this->n_dofs_per_face(face_no),
958  ExcDimensionMismatch(interpolation_matrix.n(),
959  this->n_dofs_per_face(face_no)));
960  Assert(interpolation_matrix.m() == x_source_fe.n_dofs_per_face(face_no),
961  ExcDimensionMismatch(interpolation_matrix.m(),
962  x_source_fe.n_dofs_per_face(face_no)));
963 
964  // ok, source is a RaviartThomasNodal element, so we will be able to do the
965  // work
966  const FE_RaviartThomasNodal<dim> &source_fe =
967  dynamic_cast<const FE_RaviartThomasNodal<dim> &>(x_source_fe);
968 
969  // Make sure that the element for which the DoFs should be constrained is
970  // the one with the higher polynomial degree. Actually the procedure will
971  // work also if this assertion is not satisfied. But the matrices produced
972  // in that case might lead to problems in the hp-procedures, which use this
973  // method.
974  Assert(this->n_dofs_per_face(face_no) <= source_fe.n_dofs_per_face(face_no),
976 
977  // generate a quadrature with the generalized support points. This is later
978  // based as a basis for the QProjector, which returns the support points on
979  // the face.
980  Quadrature<dim - 1> quad_face_support(
981  source_fe.generalized_face_support_points[face_no]);
982 
983  // Rule of thumb for FP accuracy, that can be expected for a given
984  // polynomial degree. This value is used to cut off values close to zero.
985  double eps = 2e-13 * this->degree * (dim - 1);
986 
987  // compute the interpolation matrix by simply taking the value at the
988  // support points.
989  const Quadrature<dim> subface_projection =
991  quad_face_support,
992  0,
993  subface);
994 
995  for (unsigned int i = 0; i < source_fe.n_dofs_per_face(face_no); ++i)
996  {
997  const Point<dim> &p = subface_projection.point(i);
998 
999  for (unsigned int j = 0; j < this->n_dofs_per_face(face_no); ++j)
1000  {
1001  double matrix_entry =
1002  this->shape_value_component(this->face_to_cell_index(j, 0), p, 0);
1003 
1004  // Correct the interpolated value. I.e. if it is close to 1 or 0,
1005  // make it exactly 1 or 0. Unfortunately, this is required to avoid
1006  // problems with higher order elements.
1007  if (std::fabs(matrix_entry - 1.0) < eps)
1008  matrix_entry = 1.0;
1009  if (std::fabs(matrix_entry) < eps)
1010  matrix_entry = 0.0;
1011 
1012  interpolation_matrix(i, j) = matrix_entry;
1013  }
1014  }
1015 
1016 #ifdef DEBUG
1017  // make sure that the row sum of each of the matrices is 1 at this
1018  // point. this must be so since the shape functions sum up to 1
1019  for (unsigned int j = 0; j < source_fe.n_dofs_per_face(face_no); ++j)
1020  {
1021  double sum = 0.;
1022 
1023  for (unsigned int i = 0; i < this->n_dofs_per_face(face_no); ++i)
1024  sum += interpolation_matrix(j, i);
1025 
1026  Assert(std::fabs(sum - 1) < 2e-13 * this->degree * (dim - 1),
1027  ExcInternalError());
1028  }
1029 #endif
1030 }
1031 
1032 
1033 
1034 template <int dim>
1035 const FullMatrix<double> &
1037  const unsigned int child,
1038  const RefinementCase<dim> &refinement_case) const
1039 {
1040  AssertIndexRange(refinement_case,
1042  Assert(refinement_case != RefinementCase<dim>::no_refinement,
1043  ExcMessage(
1044  "Prolongation matrices are only available for refined cells!"));
1045  AssertIndexRange(child, GeometryInfo<dim>::n_children(refinement_case));
1046 
1047  // initialization upon first request
1048  if (this->prolongation[refinement_case - 1][child].n() == 0)
1049  {
1050  std::lock_guard<std::mutex> lock(this->mutex);
1051 
1052  // if matrix got updated while waiting for the lock
1053  if (this->prolongation[refinement_case - 1][child].n() ==
1054  this->n_dofs_per_cell())
1055  return this->prolongation[refinement_case - 1][child];
1056 
1057  // now do the work. need to get a non-const version of data in order to
1058  // be able to modify them inside a const function
1059  FE_RaviartThomasNodal<dim> &this_nonconst =
1060  const_cast<FE_RaviartThomasNodal<dim> &>(*this);
1061  if (refinement_case == RefinementCase<dim>::isotropic_refinement)
1062  {
1063  std::vector<std::vector<FullMatrix<double>>> isotropic_matrices(
1065  isotropic_matrices.back().resize(
1067  FullMatrix<double>(this->n_dofs_per_cell(),
1068  this->n_dofs_per_cell()));
1069  FETools::compute_embedding_matrices(*this, isotropic_matrices, true);
1070  this_nonconst.prolongation[refinement_case - 1].swap(
1071  isotropic_matrices.back());
1072  }
1073  else
1074  {
1075  // must compute both restriction and prolongation matrices because
1076  // we only check for their size and the reinit call initializes them
1077  // all
1080  this_nonconst.prolongation);
1082  this_nonconst.restriction);
1083  }
1084  }
1085 
1086  // finally return the matrix
1087  return this->prolongation[refinement_case - 1][child];
1088 }
1089 
1090 
1091 
1092 template <int dim>
1093 const FullMatrix<double> &
1095  const unsigned int child,
1096  const RefinementCase<dim> &refinement_case) const
1097 {
1098  AssertIndexRange(refinement_case,
1100  Assert(refinement_case != RefinementCase<dim>::no_refinement,
1101  ExcMessage(
1102  "Restriction matrices are only available for refined cells!"));
1103  AssertIndexRange(child, GeometryInfo<dim>::n_children(refinement_case));
1104 
1105  // initialization upon first request
1106  if (this->restriction[refinement_case - 1][child].n() == 0)
1107  {
1108  std::lock_guard<std::mutex> lock(this->mutex);
1109 
1110  // if matrix got updated while waiting for the lock...
1111  if (this->restriction[refinement_case - 1][child].n() ==
1112  this->n_dofs_per_cell())
1113  return this->restriction[refinement_case - 1][child];
1114 
1115  // now do the work. need to get a non-const version of data in order to
1116  // be able to modify them inside a const function
1117  FE_RaviartThomasNodal<dim> &this_nonconst =
1118  const_cast<FE_RaviartThomasNodal<dim> &>(*this);
1119  if (refinement_case == RefinementCase<dim>::isotropic_refinement)
1120  {
1121  std::vector<std::vector<FullMatrix<double>>> isotropic_matrices(
1123  isotropic_matrices.back().resize(
1125  FullMatrix<double>(this->n_dofs_per_cell(),
1126  this->n_dofs_per_cell()));
1127  FETools::compute_projection_matrices(*this, isotropic_matrices, true);
1128  this_nonconst.restriction[refinement_case - 1].swap(
1129  isotropic_matrices.back());
1130  }
1131  else
1132  {
1133  // must compute both restriction and prolongation matrices because
1134  // we only check for their size and the reinit call initializes them
1135  // all
1138  this_nonconst.prolongation);
1140  this_nonconst.restriction);
1141  }
1142  }
1143 
1144  // finally return the matrix
1145  return this->restriction[refinement_case - 1][child];
1146 }
1147 
1148 
1149 
1150 // explicit instantiations
1151 #include "fe_raviart_thomas_nodal.inst"
1152 
1153 
std::vector< MappingKind > mapping_kind
virtual std::unique_ptr< FiniteElement< dim, dim > > clone() const override
virtual bool has_support_on_face(const unsigned int shape_index, const unsigned int face_index) const override
virtual const FullMatrix< double > & get_restriction_matrix(const unsigned int child, const RefinementCase< dim > &refinement_case=RefinementCase< dim >::isotropic_refinement) const override
void initialize_quad_dof_index_permutation_and_sign_change()
virtual std::vector< std::pair< unsigned int, unsigned int > > hp_line_dof_identities(const FiniteElement< dim > &fe_other) const override
virtual void get_face_interpolation_matrix(const FiniteElement< dim > &source, FullMatrix< double > &matrix, const unsigned int face_no=0) const override
virtual std::string get_name() const override
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 bool hp_constraints_are_implemented() const override
virtual void get_subface_interpolation_matrix(const FiniteElement< dim > &source, const unsigned int subface, FullMatrix< double > &matrix, const unsigned int face_no=0) const override
virtual FiniteElementDomination::Domination compare_for_domination(const FiniteElement< dim > &fe_other, const unsigned int codim=0) const override final
virtual const FullMatrix< double > & get_prolongation_matrix(const unsigned int child, const RefinementCase< dim > &refinement_case=RefinementCase< dim >::isotropic_refinement) const override
virtual std::vector< std::pair< unsigned int, unsigned int > > hp_vertex_dof_identities(const FiniteElement< dim > &fe_other) const override
virtual std::vector< std::pair< unsigned int, unsigned int > > hp_quad_dof_identities(const FiniteElement< dim > &fe_other, const unsigned int face_no=0) const override
FE_RaviartThomasNodal(const unsigned int p)
const unsigned int degree
Definition: fe_data.h:449
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:2413
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:2470
FullMatrix< double > interface_constraints
Definition: fe.h:2439
std::vector< Point< dim > > generalized_support_points
Definition: fe.h:2464
std::vector< std::vector< FullMatrix< double > > > prolongation
Definition: fe.h:2427
size_type n() const
size_type m() const
Definition: point.h:111
static void project_to_subface(const SubQuadrature &quadrature, const unsigned int face_no, const unsigned int subface_no, std::vector< Point< dim >> &q_points, const RefinementCase< dim - 1 > &ref_case=RefinementCase< dim - 1 >::isotropic_refinement)
static void project_to_face(const SubQuadrature &quadrature, const unsigned int face_no, std::vector< Point< dim >> &q_points)
const Point< dim > & point(const unsigned int i) const
SymmetricTensor< rank, dim, Number > sum(const SymmetricTensor< rank, dim, Number > &local, const MPI_Comm &mpi_communicator)
virtual std::string name() const =0
virtual std::unique_ptr< TensorPolynomialsBase< dim > > clone() const =0
unsigned int degree() const
virtual void evaluate(const Point< dim > &unit_point, std::vector< Tensor< 1, dim >> &values, std::vector< Tensor< 2, dim >> &grads, std::vector< Tensor< 3, dim >> &grad_grads, std::vector< Tensor< 4, dim >> &third_derivatives, std::vector< Tensor< 5, dim >> &fourth_derivatives) const =0
Definition: vector.h:109
#define DEAL_II_NAMESPACE_OPEN
Definition: config.h:442
#define DEAL_II_NAMESPACE_CLOSE
Definition: config.h:443
static ::ExceptionBase & ExcInternalError()
static ::ExceptionBase & ExcDimensionMismatch(std::size_t arg1, std::size_t arg2)
#define Assert(cond, exc)
Definition: exceptions.h:1473
std::string to_string(const T &t)
Definition: patterns.h:2403
static ::ExceptionBase & ExcNotImplemented()
static ::ExceptionBase & ExcImpossibleInDim(int arg1)
#define AssertDimension(dim1, dim2)
Definition: exceptions.h:1667
#define AssertIndexRange(index, range)
Definition: exceptions.h:1732
static ::ExceptionBase & ExcMessage(std::string arg1)
#define AssertThrow(cond, exc)
Definition: exceptions.h:1583
@ mapping_raviart_thomas
Definition: mapping.h:127
Expression fabs(const Expression &x)
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)
void reference_cell(Triangulation< dim, spacedim > &tria, const ReferenceCell &reference_cell)
SymmetricTensor< 2, dim, Number > d(const Tensor< 2, dim, Number > &F, const Tensor< 2, dim, Number > &dF_dt)
SymmetricTensor< 2, dim, Number > e(const Tensor< 2, dim, Number > &F)
std::vector< Polynomial< double > > generate_complete_Lagrange_basis(const std::vector< Point< 1 >> &points)
Definition: polynomial.cc:702
constexpr T pow(const T base, const int iexp)
Definition: utilities.h:462
std::vector< Integer > invert_permutation(const std::vector< Integer > &permutation)
Definition: utilities.h:1813
void reinit(MatrixBlock< MatrixType > &v, const BlockSparsityPattern &p)
Definition: matrix_block.h:618
std::vector< unsigned int > get_lexicographic_numbering_rt_nodal(const unsigned int degree)