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