1 // ---------------------------------------------------------------------
2 //
3 // Copyright (C) 2001 - 2020 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 
17 #include <deal.II/base/quadrature.h>
18 #include <deal.II/base/quadrature_lib.h>
19 
20 #include <deal.II/fe/fe.h>
21 #include <deal.II/fe/fe_bernstein.h>
22 #include <deal.II/fe/fe_dgq.h>
23 #include <deal.II/fe/fe_nothing.h>
24 #include <deal.II/fe/fe_q.h>
25 #include <deal.II/fe/fe_q_bubbles.h>
26 #include <deal.II/fe/fe_q_dg0.h>
27 #include <deal.II/fe/fe_q_hierarchical.h>
28 #include <deal.II/fe/fe_q_iso_q1.h>
29 #include <deal.II/fe/fe_tools.h>
30 
31 #include <deal.II/lac/vector.h>
32 
33 #include <iostream>
34 #include <memory>
35 #include <sstream>
36 
37 
38 DEAL_II_NAMESPACE_OPEN
39 
40 
41 namespace internal
42 {
43   namespace FE_DGQ
44   {
45     namespace
46     {
47       std::vector<Point<1>>
get_QGaussLobatto_points(const unsigned int degree)48       get_QGaussLobatto_points(const unsigned int degree)
49       {
50         if (degree > 0)
51           return QGaussLobatto<1>(degree + 1).get_points();
52         else
53           return std::vector<Point<1>>(1, Point<1>(0.5));
54       }
55     } // namespace
56   }   // namespace FE_DGQ
57 } // namespace internal
58 
59 
60 
61 template <int dim, int spacedim>
FE_DGQ(const unsigned int degree)62 FE_DGQ<dim, spacedim>::FE_DGQ(const unsigned int degree)
63   : FE_Poly<dim, spacedim>(
64       TensorProductPolynomials<dim>(
65         Polynomials::generate_complete_Lagrange_basis(
66           internal::FE_DGQ::get_QGaussLobatto_points(degree))),
67       FiniteElementData<dim>(get_dpo_vector(degree),
68                              1,
69                              degree,
70                              FiniteElementData<dim>::L2),
71       std::vector<bool>(
72         FiniteElementData<dim>(get_dpo_vector(degree), 1, degree)
73           .n_dofs_per_cell(),
74         true),
75       std::vector<ComponentMask>(
76         FiniteElementData<dim>(get_dpo_vector(degree), 1, degree)
77           .n_dofs_per_cell(),
78         std::vector<bool>(1, true)))
79 {
80   // Compute support points, which are the tensor product of the Lagrange
81   // interpolation points in the constructor.
82   this->unit_support_points =
83     Quadrature<dim>(internal::FE_DGQ::get_QGaussLobatto_points(degree))
84       .get_points();
85 
86   // do not initialize embedding and restriction here. these matrices are
87   // initialized on demand in get_restriction_matrix and
88   // get_prolongation_matrix
89 
90   // note: no face support points for DG elements
91 }
92 
93 
94 
95 template <int dim, int spacedim>
FE_DGQ(const std::vector<Polynomials::Polynomial<double>> & polynomials)96 FE_DGQ<dim, spacedim>::FE_DGQ(
97   const std::vector<Polynomials::Polynomial<double>> &polynomials)
98   : FE_Poly<dim, spacedim>(
99       TensorProductPolynomials<dim>(polynomials),
100       FiniteElementData<dim>(get_dpo_vector(polynomials.size() - 1),
101                              1,
102                              polynomials.size() - 1,
103                              FiniteElementData<dim>::L2),
104       std::vector<bool>(FiniteElementData<dim>(get_dpo_vector(
105                                                  polynomials.size() - 1),
106                                                1,
107                                                polynomials.size() - 1)
108                           .n_dofs_per_cell(),
109                         true),
110       std::vector<ComponentMask>(
111         FiniteElementData<dim>(get_dpo_vector(polynomials.size() - 1),
112                                1,
113                                polynomials.size() - 1)
114           .n_dofs_per_cell(),
115         std::vector<bool>(1, true)))
116 {
117   // No support points can be defined in general. Derived classes might define
118   // support points like the class FE_DGQArbitraryNodes
119 
120   // do not initialize embedding and restriction here. these matrices are
121   // initialized on demand in get_restriction_matrix and
122   // get_prolongation_matrix
123 }
124 
125 
126 
127 template <int dim, int spacedim>
128 std::string
get_name() const129 FE_DGQ<dim, spacedim>::get_name() const
130 {
131   // note that the FETools::get_fe_by_name function depends on the
132   // particular format of the string this function returns, so they have to be
133   // kept in sync
134 
135   std::ostringstream namebuf;
136   namebuf << "FE_DGQ<" << Utilities::dim_string(dim, spacedim) << ">("
137           << this->degree << ")";
138   return namebuf.str();
139 }
140 
141 
142 
143 template <int dim, int spacedim>
144 void
convert_generalized_support_point_values_to_dof_values(const std::vector<Vector<double>> & support_point_values,std::vector<double> & nodal_values) const145 FE_DGQ<dim, spacedim>::convert_generalized_support_point_values_to_dof_values(
146   const std::vector<Vector<double>> &support_point_values,
147   std::vector<double> &              nodal_values) const
148 {
149   AssertDimension(support_point_values.size(),
150                   this->get_unit_support_points().size());
151   AssertDimension(support_point_values.size(), nodal_values.size());
152   AssertDimension(this->n_dofs_per_cell(), nodal_values.size());
153 
154   for (unsigned int i = 0; i < this->n_dofs_per_cell(); ++i)
155     {
156       AssertDimension(support_point_values[i].size(), 1);
157 
158       nodal_values[i] = support_point_values[i](0);
159     }
160 }
161 
162 
163 
164 template <int dim, int spacedim>
165 std::unique_ptr<FiniteElement<dim, spacedim>>
clone() const166 FE_DGQ<dim, spacedim>::clone() const
167 {
168   return std::make_unique<FE_DGQ<dim, spacedim>>(*this);
169 }
170 
171 
172 //---------------------------------------------------------------------------
173 // Auxiliary functions
174 //---------------------------------------------------------------------------
175 
176 
177 template <int dim, int spacedim>
178 std::vector<unsigned int>
get_dpo_vector(const unsigned int deg)179 FE_DGQ<dim, spacedim>::get_dpo_vector(const unsigned int deg)
180 {
181   std::vector<unsigned int> dpo(dim + 1, 0U);
182   dpo[dim] = deg + 1;
183   for (unsigned int i = 1; i < dim; ++i)
184     dpo[dim] *= deg + 1;
185   return dpo;
186 }
187 
188 
189 
190 template <int dim, int spacedim>
191 void
rotate_indices(std::vector<unsigned int> & numbers,const char direction) const192 FE_DGQ<dim, spacedim>::rotate_indices(std::vector<unsigned int> &numbers,
193                                       const char direction) const
194 {
195   const unsigned int n = this->degree + 1;
196   unsigned int       s = n;
197   for (unsigned int i = 1; i < dim; ++i)
198     s *= n;
199   numbers.resize(s);
200 
201   unsigned int l = 0;
202 
203   if (dim == 1)
204     {
205       // Mirror around midpoint
206       for (unsigned int i = n; i > 0;)
207         numbers[l++] = --i;
208     }
209   else
210     {
211       switch (direction)
212         {
213           // Rotate xy-plane
214           // counter-clockwise
215           case 'z':
216             for (unsigned int iz = 0; iz < ((dim > 2) ? n : 1); ++iz)
217               for (unsigned int j = 0; j < n; ++j)
218                 for (unsigned int i = 0; i < n; ++i)
219                   {
220                     unsigned int k = n * i - j + n - 1 + n * n * iz;
221                     numbers[l++]   = k;
222                   }
223             break;
224           // Rotate xy-plane
225           // clockwise
226           case 'Z':
227             for (unsigned int iz = 0; iz < ((dim > 2) ? n : 1); ++iz)
228               for (unsigned int iy = 0; iy < n; ++iy)
229                 for (unsigned int ix = 0; ix < n; ++ix)
230                   {
231                     unsigned int k = n * ix - iy + n - 1 + n * n * iz;
232                     numbers[k]     = l++;
233                   }
234             break;
235           // Rotate yz-plane
236           // counter-clockwise
237           case 'x':
238             Assert(dim > 2, ExcDimensionMismatch(dim, 3));
239             for (unsigned int iz = 0; iz < n; ++iz)
240               for (unsigned int iy = 0; iy < n; ++iy)
241                 for (unsigned int ix = 0; ix < n; ++ix)
242                   {
243                     unsigned int k = n * (n * iy - iz + n - 1) + ix;
244                     numbers[l++]   = k;
245                   }
246             break;
247           // Rotate yz-plane
248           // clockwise
249           case 'X':
250             Assert(dim > 2, ExcDimensionMismatch(dim, 3));
251             for (unsigned int iz = 0; iz < n; ++iz)
252               for (unsigned int iy = 0; iy < n; ++iy)
253                 for (unsigned int ix = 0; ix < n; ++ix)
254                   {
255                     unsigned int k = n * (n * iy - iz + n - 1) + ix;
256                     numbers[k]     = l++;
257                   }
258             break;
259           default:
260             Assert(false, ExcNotImplemented());
261         }
262     }
263 }
264 
265 
266 
267 template <int dim, int spacedim>
268 void
get_interpolation_matrix(const FiniteElement<dim,spacedim> & x_source_fe,FullMatrix<double> & interpolation_matrix) const269 FE_DGQ<dim, spacedim>::get_interpolation_matrix(
270   const FiniteElement<dim, spacedim> &x_source_fe,
271   FullMatrix<double> &                interpolation_matrix) const
272 {
273   // this is only implemented, if the
274   // source FE is also a
275   // DGQ element
276   using FE = FiniteElement<dim, spacedim>;
277   AssertThrow((dynamic_cast<const FE_DGQ<dim, spacedim> *>(&x_source_fe) !=
278                nullptr),
279               typename FE::ExcInterpolationNotImplemented());
280 
281   // ok, source is a Q element, so
282   // we will be able to do the work
283   const FE_DGQ<dim, spacedim> &source_fe =
284     dynamic_cast<const FE_DGQ<dim, spacedim> &>(x_source_fe);
285 
286   Assert(interpolation_matrix.m() == this->n_dofs_per_cell(),
287          ExcDimensionMismatch(interpolation_matrix.m(),
288                               this->n_dofs_per_cell()));
289   Assert(interpolation_matrix.n() == source_fe.n_dofs_per_cell(),
290          ExcDimensionMismatch(interpolation_matrix.n(),
291                               source_fe.n_dofs_per_cell()));
292 
293 
294   // compute the interpolation
295   // matrices in much the same way as
296   // we do for the embedding matrices
297   // from mother to child.
298   FullMatrix<double> cell_interpolation(this->n_dofs_per_cell(),
299                                         this->n_dofs_per_cell());
300   FullMatrix<double> source_interpolation(this->n_dofs_per_cell(),
301                                           source_fe.n_dofs_per_cell());
302   FullMatrix<double> tmp(this->n_dofs_per_cell(), source_fe.n_dofs_per_cell());
303   for (unsigned int j = 0; j < this->n_dofs_per_cell(); ++j)
304     {
305       // generate a point on this
306       // cell and evaluate the
307       // shape functions there
308       const Point<dim> p = this->unit_support_points[j];
309       for (unsigned int i = 0; i < this->n_dofs_per_cell(); ++i)
310         cell_interpolation(j, i) = this->poly_space->compute_value(i, p);
311 
312       for (unsigned int i = 0; i < source_fe.n_dofs_per_cell(); ++i)
313         source_interpolation(j, i) = source_fe.poly_space->compute_value(i, p);
314     }
315 
316   // then compute the
317   // interpolation matrix matrix
318   // for this coordinate
319   // direction
320   cell_interpolation.gauss_jordan();
321   cell_interpolation.mmult(interpolation_matrix, source_interpolation);
322 
323   // cut off very small values
324   for (unsigned int i = 0; i < this->n_dofs_per_cell(); ++i)
325     for (unsigned int j = 0; j < source_fe.n_dofs_per_cell(); ++j)
326       if (std::fabs(interpolation_matrix(i, j)) < 1e-15)
327         interpolation_matrix(i, j) = 0.;
328 
329   // make sure that the row sum of
330   // each of the matrices is 1 at
331   // this point. this must be so
332   // since the shape functions sum up
333   // to 1
334   for (unsigned int i = 0; i < this->n_dofs_per_cell(); ++i)
335     {
336       double sum = 0.;
337       for (unsigned int j = 0; j < source_fe.n_dofs_per_cell(); ++j)
338         sum += interpolation_matrix(i, j);
339 
340       Assert(std::fabs(sum - 1) < 5e-14 * std::max(this->degree, 1U) * dim,
341              ExcInternalError());
342     }
343 }
344 
345 
346 
347 template <int dim, int spacedim>
348 void
get_face_interpolation_matrix(const FiniteElement<dim,spacedim> & x_source_fe,FullMatrix<double> & interpolation_matrix,const unsigned int) const349 FE_DGQ<dim, spacedim>::get_face_interpolation_matrix(
350   const FiniteElement<dim, spacedim> &x_source_fe,
351   FullMatrix<double> &                interpolation_matrix,
352   const unsigned int) const
353 {
354   // this is only implemented, if the source
355   // FE is also a DGQ element. in that case,
356   // both elements have no dofs on their
357   // faces and the face interpolation matrix
358   // is necessarily empty -- i.e. there isn't
359   // much we need to do here.
360   (void)interpolation_matrix;
361   using FE = FiniteElement<dim, spacedim>;
362   AssertThrow((dynamic_cast<const FE_DGQ<dim, spacedim> *>(&x_source_fe) !=
363                nullptr),
364               typename FE::ExcInterpolationNotImplemented());
365 
366   Assert(interpolation_matrix.m() == 0,
367          ExcDimensionMismatch(interpolation_matrix.m(), 0));
368   Assert(interpolation_matrix.n() == 0,
369          ExcDimensionMismatch(interpolation_matrix.m(), 0));
370 }
371 
372 
373 
374 template <int dim, int spacedim>
375 void
get_subface_interpolation_matrix(const FiniteElement<dim,spacedim> & x_source_fe,const unsigned int,FullMatrix<double> & interpolation_matrix,const unsigned int) const376 FE_DGQ<dim, spacedim>::get_subface_interpolation_matrix(
377   const FiniteElement<dim, spacedim> &x_source_fe,
378   const unsigned int,
379   FullMatrix<double> &interpolation_matrix,
380   const unsigned int) const
381 {
382   // this is only implemented, if the source
383   // FE is also a DGQ element. in that case,
384   // both elements have no dofs on their
385   // faces and the face interpolation matrix
386   // is necessarily empty -- i.e. there isn't
387   // much we need to do here.
388   (void)interpolation_matrix;
389   using FE = FiniteElement<dim, spacedim>;
390   AssertThrow((dynamic_cast<const FE_DGQ<dim, spacedim> *>(&x_source_fe) !=
391                nullptr),
392               typename FE::ExcInterpolationNotImplemented());
393 
394   Assert(interpolation_matrix.m() == 0,
395          ExcDimensionMismatch(interpolation_matrix.m(), 0));
396   Assert(interpolation_matrix.n() == 0,
397          ExcDimensionMismatch(interpolation_matrix.m(), 0));
398 }
399 
400 
401 
402 template <int dim, int spacedim>
403 const FullMatrix<double> &
get_prolongation_matrix(const unsigned int child,const RefinementCase<dim> & refinement_case) const404 FE_DGQ<dim, spacedim>::get_prolongation_matrix(
405   const unsigned int         child,
406   const RefinementCase<dim> &refinement_case) const
407 {
408   AssertIndexRange(refinement_case,
409                    RefinementCase<dim>::isotropic_refinement + 1);
410   Assert(refinement_case != RefinementCase<dim>::no_refinement,
411          ExcMessage(
412            "Prolongation matrices are only available for refined cells!"));
413   AssertIndexRange(child, GeometryInfo<dim>::n_children(refinement_case));
414 
415   // initialization upon first request
416   if (this->prolongation[refinement_case - 1][child].n() == 0)
417     {
418       std::lock_guard<std::mutex> lock(this->mutex);
419 
420       // if matrix got updated while waiting for the lock
421       if (this->prolongation[refinement_case - 1][child].n() ==
422           this->n_dofs_per_cell())
423         return this->prolongation[refinement_case - 1][child];
424 
425       // now do the work. need to get a non-const version of data in order to
426       // be able to modify them inside a const function
427       FE_DGQ<dim, spacedim> &this_nonconst =
428         const_cast<FE_DGQ<dim, spacedim> &>(*this);
429       if (refinement_case == RefinementCase<dim>::isotropic_refinement)
430         {
431           std::vector<std::vector<FullMatrix<double>>> isotropic_matrices(
432             RefinementCase<dim>::isotropic_refinement);
433           isotropic_matrices.back().resize(
434             GeometryInfo<dim>::n_children(RefinementCase<dim>(refinement_case)),
435             FullMatrix<double>(this->n_dofs_per_cell(),
436                                this->n_dofs_per_cell()));
437           if (dim == spacedim)
438             FETools::compute_embedding_matrices(*this,
439                                                 isotropic_matrices,
440                                                 true);
441           else
442             FETools::compute_embedding_matrices(FE_DGQ<dim>(this->degree),
443                                                 isotropic_matrices,
444                                                 true);
445           this_nonconst.prolongation[refinement_case - 1].swap(
446             isotropic_matrices.back());
447         }
448       else
449         {
450           // must compute both restriction and prolongation matrices because
451           // we only check for their size and the reinit call initializes them
452           // all
453           this_nonconst.reinit_restriction_and_prolongation_matrices();
454           if (dim == spacedim)
455             {
456               FETools::compute_embedding_matrices(*this,
457                                                   this_nonconst.prolongation);
458               FETools::compute_projection_matrices(*this,
459                                                    this_nonconst.restriction);
460             }
461           else
462             {
463               FE_DGQ<dim> tmp(this->degree);
464               FETools::compute_embedding_matrices(tmp,
465                                                   this_nonconst.prolongation);
466               FETools::compute_projection_matrices(tmp,
467                                                    this_nonconst.restriction);
468             }
469         }
470     }
471 
472   // finally return the matrix
473   return this->prolongation[refinement_case - 1][child];
474 }
475 
476 
477 
478 template <int dim, int spacedim>
479 const FullMatrix<double> &
get_restriction_matrix(const unsigned int child,const RefinementCase<dim> & refinement_case) const480 FE_DGQ<dim, spacedim>::get_restriction_matrix(
481   const unsigned int         child,
482   const RefinementCase<dim> &refinement_case) const
483 {
484   AssertIndexRange(refinement_case,
485                    RefinementCase<dim>::isotropic_refinement + 1);
486   Assert(refinement_case != RefinementCase<dim>::no_refinement,
487          ExcMessage(
488            "Restriction matrices are only available for refined cells!"));
489   AssertIndexRange(child, GeometryInfo<dim>::n_children(refinement_case));
490 
491   // initialization upon first request
492   if (this->restriction[refinement_case - 1][child].n() == 0)
493     {
494       std::lock_guard<std::mutex> lock(this->mutex);
495 
496       // if matrix got updated while waiting for the lock...
497       if (this->restriction[refinement_case - 1][child].n() ==
498           this->n_dofs_per_cell())
499         return this->restriction[refinement_case - 1][child];
500 
501       // now do the work. need to get a non-const version of data in order to
502       // be able to modify them inside a const function
503       FE_DGQ<dim, spacedim> &this_nonconst =
504         const_cast<FE_DGQ<dim, spacedim> &>(*this);
505       if (refinement_case == RefinementCase<dim>::isotropic_refinement)
506         {
507           std::vector<std::vector<FullMatrix<double>>> isotropic_matrices(
508             RefinementCase<dim>::isotropic_refinement);
509           isotropic_matrices.back().resize(
510             GeometryInfo<dim>::n_children(RefinementCase<dim>(refinement_case)),
511             FullMatrix<double>(this->n_dofs_per_cell(),
512                                this->n_dofs_per_cell()));
513           if (dim == spacedim)
514             FETools::compute_projection_matrices(*this,
515                                                  isotropic_matrices,
516                                                  true);
517           else
518             FETools::compute_projection_matrices(FE_DGQ<dim>(this->degree),
519                                                  isotropic_matrices,
520                                                  true);
521           this_nonconst.restriction[refinement_case - 1].swap(
522             isotropic_matrices.back());
523         }
524       else
525         {
526           // must compute both restriction and prolongation matrices because
527           // we only check for their size and the reinit call initializes them
528           // all
529           this_nonconst.reinit_restriction_and_prolongation_matrices();
530           if (dim == spacedim)
531             {
532               FETools::compute_embedding_matrices(*this,
533                                                   this_nonconst.prolongation);
534               FETools::compute_projection_matrices(*this,
535                                                    this_nonconst.restriction);
536             }
537           else
538             {
539               FE_DGQ<dim> tmp(this->degree);
540               FETools::compute_embedding_matrices(tmp,
541                                                   this_nonconst.prolongation);
542               FETools::compute_projection_matrices(tmp,
543                                                    this_nonconst.restriction);
544             }
545         }
546     }
547 
548   // finally return the matrix
549   return this->restriction[refinement_case - 1][child];
550 }
551 
552 
553 
554 template <int dim, int spacedim>
555 bool
hp_constraints_are_implemented() const556 FE_DGQ<dim, spacedim>::hp_constraints_are_implemented() const
557 {
558   return true;
559 }
560 
561 
562 
563 template <int dim, int spacedim>
564 std::vector<std::pair<unsigned int, unsigned int>>
hp_vertex_dof_identities(const FiniteElement<dim,spacedim> &) const565 FE_DGQ<dim, spacedim>::hp_vertex_dof_identities(
566   const FiniteElement<dim, spacedim> & /*fe_other*/) const
567 {
568   // this element is discontinuous, so by definition there can
569   // be no identities between its dofs and those of any neighbor
570   // (of whichever type the neighbor may be -- after all, we have
571   // no face dofs on this side to begin with)
572   return std::vector<std::pair<unsigned int, unsigned int>>();
573 }
574 
575 
576 
577 template <int dim, int spacedim>
578 std::vector<std::pair<unsigned int, unsigned int>>
hp_line_dof_identities(const FiniteElement<dim,spacedim> &) const579 FE_DGQ<dim, spacedim>::hp_line_dof_identities(
580   const FiniteElement<dim, spacedim> & /*fe_other*/) const
581 {
582   // this element is discontinuous, so by definition there can
583   // be no identities between its dofs and those of any neighbor
584   // (of whichever type the neighbor may be -- after all, we have
585   // no face dofs on this side to begin with)
586   return std::vector<std::pair<unsigned int, unsigned int>>();
587 }
588 
589 
590 
591 template <int dim, int spacedim>
592 std::vector<std::pair<unsigned int, unsigned int>>
hp_quad_dof_identities(const FiniteElement<dim,spacedim> &,const unsigned int) const593 FE_DGQ<dim, spacedim>::hp_quad_dof_identities(
594   const FiniteElement<dim, spacedim> & /*fe_other*/,
595   const unsigned int) const
596 {
597   // this element is discontinuous, so by definition there can
598   // be no identities between its dofs and those of any neighbor
599   // (of whichever type the neighbor may be -- after all, we have
600   // no face dofs on this side to begin with)
601   return std::vector<std::pair<unsigned int, unsigned int>>();
602 }
603 
604 
605 
606 template <int dim, int spacedim>
607 FiniteElementDomination::Domination
compare_for_domination(const FiniteElement<dim,spacedim> & fe_other,const unsigned int codim) const608 FE_DGQ<dim, spacedim>::compare_for_domination(
609   const FiniteElement<dim, spacedim> &fe_other,
610   const unsigned int                  codim) const
611 {
612   Assert(codim <= dim, ExcImpossibleInDim(dim));
613 
614   // vertex/line/face domination
615   // ---------------------------
616   if (codim > 0)
617     // this is a discontinuous element, so by definition there will
618     // be no constraints wherever this element comes together with
619     // any other kind of element
620     return FiniteElementDomination::no_requirements;
621 
622   // cell domination
623   // ---------------
624   // The following block of conditionals is rather ugly, but there is currently
625   // no other way how to deal with a robust comparison of FE_DGQ elements with
626   // relevant others in the current implementation.
627   if (const FE_DGQ<dim, spacedim> *fe_dgq_other =
628         dynamic_cast<const FE_DGQ<dim, spacedim> *>(&fe_other))
629     {
630       if (this->degree < fe_dgq_other->degree)
631         return FiniteElementDomination::this_element_dominates;
632       else if (this->degree == fe_dgq_other->degree)
633         return FiniteElementDomination::either_element_can_dominate;
634       else
635         return FiniteElementDomination::other_element_dominates;
636     }
637   else if (const FE_Q<dim, spacedim> *fe_q_other =
638              dynamic_cast<const FE_Q<dim, spacedim> *>(&fe_other))
639     {
640       if (this->degree < fe_q_other->degree)
641         return FiniteElementDomination::this_element_dominates;
642       else if (this->degree == fe_q_other->degree)
643         return FiniteElementDomination::either_element_can_dominate;
644       else
645         return FiniteElementDomination::other_element_dominates;
646     }
647   else if (const FE_Bernstein<dim, spacedim> *fe_bernstein_other =
648              dynamic_cast<const FE_Bernstein<dim, spacedim> *>(&fe_other))
649     {
650       if (this->degree < fe_bernstein_other->degree)
651         return FiniteElementDomination::this_element_dominates;
652       else if (this->degree == fe_bernstein_other->degree)
653         return FiniteElementDomination::either_element_can_dominate;
654       else
655         return FiniteElementDomination::other_element_dominates;
656     }
657   else if (const FE_Q_Bubbles<dim, spacedim> *fe_bubbles_other =
658              dynamic_cast<const FE_Q_Bubbles<dim, spacedim> *>(&fe_other))
659     {
660       if (this->degree < fe_bubbles_other->degree)
661         return FiniteElementDomination::this_element_dominates;
662       else if (this->degree == fe_bubbles_other->degree)
663         return FiniteElementDomination::either_element_can_dominate;
664       else
665         return FiniteElementDomination::other_element_dominates;
666     }
667   else if (const FE_Q_DG0<dim, spacedim> *fe_dg0_other =
668              dynamic_cast<const FE_Q_DG0<dim, spacedim> *>(&fe_other))
669     {
670       if (this->degree < fe_dg0_other->degree)
671         return FiniteElementDomination::this_element_dominates;
672       else if (this->degree == fe_dg0_other->degree)
673         return FiniteElementDomination::either_element_can_dominate;
674       else
675         return FiniteElementDomination::other_element_dominates;
676     }
677   else if (const FE_Q_iso_Q1<dim, spacedim> *fe_q_iso_q1_other =
678              dynamic_cast<const FE_Q_iso_Q1<dim, spacedim> *>(&fe_other))
679     {
680       if (this->degree < fe_q_iso_q1_other->degree)
681         return FiniteElementDomination::this_element_dominates;
682       else if (this->degree == fe_q_iso_q1_other->degree)
683         return FiniteElementDomination::either_element_can_dominate;
684       else
685         return FiniteElementDomination::other_element_dominates;
686     }
687   else if (const FE_Q_Hierarchical<dim> *fe_hierarchical_other =
688              dynamic_cast<const FE_Q_Hierarchical<dim> *>(&fe_other))
689     {
690       if (this->degree < fe_hierarchical_other->degree)
691         return FiniteElementDomination::this_element_dominates;
692       else if (this->degree == fe_hierarchical_other->degree)
693         return FiniteElementDomination::either_element_can_dominate;
694       else
695         return FiniteElementDomination::other_element_dominates;
696     }
697   else if (const FE_Nothing<dim> *fe_nothing =
698              dynamic_cast<const FE_Nothing<dim> *>(&fe_other))
699     {
700       if (fe_nothing->is_dominating())
701         return FiniteElementDomination::other_element_dominates;
702       else
703         // the FE_Nothing has no degrees of freedom and it is typically used
704         // in a context where we don't require any continuity along the
705         // interface
706         return FiniteElementDomination::no_requirements;
707     }
708 
709   Assert(false, ExcNotImplemented());
710   return FiniteElementDomination::neither_element_dominates;
711 }
712 
713 
714 
715 template <int dim, int spacedim>
716 bool
has_support_on_face(const unsigned int shape_index,const unsigned int face_index) const717 FE_DGQ<dim, spacedim>::has_support_on_face(const unsigned int shape_index,
718                                            const unsigned int face_index) const
719 {
720   AssertIndexRange(shape_index, this->n_dofs_per_cell());
721   AssertIndexRange(face_index, GeometryInfo<dim>::faces_per_cell);
722 
723   unsigned int n = this->degree + 1;
724 
725   // For DGQ elements that do not define support points, we cannot define
726   // whether they have support at the boundary easily, so return true in any
727   // case
728   if (this->unit_support_points.empty())
729     return true;
730 
731   // for DGQ(0) elements or arbitrary node DGQ with support points not located
732   // at the element boundary, the single shape functions is constant and
733   // therefore lives on the boundary
734   bool support_points_on_boundary = true;
735   for (unsigned int d = 0; d < dim; ++d)
736     if (std::abs(this->unit_support_points[0][d]) > 1e-13)
737       support_points_on_boundary = false;
738   for (unsigned int d = 0; d < dim; ++d)
739     if (std::abs(this->unit_support_points.back()[d] - 1.) > 1e-13)
740       support_points_on_boundary = false;
741   if (support_points_on_boundary == false)
742     return true;
743 
744   unsigned int n2 = n * n;
745 
746   switch (dim)
747     {
748       case 1:
749         {
750           // in 1d, things are simple. since
751           // there is only one degree of
752           // freedom per vertex in this
753           // class, the first is on vertex 0
754           // (==face 0 in some sense), the
755           // second on face 1:
756           return (((shape_index == 0) && (face_index == 0)) ||
757                   ((shape_index == this->degree) && (face_index == 1)));
758         }
759 
760       case 2:
761         {
762           if (face_index == 0 && (shape_index % n) == 0)
763             return true;
764           if (face_index == 1 && (shape_index % n) == this->degree)
765             return true;
766           if (face_index == 2 && shape_index < n)
767             return true;
768           if (face_index == 3 && shape_index >= this->n_dofs_per_cell() - n)
769             return true;
770           return false;
771         }
772 
773       case 3:
774         {
775           const unsigned int in2 = shape_index % n2;
776 
777           // x=0
778           if (face_index == 0 && (shape_index % n) == 0)
779             return true;
780           // x=1
781           if (face_index == 1 && (shape_index % n) == n - 1)
782             return true;
783           // y=0
784           if (face_index == 2 && in2 < n)
785             return true;
786           // y=1
787           if (face_index == 3 && in2 >= n2 - n)
788             return true;
789           // z=0
790           if (face_index == 4 && shape_index < n2)
791             return true;
792           // z=1
793           if (face_index == 5 && shape_index >= this->n_dofs_per_cell() - n2)
794             return true;
795           return false;
796         }
797 
798       default:
799         Assert(false, ExcNotImplemented());
800     }
801   return true;
802 }
803 
804 
805 
806 template <int dim, int spacedim>
807 std::pair<Table<2, bool>, std::vector<unsigned int>>
get_constant_modes() const808 FE_DGQ<dim, spacedim>::get_constant_modes() const
809 {
810   Table<2, bool> constant_modes(1, this->n_dofs_per_cell());
811   constant_modes.fill(true);
812   return std::pair<Table<2, bool>, std::vector<unsigned int>>(
813     constant_modes, std::vector<unsigned int>(1, 0));
814 }
815 
816 
817 
818 // ------------------------------ FE_DGQArbitraryNodes -----------------------
819 
820 template <int dim, int spacedim>
FE_DGQArbitraryNodes(const Quadrature<1> & points)821 FE_DGQArbitraryNodes<dim, spacedim>::FE_DGQArbitraryNodes(
822   const Quadrature<1> &points)
823   : FE_DGQ<dim, spacedim>(
824       Polynomials::generate_complete_Lagrange_basis(points.get_points()))
825 {
826   Assert(points.size() > 0,
827          (typename FiniteElement<dim, spacedim>::ExcFEHasNoSupportPoints()));
828   this->unit_support_points = Quadrature<dim>(points).get_points();
829 }
830 
831 
832 
833 template <int dim, int spacedim>
834 std::string
get_name() const835 FE_DGQArbitraryNodes<dim, spacedim>::get_name() const
836 {
837   // note that the FETools::get_fe_by_name function does not work for
838   // FE_DGQArbitraryNodes since there is no initialization by a degree value.
839   std::ostringstream  namebuf;
840   bool                equidistant = true;
841   std::vector<double> points(this->degree + 1);
842 
843   auto *const polynomial_space =
844     dynamic_cast<TensorProductPolynomials<dim> *>(this->poly_space.get());
845   Assert(polynomial_space != nullptr, ExcInternalError());
846   std::vector<unsigned int> lexicographic =
847     polynomial_space->get_numbering_inverse();
848   for (unsigned int j = 0; j <= this->degree; j++)
849     points[j] = this->unit_support_points[lexicographic[j]][0];
850 
851   // Check whether the support points are equidistant.
852   for (unsigned int j = 0; j <= this->degree; j++)
853     if (std::abs(points[j] - static_cast<double>(j) / this->degree) > 1e-15)
854       {
855         equidistant = false;
856         break;
857       }
858   if (this->degree == 0 && std::abs(points[0] - 0.5) < 1e-15)
859     equidistant = true;
860 
861   if (equidistant == true)
862     {
863       if (this->degree > 2)
864         namebuf << "FE_DGQArbitraryNodes<"
865                 << Utilities::dim_string(dim, spacedim)
866                 << ">(QIterated(QTrapezoid()," << this->degree << "))";
867       else
868         namebuf << "FE_DGQ<" << Utilities::dim_string(dim, spacedim) << ">("
869                 << this->degree << ")";
870       return namebuf.str();
871     }
872 
873   // Check whether the support points come from QGaussLobatto.
874   const QGaussLobatto<1> points_gl(this->degree + 1);
875   bool                   gauss_lobatto = true;
876   for (unsigned int j = 0; j <= this->degree; j++)
877     if (points[j] != points_gl.point(j)(0))
878       {
879         gauss_lobatto = false;
880         break;
881       }
882 
883   if (gauss_lobatto == true)
884     {
885       namebuf << "FE_DGQ<" << Utilities::dim_string(dim, spacedim) << ">("
886               << this->degree << ")";
887       return namebuf.str();
888     }
889 
890   // Check whether the support points come from QGauss.
891   const QGauss<1> points_g(this->degree + 1);
892   bool            gauss = true;
893   for (unsigned int j = 0; j <= this->degree; j++)
894     if (points[j] != points_g.point(j)(0))
895       {
896         gauss = false;
897         break;
898       }
899 
900   if (gauss == true)
901     {
902       namebuf << "FE_DGQArbitraryNodes<" << Utilities::dim_string(dim, spacedim)
903               << ">(QGauss(" << this->degree + 1 << "))";
904       return namebuf.str();
905     }
906 
907   // Check whether the support points come from QGauss.
908   const QGaussLog<1> points_glog(this->degree + 1);
909   bool               gauss_log = true;
910   for (unsigned int j = 0; j <= this->degree; j++)
911     if (points[j] != points_glog.point(j)(0))
912       {
913         gauss_log = false;
914         break;
915       }
916 
917   if (gauss_log == true)
918     {
919       namebuf << "FE_DGQArbitraryNodes<" << Utilities::dim_string(dim, spacedim)
920               << ">(QGaussLog(" << this->degree + 1 << "))";
921       return namebuf.str();
922     }
923 
924   // All guesses exhausted
925   namebuf << "FE_DGQArbitraryNodes<" << Utilities::dim_string(dim, spacedim)
926           << ">(QUnknownNodes(" << this->degree + 1 << "))";
927   return namebuf.str();
928 }
929 
930 
931 
932 template <int dim, int spacedim>
933 void
934 FE_DGQArbitraryNodes<dim, spacedim>::
convert_generalized_support_point_values_to_dof_values(const std::vector<Vector<double>> & support_point_values,std::vector<double> & nodal_values) const935   convert_generalized_support_point_values_to_dof_values(
936     const std::vector<Vector<double>> &support_point_values,
937     std::vector<double> &              nodal_values) const
938 {
939   AssertDimension(support_point_values.size(),
940                   this->get_unit_support_points().size());
941   AssertDimension(support_point_values.size(), nodal_values.size());
942   AssertDimension(this->n_dofs_per_cell(), nodal_values.size());
943 
944   for (unsigned int i = 0; i < this->n_dofs_per_cell(); ++i)
945     {
946       AssertDimension(support_point_values[i].size(), 1);
947 
948       nodal_values[i] = support_point_values[i](0);
949     }
950 }
951 
952 
953 
954 template <int dim, int spacedim>
955 std::unique_ptr<FiniteElement<dim, spacedim>>
clone() const956 FE_DGQArbitraryNodes<dim, spacedim>::clone() const
957 {
958   // Construct a dummy quadrature formula containing the FE's nodes:
959   std::vector<Point<1>> qpoints(this->degree + 1);
960   auto *const           polynomial_space =
961     dynamic_cast<TensorProductPolynomials<dim> *>(this->poly_space.get());
962   Assert(polynomial_space != nullptr, ExcInternalError());
963   std::vector<unsigned int> lexicographic =
964     polynomial_space->get_numbering_inverse();
965   for (unsigned int i = 0; i <= this->degree; ++i)
966     qpoints[i] = Point<1>(this->unit_support_points[lexicographic[i]][0]);
967   Quadrature<1> pquadrature(qpoints);
968 
969   return std::make_unique<FE_DGQArbitraryNodes<dim, spacedim>>(pquadrature);
970 }
971 
972 
973 
974 // ---------------------------------- FE_DGQLegendre -------------------------
975 
976 template <int dim, int spacedim>
FE_DGQLegendre(const unsigned int degree)977 FE_DGQLegendre<dim, spacedim>::FE_DGQLegendre(const unsigned int degree)
978   : FE_DGQ<dim, spacedim>(
979       Polynomials::Legendre::generate_complete_basis(degree))
980 {}
981 
982 
983 
984 template <int dim, int spacedim>
985 std::pair<Table<2, bool>, std::vector<unsigned int>>
get_constant_modes() const986 FE_DGQLegendre<dim, spacedim>::get_constant_modes() const
987 {
988   // Legendre represents a constant function by one in the first basis
989   // function and zero in all others
990   Table<2, bool> constant_modes(1, this->n_dofs_per_cell());
991   constant_modes(0, 0) = true;
992   return std::pair<Table<2, bool>, std::vector<unsigned int>>(
993     constant_modes, std::vector<unsigned int>(1, 0));
994 }
995 
996 
997 
998 template <int dim, int spacedim>
999 std::string
get_name() const1000 FE_DGQLegendre<dim, spacedim>::get_name() const
1001 {
1002   return "FE_DGQLegendre<" + Utilities::dim_string(dim, spacedim) + ">(" +
1003          Utilities::int_to_string(this->degree) + ")";
1004 }
1005 
1006 
1007 
1008 template <int dim, int spacedim>
1009 std::unique_ptr<FiniteElement<dim, spacedim>>
clone() const1010 FE_DGQLegendre<dim, spacedim>::clone() const
1011 {
1012   return std::make_unique<FE_DGQLegendre<dim, spacedim>>(this->degree);
1013 }
1014 
1015 
1016 
1017 // ---------------------------------- FE_DGQHermite --------------------------
1018 
1019 template <int dim, int spacedim>
FE_DGQHermite(const unsigned int degree)1020 FE_DGQHermite<dim, spacedim>::FE_DGQHermite(const unsigned int degree)
1021   : FE_DGQ<dim, spacedim>(
1022       Polynomials::HermiteLikeInterpolation::generate_complete_basis(degree))
1023 {}
1024 
1025 
1026 
1027 template <int dim, int spacedim>
1028 std::string
get_name() const1029 FE_DGQHermite<dim, spacedim>::get_name() const
1030 {
1031   return "FE_DGQHermite<" + Utilities::dim_string(dim, spacedim) + ">(" +
1032          Utilities::int_to_string(this->degree) + ")";
1033 }
1034 
1035 
1036 
1037 template <int dim, int spacedim>
1038 std::unique_ptr<FiniteElement<dim, spacedim>>
clone() const1039 FE_DGQHermite<dim, spacedim>::clone() const
1040 {
1041   return std::make_unique<FE_DGQHermite<dim, spacedim>>(this->degree);
1042 }
1043 
1044 
1045 
1046 // explicit instantiations
1047 #include "fe_dgq.inst"
1048 
1049 
1050 DEAL_II_NAMESPACE_CLOSE
1051