]> https://gitweb.dealii.org/ - dealii.git/commitdiff
Split vector_tools.templates.h
authorDaniel Arndt <arndtd@ornl.gov>
Sun, 19 Apr 2020 16:50:21 +0000 (12:50 -0400)
committerDaniel Arndt <arndtd@ornl.gov>
Tue, 21 Apr 2020 23:13:08 +0000 (19:13 -0400)
23 files changed:
include/deal.II/numerics/vector_tools.templates.h
include/deal.II/numerics/vector_tools_boundary.templates.h [new file with mode: 0644]
include/deal.II/numerics/vector_tools_constraints.templates.h [new file with mode: 0644]
include/deal.II/numerics/vector_tools_integrate_difference.templates.h [new file with mode: 0644]
include/deal.II/numerics/vector_tools_interpolate.templates.h [new file with mode: 0644]
include/deal.II/numerics/vector_tools_mean_value.templates.h [new file with mode: 0644]
include/deal.II/numerics/vector_tools_point_gradient.templates.h [new file with mode: 0644]
include/deal.II/numerics/vector_tools_point_value.templates.h [new file with mode: 0644]
include/deal.II/numerics/vector_tools_project.templates.h [new file with mode: 0644]
include/deal.II/numerics/vector_tools_rhs.templates.h [new file with mode: 0644]
source/numerics/vector_tools_boundary.cc
source/numerics/vector_tools_constraints.cc
source/numerics/vector_tools_integrate_difference.cc
source/numerics/vector_tools_interpolate.cc
source/numerics/vector_tools_mean_value.cc
source/numerics/vector_tools_point_gradient.cc
source/numerics/vector_tools_point_value.cc
source/numerics/vector_tools_project.cc
source/numerics/vector_tools_project_codim.cc
source/numerics/vector_tools_project_hp.cc
source/numerics/vector_tools_project_qp.cc
source/numerics/vector_tools_project_qpmf.cc
source/numerics/vector_tools_rhs.cc

index 18917f863ff9374b7f83cd6d06e2ed04ff9d5b72..49805adfc452b7ea89486f2d63bc2fc85028ff73 100644 (file)
 #ifndef dealii_vector_tools_templates_h
 #define dealii_vector_tools_templates_h
 
-#include <deal.II/base/config.h>
-
-#include <deal.II/base/derivative_form.h>
-#include <deal.II/base/function.h>
-#include <deal.II/base/numbers.h>
-#include <deal.II/base/polynomials_piecewise.h>
-#include <deal.II/base/qprojector.h>
-#include <deal.II/base/quadrature.h>
-
-#include <deal.II/distributed/tria_base.h>
-
-#include <deal.II/dofs/dof_accessor.h>
-#include <deal.II/dofs/dof_handler.h>
-#include <deal.II/dofs/dof_tools.h>
-
-#include <deal.II/fe/fe.h>
-#include <deal.II/fe/fe_dgp.h>
-#include <deal.II/fe/fe_dgq.h>
-#include <deal.II/fe/fe_nedelec.h>
-#include <deal.II/fe/fe_nedelec_sz.h>
-#include <deal.II/fe/fe_nothing.h>
-#include <deal.II/fe/fe_q.h>
-#include <deal.II/fe/fe_q_dg0.h>
-#include <deal.II/fe/fe_raviart_thomas.h>
-#include <deal.II/fe/fe_system.h>
-#include <deal.II/fe/fe_tools.h>
-#include <deal.II/fe/fe_values.h>
-#include <deal.II/fe/mapping_q.h>
-#include <deal.II/fe/mapping_q1.h>
-
-#include <deal.II/grid/grid_tools.h>
-#include <deal.II/grid/intergrid_map.h>
-#include <deal.II/grid/tria_accessor.h>
-#include <deal.II/grid/tria_iterator.h>
-
-#include <deal.II/hp/dof_handler.h>
-#include <deal.II/hp/fe_values.h>
-#include <deal.II/hp/mapping_collection.h>
-#include <deal.II/hp/q_collection.h>
-
-#include <deal.II/lac/affine_constraints.h>
-#include <deal.II/lac/block_vector.h>
-#include <deal.II/lac/dynamic_sparsity_pattern.h>
-#include <deal.II/lac/la_parallel_block_vector.h>
-#include <deal.II/lac/la_parallel_vector.h>
-#include <deal.II/lac/la_vector.h>
-#include <deal.II/lac/petsc_block_vector.h>
-#include <deal.II/lac/petsc_vector.h>
-#include <deal.II/lac/precondition.h>
-#include <deal.II/lac/solver_cg.h>
-#include <deal.II/lac/solver_gmres.h>
-#include <deal.II/lac/sparse_matrix.h>
-#include <deal.II/lac/trilinos_epetra_vector.h>
-#include <deal.II/lac/trilinos_parallel_block_vector.h>
-#include <deal.II/lac/trilinos_tpetra_vector.h>
-#include <deal.II/lac/trilinos_vector.h>
-#include <deal.II/lac/vector.h>
-#include <deal.II/lac/vector_memory.h>
-
-#include <deal.II/matrix_free/fe_evaluation.h>
-#include <deal.II/matrix_free/matrix_free.h>
-#include <deal.II/matrix_free/operators.h>
-
-#include <deal.II/numerics/matrix_tools.h>
-#include <deal.II/numerics/vector_tools.h>
-
-#include <boost/range/iterator_range.hpp>
-
-#include <algorithm>
-#include <array>
-#include <cmath>
-#include <limits>
-#include <list>
-#include <numeric>
-#include <set>
-#include <typeinfo>
-#include <vector>
-
-DEAL_II_NAMESPACE_OPEN
-
-namespace VectorTools
-{
-  // This namespace contains the actual implementation called
-  // by VectorTools::interpolate and variants (such as
-  // VectorTools::interpolate_by_material_id).
-  namespace internal
-  {
-    // A small helper function to transform a component range starting
-    // at offset from the real to the unit cell according to the
-    // supplied conformity. The function_values vector is transformed
-    // in place.
-    //
-    // FIXME: This should be refactored into the mapping (i.e.
-    // implement the inverse function of Mapping<dim, spacedim>::transform).
-    // Further, the finite element should make the information about
-    // the correct mapping directly accessible (i.e. which MappingKind
-    // should be used). Using fe.conforming_space might be a bit of a
-    // problem because we only support doing nothing, Hcurl, and Hdiv
-    // conforming mappings.
-    //
-    // Input:
-    //  conformity: conformity of the finite element, used to select
-    //              appropriate type of transformation
-    //  fe_values_jacobians: used for jacobians (and inverses of
-    //                        jacobians). the object is supposed to be
-    //                        reinit()'d for the current cell
-    //  function_values, offset: function_values is manipulated in place
-    //                           starting at position offset
-    template <int dim, int spacedim, typename FEValuesType, typename T3>
-    void
-    transform(const typename FiniteElementData<dim>::Conformity conformity,
-              const unsigned int                                offset,
-              const FEValuesType &fe_values_jacobians,
-              T3 &                function_values)
-    {
-      switch (conformity)
-        {
-          case FiniteElementData<dim>::Hcurl:
-            // See Monk, Finite Element Methods for Maxwell's Equations,
-            // p. 77ff, formula (3.76) and Corollary 3.58.
-            // For given mapping F_K: \hat K \to K, we have to transform
-            //  \hat u = (dF_K)^T u\circ F_K
-
-            for (unsigned int i = 0; i < function_values.size(); ++i)
-              {
-                const auto &jacobians =
-                  fe_values_jacobians.get_present_fe_values().get_jacobians();
-
-                const ArrayView<typename T3::value_type::value_type> source(
-                  &function_values[i][0] + offset, dim);
-
-                Tensor<1,
-                       dim,
-                       typename ProductType<typename T3::value_type::value_type,
-                                            double>::type>
-                  destination;
-
-                // value[m] <- sum jacobian_transpose[m][n] * old_value[n]:
-                TensorAccessors::contract<1, 2, 1, dim>(
-                  destination, jacobians[i].transpose(), source);
-
-                // now copy things back into the input=output vector
-                for (unsigned int d = 0; d < dim; ++d)
-                  source[d] = destination[d];
-              }
-            break;
-
-          case FiniteElementData<dim>::Hdiv:
-            // See Monk, Finite Element Methods for Maxwell's Equations,
-            // p. 79ff, formula (3.77) and Lemma 3.59.
-            // For given mapping F_K: \hat K \to K, we have to transform
-            //  \hat w = det(dF_K) (dF_K)^{-1} w\circ F_K
-
-            for (unsigned int i = 0; i < function_values.size(); ++i)
-              {
-                const auto &jacobians =
-                  fe_values_jacobians.get_present_fe_values().get_jacobians();
-                const auto &inverse_jacobians =
-                  fe_values_jacobians.get_present_fe_values()
-                    .get_inverse_jacobians();
-
-                const ArrayView<typename T3::value_type::value_type> source(
-                  &function_values[i][0] + offset, dim);
-
-                Tensor<1,
-                       dim,
-                       typename ProductType<typename T3::value_type::value_type,
-                                            double>::type>
-                  destination;
-
-                // value[m] <- sum inverse_jacobians[m][n] * old_value[n]:
-                TensorAccessors::contract<1, 2, 1, dim>(destination,
-                                                        inverse_jacobians[i],
-                                                        source);
-                destination *= jacobians[i].determinant();
-
-                // now copy things back into the input=output vector
-                for (unsigned int d = 0; d < dim; ++d)
-                  source[d] = destination[d];
-              }
-            break;
-
-          case FiniteElementData<dim>::H1:
-            DEAL_II_FALLTHROUGH;
-          case FiniteElementData<dim>::L2:
-            // See Monk, Finite Element Methods for Maxwell's Equations,
-            // p. 77ff, formula (3.74).
-            // For given mapping F_K: \hat K \to K, we have to transform
-            //  \hat p = p\circ F_K
-            //  i.e., do nothing.
-            break;
-
-          default:
-            // In case we deal with an unknown conformity, just assume we
-            // deal with a Lagrange element and do nothing.
-            break;
-
-        } /*switch*/
-    }
-
-
-    // A small helper function that iteratively applies above transform
-    // function to a vector function_values recursing over a given finite
-    // element decomposing it into base elements:
-    //
-    // Input
-    //   fe: the full finite element corresponding to function_values
-    //   [ rest see above]
-    // Output: the offset after we have handled the element at
-    //   a given offset
-    template <int dim, int spacedim, typename FEValuesType, typename T3>
-    unsigned int
-    apply_transform(const FiniteElement<dim, spacedim> &fe,
-                    const unsigned int                  offset,
-                    const FEValuesType &                fe_values_jacobians,
-                    T3 &                                function_values)
-    {
-      if (const auto *system =
-            dynamic_cast<const FESystem<dim, spacedim> *>(&fe))
-        {
-          // In case of an FESystem transform every (vector) component
-          // separately:
-          unsigned current_offset = offset;
-          for (unsigned int i = 0; i < system->n_base_elements(); ++i)
-            {
-              const auto &base_fe      = system->base_element(i);
-              const auto  multiplicity = system->element_multiplicity(i);
-              for (unsigned int m = 0; m < multiplicity; ++m)
-                {
-                  // recursively call apply_transform to make sure to
-                  // correctly handle nested fe systems.
-                  current_offset = apply_transform(base_fe,
-                                                   current_offset,
-                                                   fe_values_jacobians,
-                                                   function_values);
-                }
-            }
-          return current_offset;
-        }
-      else
-        {
-          transform<dim, spacedim>(fe.conforming_space,
-                                   offset,
-                                   fe_values_jacobians,
-                                   function_values);
-          return (offset + fe.n_components());
-        }
-    }
-
-
-    // Internal implementation of interpolate that takes a generic functor
-    // function such that function(cell) is of type
-    // Function<spacedim, typename VectorType::value_type>*
-    //
-    // A given cell is skipped if function(cell) == nullptr
-    template <int dim,
-              int spacedim,
-              typename VectorType,
-              template <int, int> class DoFHandlerType,
-              typename T>
-    void
-    interpolate(const Mapping<dim, spacedim> &       mapping,
-                const DoFHandlerType<dim, spacedim> &dof_handler,
-                T &                                  function,
-                VectorType &                         vec,
-                const ComponentMask &                component_mask)
-    {
-      Assert(component_mask.represents_n_components(
-               dof_handler.get_fe_collection().n_components()),
-             ExcMessage(
-               "The number of components in the mask has to be either "
-               "zero or equal to the number of components in the finite "
-               "element."));
-
-      Assert(vec.size() == dof_handler.n_dofs(),
-             ExcDimensionMismatch(vec.size(), dof_handler.n_dofs()));
-
-      Assert(component_mask.n_selected_components(
-               dof_handler.get_fe_collection().n_components()) > 0,
-             ComponentMask::ExcNoComponentSelected());
-
-      //
-      // Computing the generalized interpolant isn't quite as straightforward
-      // as for classical Lagrange elements. A major complication is the fact
-      // it generally doesn't hold true that a function evaluates to the same
-      // dof coefficient on different cells. This means *setting* the value
-      // of a (global) degree of freedom computed on one cell doesn't
-      // necessarily lead to the same result when computed on a neighboring
-      // cell (that shares the same global degree of freedom).
-      //
-      // We thus, do the following operation:
-      //
-      // On each cell:
-      //
-      //  - We first determine all function values u(x_i) in generalized
-      //    support points
-      //
-      //  - We transform these function values back to the unit cell
-      //    according to the conformity of the component (scalar, Hdiv, or
-      //    Hcurl conforming); see [Monk, Finite Element Methods for Maxwell's
-      //    Equations, p.77ff Section 3.9] for details. This results in
-      //    \hat u(\hat x_i)
-      //
-      //  - We convert these generalized support point values to nodal values
-      //
-      //  - For every global dof we take the average 1 / n_K \sum_{K} dof_K
-      //    where n_K is the number of cells sharing the global dof and dof_K
-      //    is the computed value on the cell K.
-      //
-      // For every degree of freedom that is shared by k cells, we compute
-      // its value on all k cells and take the weighted average with respect
-      // to the JxW values.
-      //
-
-      using number = typename VectorType::value_type;
-
-      const hp::FECollection<dim, spacedim> &fe(
-        dof_handler.get_fe_collection());
-
-      std::vector<types::global_dof_index> dofs_on_cell(fe.max_dofs_per_cell());
-
-      // Temporary storage for cell-wise interpolation operation. We store a
-      // variant for every fe we encounter to speed up resizing operations.
-      // The first vector is used for local function evaluation. The vector
-      // dof_values is used to store intermediate cell-wise interpolation
-      // results (see the detailed explanation in the for loop further down
-      // below).
-
-      std::vector<std::vector<Vector<number>>> fe_function_values(fe.size());
-      std::vector<std::vector<number>>         fe_dof_values(fe.size());
-
-      // We will need two temporary global vectors that store the new values
-      // and weights.
-      VectorType interpolation;
-      VectorType weights;
-      interpolation.reinit(vec);
-      weights.reinit(vec);
-
-      // Store locally owned dofs, so that we can skip all non-local dofs,
-      // if they do not need to be interpolated.
-      const IndexSet locally_owned_dofs = vec.locally_owned_elements();
-
-      // We use an FEValues object to transform all generalized support
-      // points from the unit cell to the real cell coordinates. Thus,
-      // initialize a quadrature with all generalized support points and
-      // create an FEValues object with it.
-
-      hp::QCollection<dim> support_quadrature;
-      for (unsigned int fe_index = 0; fe_index < fe.size(); ++fe_index)
-        {
-          const auto &points = fe[fe_index].get_generalized_support_points();
-          support_quadrature.push_back(Quadrature<dim>(points));
-        }
-
-      const hp::MappingCollection<dim, spacedim> mapping_collection(mapping);
-
-      // An FEValues object to evaluate (generalized) support point
-      // locations as well as Jacobians and their inverses.
-      // the latter are only needed for Hcurl or Hdiv conforming elements,
-      // but we'll just always include them.
-      hp::FEValues<dim, spacedim> fe_values(mapping_collection,
-                                            fe,
-                                            support_quadrature,
-                                            update_quadrature_points |
-                                              update_jacobians |
-                                              update_inverse_jacobians);
-
-      //
-      // Now loop over all locally owned, active cells.
-      //
-
-      for (const auto &cell : dof_handler.active_cell_iterators())
-        {
-          // If this cell is not locally owned, do nothing.
-          if (!cell->is_locally_owned())
-            continue;
-
-          const unsigned int fe_index = cell->active_fe_index();
-
-          // Do nothing if there are no local degrees of freedom.
-          if (fe[fe_index].dofs_per_cell == 0)
-            continue;
-
-          // Skip processing of the current cell if the function object is
-          // invalid. This is used by interpolate_by_material_id to skip
-          // interpolating over cells with unknown material id.
-          if (!function(cell))
-            continue;
-
-          // Get transformed, generalized support points
-          fe_values.reinit(cell);
-          const std::vector<Point<spacedim>> &generalized_support_points =
-            fe_values.get_present_fe_values().get_quadrature_points();
-
-          // Get indices of the dofs on this cell
-          const auto n_dofs = fe[fe_index].dofs_per_cell;
-          dofs_on_cell.resize(n_dofs);
-          cell->get_dof_indices(dofs_on_cell);
-
-          // Prepare temporary storage
-          auto &function_values = fe_function_values[fe_index];
-          auto &dof_values      = fe_dof_values[fe_index];
-
-          const auto n_components = fe[fe_index].n_components();
-          function_values.resize(generalized_support_points.size(),
-                                 Vector<number>(n_components));
-          dof_values.resize(n_dofs);
-
-          // Get all function values:
-          Assert(
-            n_components == function(cell)->n_components,
-            ExcDimensionMismatch(dof_handler.get_fe_collection().n_components(),
-                                 function(cell)->n_components));
-          function(cell)->vector_value_list(generalized_support_points,
-                                            function_values);
-
-          {
-            // Before we can average, we have to transform all function values
-            // from the real cell back to the unit cell. We query the finite
-            // element for the correct transformation. Matters get a bit more
-            // complicated because we have to apply said transformation for
-            // every base element.
-
-            const unsigned int offset =
-              apply_transform(fe[fe_index],
-                              /* starting_offset = */ 0,
-                              fe_values,
-                              function_values);
-            (void)offset;
-            Assert(offset == n_components, ExcInternalError());
-          }
-
-          FETools::convert_generalized_support_point_values_to_dof_values(
-            fe[fe_index], function_values, dof_values);
-
-          for (unsigned int i = 0; i < n_dofs; ++i)
-            {
-              const auto &nonzero_components =
-                fe[fe_index].get_nonzero_components(i);
-
-              // Figure out whether the component mask applies. We assume
-              // that we are allowed to set degrees of freedom if at least
-              // one of the components (of the dof) is selected.
-              bool selected = false;
-              for (unsigned int c = 0; c < nonzero_components.size(); ++c)
-                selected =
-                  selected || (nonzero_components[c] && component_mask[c]);
-
-              if (selected)
-                {
-#ifdef DEBUG
-                  // make sure that all selected base elements are indeed
-                  // interpolatory
-
-                  if (const auto fe_system =
-                        dynamic_cast<const FESystem<dim> *>(&fe[fe_index]))
-                    {
-                      const auto index =
-                        fe_system->system_to_base_index(i).first.first;
-                      Assert(fe_system->base_element(index)
-                               .has_generalized_support_points(),
-                             ExcMessage("The component mask supplied to "
-                                        "VectorTools::interpolate selects a "
-                                        "non-interpolatory element."));
-                    }
-#endif
-
-                  // Add local values to the global vectors
-                  ::dealii::internal::ElementAccess<VectorType>::add(
-                    dof_values[i], dofs_on_cell[i], interpolation);
-                  ::dealii::internal::ElementAccess<VectorType>::add(
-                    typename VectorType::value_type(1.0),
-                    dofs_on_cell[i],
-                    weights);
-                }
-              else
-                {
-                  // If a component is ignored, copy the dof values
-                  // from the vector "vec", but only if they are locally
-                  // available
-                  if (locally_owned_dofs.is_element(dofs_on_cell[i]))
-                    {
-                      const auto value =
-                        ::dealii::internal::ElementAccess<VectorType>::get(
-                          vec, dofs_on_cell[i]);
-                      ::dealii::internal::ElementAccess<VectorType>::add(
-                        value, dofs_on_cell[i], interpolation);
-                      ::dealii::internal::ElementAccess<VectorType>::add(
-                        typename VectorType::value_type(1.0),
-                        dofs_on_cell[i],
-                        weights);
-                    }
-                }
-            }
-        } /* loop over dof_handler.active_cell_iterators() */
-
-      interpolation.compress(VectorOperation::add);
-      weights.compress(VectorOperation::add);
-
-      for (const auto i : interpolation.locally_owned_elements())
-        {
-          const auto weight =
-            ::dealii::internal::ElementAccess<VectorType>::get(weights, i);
-
-          // See if we touched this DoF at all. If so, set the average
-          // of the value we computed in the output vector. Otherwise,
-          // don't touch the value at all.
-          if (weight != number(0))
-            {
-              const auto value =
-                ::dealii::internal::ElementAccess<VectorType>::get(
-                  interpolation, i);
-              ::dealii::internal::ElementAccess<VectorType>::set(value / weight,
-                                                                 i,
-                                                                 vec);
-            }
-        }
-      vec.compress(VectorOperation::insert);
-    }
-
-  } // namespace internal
-
-
-
-  template <int dim,
-            int spacedim,
-            typename VectorType,
-            template <int, int> class DoFHandlerType>
-  void
-  interpolate(
-    const Mapping<dim, spacedim> &                             mapping,
-    const DoFHandlerType<dim, spacedim> &                      dof_handler,
-    const Function<spacedim, typename VectorType::value_type> &function,
-    VectorType &                                               vec,
-    const ComponentMask &                                      component_mask)
-  {
-    Assert(dof_handler.get_fe_collection().n_components() ==
-             function.n_components,
-           ExcDimensionMismatch(dof_handler.get_fe_collection().n_components(),
-                                function.n_components));
-
-    // Create a small lambda capture wrapping function and call the
-    // internal implementation
-    const auto function_map = [&function](
-      const typename DoFHandlerType<dim, spacedim>::active_cell_iterator &)
-      -> const Function<spacedim, typename VectorType::value_type> *
-    {
-      return &function;
-    };
-
-    internal::interpolate(
-      mapping, dof_handler, function_map, vec, component_mask);
-  }
-
-
-
-  template <int dim,
-            int spacedim,
-            typename VectorType,
-            template <int, int> class DoFHandlerType>
-  void
-  interpolate(
-    const DoFHandlerType<dim, spacedim> &                      dof,
-    const Function<spacedim, typename VectorType::value_type> &function,
-    VectorType &                                               vec,
-    const ComponentMask &                                      component_mask)
-  {
-    interpolate(StaticMappingQ1<dim, spacedim>::mapping,
-                dof,
-                function,
-                vec,
-                component_mask);
-  }
-
-
-
-  template <int dim, class InVector, class OutVector, int spacedim>
-  void
-  interpolate(const DoFHandler<dim, spacedim> &dof_1,
-              const DoFHandler<dim, spacedim> &dof_2,
-              const FullMatrix<double> &       transfer,
-              const InVector &                 data_1,
-              OutVector &                      data_2)
-  {
-    using number = typename OutVector::value_type;
-    Vector<number> cell_data_1(dof_1.get_fe().dofs_per_cell);
-    Vector<number> cell_data_2(dof_2.get_fe().dofs_per_cell);
-
-    // Reset output vector.
-    data_2 = static_cast<number>(0);
-
-    // Store how many cells share each dof (unghosted).
-    OutVector touch_count;
-    touch_count.reinit(data_2);
-
-    std::vector<types::global_dof_index> local_dof_indices(
-      dof_2.get_fe().dofs_per_cell);
-
-    typename DoFHandler<dim, spacedim>::active_cell_iterator cell_1 =
-      dof_1.begin_active();
-    typename DoFHandler<dim, spacedim>::active_cell_iterator cell_2 =
-      dof_2.begin_active();
-    const typename DoFHandler<dim, spacedim>::cell_iterator end_1 = dof_1.end();
-
-    for (; cell_1 != end_1; ++cell_1, ++cell_2)
-      {
-        if (cell_1->is_locally_owned())
-          {
-            Assert(cell_2->is_locally_owned(), ExcInternalError());
-
-            // Perform dof interpolation.
-            cell_1->get_dof_values(data_1, cell_data_1);
-            transfer.vmult(cell_data_2, cell_data_1);
-
-            cell_2->get_dof_indices(local_dof_indices);
-
-            // Distribute cell vector.
-            for (unsigned int j = 0; j < dof_2.get_fe().dofs_per_cell; ++j)
-              {
-                ::dealii::internal::ElementAccess<OutVector>::add(
-                  cell_data_2(j), local_dof_indices[j], data_2);
-
-                // Count cells that share each dof.
-                ::dealii::internal::ElementAccess<OutVector>::add(
-                  static_cast<number>(1), local_dof_indices[j], touch_count);
-              }
-          }
-      }
-
-    // Collect information over all the parallel processes.
-    data_2.compress(VectorOperation::add);
-    touch_count.compress(VectorOperation::add);
-
-    // Compute the mean value of the sum which has been placed in
-    // each entry of the output vector only at locally owned elements.
-    for (const auto &i : data_2.locally_owned_elements())
-      {
-        const number touch_count_i =
-          ::dealii::internal::ElementAccess<OutVector>::get(touch_count, i);
-
-        Assert(touch_count_i != static_cast<number>(0), ExcInternalError());
-
-        const number value =
-          ::dealii::internal::ElementAccess<OutVector>::get(data_2, i) /
-          touch_count_i;
-
-        ::dealii::internal::ElementAccess<OutVector>::set(value, i, data_2);
-      }
-
-    // Compress data_2 to set the proper values on all the parallel processes.
-    data_2.compress(VectorOperation::insert);
-  }
-
-
-
-  template <int dim,
-            int spacedim,
-            typename VectorType,
-            template <int, int> class DoFHandlerType>
-  void
-  interpolate_based_on_material_id(
-    const Mapping<dim, spacedim> &       mapping,
-    const DoFHandlerType<dim, spacedim> &dof_handler,
-    const std::map<types::material_id,
-                   const Function<spacedim, typename VectorType::value_type> *>
-      &                  functions,
-    VectorType &         vec,
-    const ComponentMask &component_mask)
-  {
-    // Create a small lambda capture wrapping the function map and call the
-    // internal implementation
-    const auto function_map = [&functions](
-      const typename DoFHandlerType<dim, spacedim>::active_cell_iterator &cell)
-      -> const Function<spacedim, typename VectorType::value_type> *
-    {
-      const auto function = functions.find(cell->material_id());
-      if (function != functions.end())
-        return function->second;
-      else
-        return nullptr;
-    };
-
-    internal::interpolate(
-      mapping, dof_handler, function_map, vec, component_mask);
-  }
-
-
-  namespace internal
-  {
-    /**
-     * Interpolate zero boundary values. We don't need to worry about a
-     * mapping here because the function we evaluate for the DoFs is zero in
-     * the mapped locations as well as in the original, unmapped locations
-     */
-    template <int dim,
-              int spacedim,
-              template <int, int> class DoFHandlerType,
-              typename number>
-    void
-    interpolate_zero_boundary_values(
-      const DoFHandlerType<dim, spacedim> &      dof_handler,
-      std::map<types::global_dof_index, number> &boundary_values)
-    {
-      // loop over all boundary faces
-      // to get all dof indices of
-      // dofs on the boundary. note
-      // that in 3d there are cases
-      // where a face is not at the
-      // boundary, yet one of its
-      // lines is, and we should
-      // consider the degrees of
-      // freedom on it as boundary
-      // nodes. likewise, in 2d and
-      // 3d there are cases where a
-      // cell is only at the boundary
-      // by one vertex. nevertheless,
-      // since we do not support
-      // boundaries with dimension
-      // less or equal to dim-2, each
-      // such boundary dof is also
-      // found from some other face
-      // that is actually wholly on
-      // the boundary, not only by
-      // one line or one vertex
-      typename DoFHandlerType<dim, spacedim>::active_cell_iterator
-        cell = dof_handler.begin_active(),
-        endc = dof_handler.end();
-      std::vector<types::global_dof_index> face_dof_indices;
-      for (; cell != endc; ++cell)
-        for (auto f : GeometryInfo<dim>::face_indices())
-          if (cell->at_boundary(f))
-            {
-              face_dof_indices.resize(cell->get_fe().dofs_per_face);
-              cell->face(f)->get_dof_indices(face_dof_indices,
-                                             cell->active_fe_index());
-              for (unsigned int i = 0; i < cell->get_fe().dofs_per_face; ++i)
-                // enter zero boundary values
-                // for all boundary nodes
-                //
-                // we need not care about
-                // vector valued elements here,
-                // since we set all components
-                boundary_values[face_dof_indices[i]] = 0.;
-            }
-    }
-  } // namespace internal
-
-
-
-  template <int dim,
-            int spacedim,
-            typename VectorType,
-            template <int, int> class DoFHandlerType>
-  void
-  interpolate_to_different_mesh(const DoFHandlerType<dim, spacedim> &dof1,
-                                const VectorType &                   u1,
-                                const DoFHandlerType<dim, spacedim> &dof2,
-                                VectorType &                         u2)
-  {
-    Assert(GridTools::have_same_coarse_mesh(dof1, dof2),
-           ExcMessage("The two DoF handlers must represent triangulations that "
-                      "have the same coarse meshes"));
-
-    InterGridMap<DoFHandlerType<dim, spacedim>> intergridmap;
-    intergridmap.make_mapping(dof1, dof2);
-
-    AffineConstraints<typename VectorType::value_type> dummy;
-    dummy.close();
-
-    interpolate_to_different_mesh(intergridmap, u1, dummy, u2);
-  }
-
-
-
-  template <int dim,
-            int spacedim,
-            typename VectorType,
-            template <int, int> class DoFHandlerType>
-  void
-  interpolate_to_different_mesh(
-    const DoFHandlerType<dim, spacedim> &                     dof1,
-    const VectorType &                                        u1,
-    const DoFHandlerType<dim, spacedim> &                     dof2,
-    const AffineConstraints<typename VectorType::value_type> &constraints,
-    VectorType &                                              u2)
-  {
-    Assert(GridTools::have_same_coarse_mesh(dof1, dof2),
-           ExcMessage("The two DoF handlers must represent triangulations that "
-                      "have the same coarse meshes"));
-
-    InterGridMap<DoFHandlerType<dim, spacedim>> intergridmap;
-    intergridmap.make_mapping(dof1, dof2);
-
-    interpolate_to_different_mesh(intergridmap, u1, constraints, u2);
-  }
-
-  namespace internal
-  {
-    /**
-     * Return whether the cell and all of its descendants are locally owned.
-     */
-    template <typename cell_iterator>
-    bool
-    is_locally_owned(const cell_iterator &cell)
-    {
-      if (cell->is_active())
-        return cell->is_locally_owned();
-
-      for (unsigned int c = 0; c < cell->n_children(); ++c)
-        if (!is_locally_owned(cell->child(c)))
-          return false;
-
-      return true;
-    }
-  } // namespace internal
-
-  template <int dim,
-            int spacedim,
-            typename VectorType,
-            template <int, int> class DoFHandlerType>
-  void
-  interpolate_to_different_mesh(
-    const InterGridMap<DoFHandlerType<dim, spacedim>> &       intergridmap,
-    const VectorType &                                        u1,
-    const AffineConstraints<typename VectorType::value_type> &constraints,
-    VectorType &                                              u2)
-  {
-    const DoFHandlerType<dim, spacedim> &dof1 = intergridmap.get_source_grid();
-    const DoFHandlerType<dim, spacedim> &dof2 =
-      intergridmap.get_destination_grid();
-    (void)dof2;
-
-    Assert(dof1.get_fe_collection() == dof2.get_fe_collection(),
-           ExcMessage(
-             "The FECollections of both DoFHandler objects must match"));
-    Assert(u1.size() == dof1.n_dofs(),
-           ExcDimensionMismatch(u1.size(), dof1.n_dofs()));
-    Assert(u2.size() == dof2.n_dofs(),
-           ExcDimensionMismatch(u2.size(), dof2.n_dofs()));
-
-    Vector<typename VectorType::value_type> cache;
-
-    // Looping over the finest common
-    // mesh, this means that source and
-    // destination cells have to be on the
-    // same level and at least one has to
-    // be active.
-    //
-    // Therefore, loop over all cells
-    // (active and inactive) of the source
-    // grid ..
-    typename DoFHandlerType<dim, spacedim>::cell_iterator cell1 = dof1.begin();
-    const typename DoFHandlerType<dim, spacedim>::cell_iterator endc1 =
-      dof1.end();
-
-    for (; cell1 != endc1; ++cell1)
-      {
-        const typename DoFHandlerType<dim, spacedim>::cell_iterator cell2 =
-          intergridmap[cell1];
-
-        // .. and skip if source and destination
-        // cells are not on the same level ..
-        if (cell1->level() != cell2->level())
-          continue;
-        // .. or none of them is active.
-        if (!cell1->is_active() && !cell2->is_active())
-          continue;
-
-        Assert(
-          internal::is_locally_owned(cell1) ==
-            internal::is_locally_owned(cell2),
-          ExcMessage(
-            "The two Triangulations are required to have the same parallel partitioning."));
-
-        // Skip foreign cells.
-        if (cell1->is_active() && !cell1->is_locally_owned())
-          continue;
-        if (cell2->is_active() && !cell2->is_locally_owned())
-          continue;
-
-        // Get and set the corresponding
-        // dof_values by interpolation.
-        if (cell1->is_active())
-          {
-            cache.reinit(cell1->get_fe().dofs_per_cell);
-            cell1->get_interpolated_dof_values(u1,
-                                               cache,
-                                               cell1->active_fe_index());
-            cell2->set_dof_values_by_interpolation(cache,
-                                                   u2,
-                                                   cell1->active_fe_index());
-          }
-        else
-          {
-            cache.reinit(cell2->get_fe().dofs_per_cell);
-            cell1->get_interpolated_dof_values(u1,
-                                               cache,
-                                               cell2->active_fe_index());
-            cell2->set_dof_values_by_interpolation(cache,
-                                                   u2,
-                                                   cell2->active_fe_index());
-          }
-      }
-
-    // finish the work on parallel vectors
-    u2.compress(VectorOperation::insert);
-    // Apply hanging node constraints.
-    constraints.distribute(u2);
-  }
-
-  namespace internal
-  {
-    /**
-     * Compute the boundary values to be used in the project() functions.
-     */
-    template <int dim,
-              int spacedim,
-              template <int, int> class DoFHandlerType,
-              template <int, int> class M_or_MC,
-              template <int> class Q_or_QC,
-              typename number>
-    void
-    project_compute_b_v(
-      const M_or_MC<dim, spacedim> &             mapping,
-      const DoFHandlerType<dim, spacedim> &      dof,
-      const Function<spacedim, number> &         function,
-      const bool                                 enforce_zero_boundary,
-      const Q_or_QC<dim - 1> &                   q_boundary,
-      const bool                                 project_to_boundary_first,
-      std::map<types::global_dof_index, number> &boundary_values)
-    {
-      if (enforce_zero_boundary == true)
-        // no need to project boundary
-        // values, but enforce
-        // homogeneous boundary values
-        // anyway
-        interpolate_zero_boundary_values(dof, boundary_values);
-
-      else
-        // no homogeneous boundary values
-        if (project_to_boundary_first == true)
-        // boundary projection required
-        {
-          // set up a list of boundary
-          // functions for the
-          // different boundary
-          // parts. We want the
-          // function to hold on
-          // all parts of the boundary
-          const std::vector<types::boundary_id> used_boundary_ids =
-            dof.get_triangulation().get_boundary_ids();
-
-          std::map<types::boundary_id, const Function<spacedim, number> *>
-            boundary_functions;
-          for (const auto used_boundary_id : used_boundary_ids)
-            boundary_functions[used_boundary_id] = &function;
-          project_boundary_values(
-            mapping, dof, boundary_functions, q_boundary, boundary_values);
-        }
-    }
-
-
-
-    /**
-     * Return whether the boundary values try to constrain a degree of freedom
-     * that is already constrained to something else
-     */
-    template <typename number>
-    bool
-    constraints_and_b_v_are_compatible(
-      const AffineConstraints<number> &          constraints,
-      std::map<types::global_dof_index, number> &boundary_values)
-    {
-      for (const auto &boundary_value : boundary_values)
-        if (constraints.is_constrained(boundary_value.first))
-          // TODO: This looks wrong -- shouldn't it be ==0 in the first
-          // condition and && ?
-          if (!(constraints.get_constraint_entries(boundary_value.first)
-                    ->size() > 0 ||
-                (constraints.get_inhomogeneity(boundary_value.first) ==
-                 boundary_value.second)))
-            return false;
-
-      return true;
-    }
-
-
-
-    template <typename number>
-    void
-    invert_mass_matrix(const SparseMatrix<number> &mass_matrix,
-                       const Vector<number> &      rhs,
-                       Vector<number> &            solution)
-    {
-      // Allow for a maximum of 5*n steps to reduce the residual by 10^-12. n
-      // steps may not be sufficient, since roundoff errors may accumulate for
-      // badly conditioned matrices
-      ReductionControl control(5 * rhs.size(), 0., 1e-12, false, false);
-      GrowingVectorMemory<Vector<number>> memory;
-      SolverCG<Vector<number>>            cg(control, memory);
-
-      PreconditionSSOR<SparseMatrix<number>> prec;
-      prec.initialize(mass_matrix, 1.2);
-
-      cg.solve(mass_matrix, solution, rhs, prec);
-    }
-
-    template <typename number>
-    void
-    invert_mass_matrix(const SparseMatrix<number> & /*mass_matrix*/,
-                       const Vector<std::complex<number>> & /*rhs*/,
-                       Vector<std::complex<number>> & /*solution*/)
-    {
-      Assert(false, ExcNotImplemented());
-    }
-
-
-
-    /**
-     * Generic implementation of the project() function
-     */
-    template <int dim,
-              int spacedim,
-              typename VectorType,
-              template <int, int> class DoFHandlerType,
-              template <int, int> class M_or_MC,
-              template <int> class Q_or_QC>
-    void
-    do_project(
-      const M_or_MC<dim, spacedim> &                             mapping,
-      const DoFHandlerType<dim, spacedim> &                      dof,
-      const AffineConstraints<typename VectorType::value_type> & constraints,
-      const Q_or_QC<dim> &                                       quadrature,
-      const Function<spacedim, typename VectorType::value_type> &function,
-      VectorType &                                               vec_result,
-      const bool              enforce_zero_boundary,
-      const Q_or_QC<dim - 1> &q_boundary,
-      const bool              project_to_boundary_first)
-    {
-      using number = typename VectorType::value_type;
-      Assert(dof.get_fe(0).n_components() == function.n_components,
-             ExcDimensionMismatch(dof.get_fe(0).n_components(),
-                                  function.n_components));
-      Assert(vec_result.size() == dof.n_dofs(),
-             ExcDimensionMismatch(vec_result.size(), dof.n_dofs()));
-
-      // make up boundary values
-      std::map<types::global_dof_index, number> boundary_values;
-      project_compute_b_v(mapping,
-                          dof,
-                          function,
-                          enforce_zero_boundary,
-                          q_boundary,
-                          project_to_boundary_first,
-                          boundary_values);
-
-      // check if constraints are compatible (see below)
-      const bool constraints_are_compatible =
-        constraints_and_b_v_are_compatible<number>(constraints,
-                                                   boundary_values);
-
-      // set up mass matrix and right hand side
-      Vector<number>  vec(dof.n_dofs());
-      SparsityPattern sparsity;
-      {
-        DynamicSparsityPattern dsp(dof.n_dofs(), dof.n_dofs());
-        DoFTools::make_sparsity_pattern(dof,
-                                        dsp,
-                                        constraints,
-                                        !constraints_are_compatible);
-
-        sparsity.copy_from(dsp);
-      }
-      SparseMatrix<number> mass_matrix(sparsity);
-      Vector<number>       tmp(mass_matrix.n());
-
-      // If the constraints object does not conflict with the given boundary
-      // values (i.e., it either does not contain boundary values or it contains
-      // the same as boundary_values), we can let it call
-      // distribute_local_to_global straight away, otherwise we need to first
-      // interpolate the boundary values and then condense the matrix and vector
-      if (constraints_are_compatible)
-        {
-          const Function<spacedim, number> *dummy = nullptr;
-          MatrixCreator::create_mass_matrix(mapping,
-                                            dof,
-                                            quadrature,
-                                            mass_matrix,
-                                            function,
-                                            tmp,
-                                            dummy,
-                                            constraints);
-          if (boundary_values.size() > 0)
-            MatrixTools::apply_boundary_values(
-              boundary_values, mass_matrix, vec, tmp, true);
-        }
-      else
-        {
-          // create mass matrix and rhs at once, which is faster.
-          MatrixCreator::create_mass_matrix(
-            mapping, dof, quadrature, mass_matrix, function, tmp);
-          MatrixTools::apply_boundary_values(
-            boundary_values, mass_matrix, vec, tmp, true);
-          constraints.condense(mass_matrix, tmp);
-        }
-
-      invert_mass_matrix(mass_matrix, tmp, vec);
-      constraints.distribute(vec);
-
-      // copy vec into vec_result. we can't use vec_result itself above, since
-      // it may be of another type than Vector<double> and that wouldn't
-      // necessarily go together with the matrix and other functions
-      for (unsigned int i = 0; i < vec.size(); ++i)
-        ::dealii::internal::ElementAccess<VectorType>::set(vec(i),
-                                                           i,
-                                                           vec_result);
-    }
-
-
-
-    /*
-     * MatrixFree implementation of project() for an arbitrary number of
-     * components and arbitrary degree of the FiniteElement.
-     */
-    template <int components,
-              int fe_degree,
-              int dim,
-              typename Number,
-              int spacedim>
-    void
-    project_matrix_free(
-      const Mapping<dim, spacedim> &   mapping,
-      const DoFHandler<dim, spacedim> &dof,
-      const AffineConstraints<Number> &constraints,
-      const Quadrature<dim> &          quadrature,
-      const Function<
-        spacedim,
-        typename LinearAlgebra::distributed::Vector<Number>::value_type>
-        &                                         function,
-      LinearAlgebra::distributed::Vector<Number> &work_result,
-      const bool                                  enforce_zero_boundary,
-      const Quadrature<dim - 1> &                 q_boundary,
-      const bool                                  project_to_boundary_first)
-    {
-      Assert(project_to_boundary_first == false, ExcNotImplemented());
-      Assert(enforce_zero_boundary == false, ExcNotImplemented());
-      (void)enforce_zero_boundary;
-      (void)project_to_boundary_first;
-      (void)q_boundary;
-
-      Assert(dof.get_fe(0).n_components() == function.n_components,
-             ExcDimensionMismatch(dof.get_fe(0).n_components(),
-                                  function.n_components));
-      Assert(fe_degree == -1 ||
-               dof.get_fe().degree == static_cast<unsigned int>(fe_degree),
-             ExcDimensionMismatch(fe_degree, dof.get_fe().degree));
-      Assert(dof.get_fe(0).n_components() == components,
-             ExcDimensionMismatch(components, dof.get_fe(0).n_components()));
-
-      // set up mass matrix and right hand side
-      typename MatrixFree<dim, Number>::AdditionalData additional_data;
-      additional_data.tasks_parallel_scheme =
-        MatrixFree<dim, Number>::AdditionalData::partition_color;
-      additional_data.mapping_update_flags =
-        (update_values | update_JxW_values);
-      std::shared_ptr<MatrixFree<dim, Number>> matrix_free(
-        new MatrixFree<dim, Number>());
-      matrix_free->reinit(mapping,
-                          dof,
-                          constraints,
-                          QGauss<1>(dof.get_fe().degree + 2),
-                          additional_data);
-      using MatrixType = MatrixFreeOperators::MassOperator<
-        dim,
-        fe_degree,
-        fe_degree + 2,
-        components,
-        LinearAlgebra::distributed::Vector<Number>>;
-      MatrixType mass_matrix;
-      mass_matrix.initialize(matrix_free);
-      mass_matrix.compute_diagonal();
-
-      LinearAlgebra::distributed::Vector<Number> rhs, inhomogeneities;
-      matrix_free->initialize_dof_vector(work_result);
-      matrix_free->initialize_dof_vector(rhs);
-      matrix_free->initialize_dof_vector(inhomogeneities);
-      constraints.distribute(inhomogeneities);
-      inhomogeneities *= -1.;
-
-      {
-        create_right_hand_side(
-          mapping, dof, quadrature, function, rhs, constraints);
-
-        // account for inhomogeneous constraints
-        inhomogeneities.update_ghost_values();
-        FEEvaluation<dim, fe_degree, fe_degree + 2, components, Number> phi(
-          *matrix_free);
-        for (unsigned int cell = 0; cell < matrix_free->n_macro_cells(); ++cell)
-          {
-            phi.reinit(cell);
-            phi.read_dof_values_plain(inhomogeneities);
-            phi.evaluate(true, false);
-            for (unsigned int q = 0; q < phi.n_q_points; ++q)
-              phi.submit_value(phi.get_value(q), q);
-
-            phi.integrate(true, false);
-            phi.distribute_local_to_global(rhs);
-          }
-        rhs.compress(VectorOperation::add);
-      }
-
-      // now invert the matrix
-      // Allow for a maximum of 6*n steps to reduce the residual by 10^-12. n
-      // steps may not be sufficient, since roundoff errors may accumulate for
-      // badly conditioned matrices. This behavior can be observed, e.g. for
-      // FE_Q_Hierarchical for degree higher than three.
-      ReductionControl control(6 * rhs.size(), 0., 1e-12, false, false);
-      SolverCG<LinearAlgebra::distributed::Vector<Number>> cg(control);
-      PreconditionJacobi<MatrixType>                       preconditioner;
-      preconditioner.initialize(mass_matrix, 1.);
-      cg.solve(mass_matrix, work_result, rhs, preconditioner);
-      work_result += inhomogeneities;
-
-      constraints.distribute(work_result);
-    }
-
-
-
-    /**
-     * Helper interface. After figuring out the number of components in
-     * project_matrix_free_component, we determine the degree of the
-     * FiniteElement and call project_matrix_free with the appropriate
-     * template arguments.
-     */
-    template <int components, int dim, typename Number, int spacedim>
-    void
-    project_matrix_free_degree(
-      const Mapping<dim, spacedim> &   mapping,
-      const DoFHandler<dim, spacedim> &dof,
-      const AffineConstraints<Number> &constraints,
-      const Quadrature<dim> &          quadrature,
-      const Function<
-        spacedim,
-        typename LinearAlgebra::distributed::Vector<Number>::value_type>
-        &                                         function,
-      LinearAlgebra::distributed::Vector<Number> &work_result,
-      const bool                                  enforce_zero_boundary,
-      const Quadrature<dim - 1> &                 q_boundary,
-      const bool                                  project_to_boundary_first)
-    {
-      switch (dof.get_fe().degree)
-        {
-          case 1:
-            project_matrix_free<components, 1>(mapping,
-                                               dof,
-                                               constraints,
-                                               quadrature,
-                                               function,
-                                               work_result,
-                                               enforce_zero_boundary,
-                                               q_boundary,
-                                               project_to_boundary_first);
-            break;
-
-          case 2:
-            project_matrix_free<components, 2>(mapping,
-                                               dof,
-                                               constraints,
-                                               quadrature,
-                                               function,
-                                               work_result,
-                                               enforce_zero_boundary,
-                                               q_boundary,
-                                               project_to_boundary_first);
-            break;
-
-          case 3:
-            project_matrix_free<components, 3>(mapping,
-                                               dof,
-                                               constraints,
-                                               quadrature,
-                                               function,
-                                               work_result,
-                                               enforce_zero_boundary,
-                                               q_boundary,
-                                               project_to_boundary_first);
-            break;
-
-          default:
-            project_matrix_free<components, -1>(mapping,
-                                                dof,
-                                                constraints,
-                                                quadrature,
-                                                function,
-                                                work_result,
-                                                enforce_zero_boundary,
-                                                q_boundary,
-                                                project_to_boundary_first);
-        }
-    }
-
-
-
-    // Helper interface for the matrix-free implementation of project().
-    // Used to determine the number of components.
-    template <int dim, typename Number, int spacedim>
-    void
-    project_matrix_free_component(
-      const Mapping<dim, spacedim> &   mapping,
-      const DoFHandler<dim, spacedim> &dof,
-      const AffineConstraints<Number> &constraints,
-      const Quadrature<dim> &          quadrature,
-      const Function<
-        spacedim,
-        typename LinearAlgebra::distributed::Vector<Number>::value_type>
-        &                                         function,
-      LinearAlgebra::distributed::Vector<Number> &work_result,
-      const bool                                  enforce_zero_boundary,
-      const Quadrature<dim - 1> &                 q_boundary,
-      const bool                                  project_to_boundary_first)
-    {
-      switch (dof.get_fe(0).n_components())
-        {
-          case 1:
-            project_matrix_free_degree<1>(mapping,
-                                          dof,
-                                          constraints,
-                                          quadrature,
-                                          function,
-                                          work_result,
-                                          enforce_zero_boundary,
-                                          q_boundary,
-                                          project_to_boundary_first);
-            break;
-
-          case 2:
-            project_matrix_free_degree<2>(mapping,
-                                          dof,
-                                          constraints,
-                                          quadrature,
-                                          function,
-                                          work_result,
-                                          enforce_zero_boundary,
-                                          q_boundary,
-                                          project_to_boundary_first);
-            break;
-
-          case 3:
-            project_matrix_free_degree<3>(mapping,
-                                          dof,
-                                          constraints,
-                                          quadrature,
-                                          function,
-                                          work_result,
-                                          enforce_zero_boundary,
-                                          q_boundary,
-                                          project_to_boundary_first);
-            break;
-
-          case 4:
-            project_matrix_free_degree<4>(mapping,
-                                          dof,
-                                          constraints,
-                                          quadrature,
-                                          function,
-                                          work_result,
-                                          enforce_zero_boundary,
-                                          q_boundary,
-                                          project_to_boundary_first);
-            break;
-
-          default:
-            Assert(false, ExcInternalError());
-        }
-    }
-
-
-
-    /**
-     * Helper interface for the matrix-free implementation of project(): avoid
-     * instantiating the other helper functions for more than one VectorType
-     * by copying from a LinearAlgebra::distributed::Vector.
-     */
-    template <int dim, typename VectorType, int spacedim>
-    void
-    project_matrix_free_copy_vector(
-      const Mapping<dim, spacedim> &                             mapping,
-      const DoFHandler<dim, spacedim> &                          dof,
-      const AffineConstraints<typename VectorType::value_type> & constraints,
-      const Quadrature<dim> &                                    quadrature,
-      const Function<spacedim, typename VectorType::value_type> &function,
-      VectorType &                                               vec_result,
-      const bool                 enforce_zero_boundary,
-      const Quadrature<dim - 1> &q_boundary,
-      const bool                 project_to_boundary_first)
-    {
-      Assert(vec_result.size() == dof.n_dofs(),
-             ExcDimensionMismatch(vec_result.size(), dof.n_dofs()));
-
-      LinearAlgebra::distributed::Vector<typename VectorType::value_type>
-        work_result;
-      project_matrix_free_component(mapping,
-                                    dof,
-                                    constraints,
-                                    quadrature,
-                                    function,
-                                    work_result,
-                                    enforce_zero_boundary,
-                                    q_boundary,
-                                    project_to_boundary_first);
-
-      const IndexSet &          locally_owned_dofs = dof.locally_owned_dofs();
-      IndexSet::ElementIterator it                 = locally_owned_dofs.begin();
-      for (; it != locally_owned_dofs.end(); ++it)
-        ::dealii::internal::ElementAccess<VectorType>::set(work_result(*it),
-                                                           *it,
-                                                           vec_result);
-      vec_result.compress(VectorOperation::insert);
-    }
-
-
-
-    /**
-     * Specialization of project() for the case dim==spacedim.
-     * Check if we can use the MatrixFree implementation or need
-     * to use the matrix based one.
-     */
-    template <typename VectorType, int dim>
-    void
-    project(
-      const Mapping<dim> &                                      mapping,
-      const DoFHandler<dim> &                                   dof,
-      const AffineConstraints<typename VectorType::value_type> &constraints,
-      const Quadrature<dim> &                                   quadrature,
-      const Function<dim, typename VectorType::value_type> &    function,
-      VectorType &                                              vec_result,
-      const bool                 enforce_zero_boundary,
-      const Quadrature<dim - 1> &q_boundary,
-      const bool                 project_to_boundary_first)
-    {
-      // If we can, use the matrix-free implementation
-      bool use_matrix_free =
-        MatrixFree<dim, typename VectorType::value_type>::is_supported(
-          dof.get_fe());
-
-      // enforce_zero_boundary and project_to_boundary_first
-      // are not yet supported.
-      // We have explicit instantiations only if
-      // the number of components is not too high.
-      if (enforce_zero_boundary || project_to_boundary_first ||
-          dof.get_fe(0).n_components() > 4)
-        use_matrix_free = false;
-
-      if (use_matrix_free)
-        project_matrix_free_copy_vector(mapping,
-                                        dof,
-                                        constraints,
-                                        quadrature,
-                                        function,
-                                        vec_result,
-                                        enforce_zero_boundary,
-                                        q_boundary,
-                                        project_to_boundary_first);
-      else
-        {
-          Assert((dynamic_cast<const parallel::TriangulationBase<dim> *>(
-                    &(dof.get_triangulation())) == nullptr),
-                 ExcNotImplemented());
-          do_project(mapping,
-                     dof,
-                     constraints,
-                     quadrature,
-                     function,
-                     vec_result,
-                     enforce_zero_boundary,
-                     q_boundary,
-                     project_to_boundary_first);
-        }
-    }
-
-
-
-    template <int dim, typename VectorType, int spacedim, int fe_degree>
-    void
-    project_parallel(
-      const Mapping<dim, spacedim> &                            mapping,
-      const DoFHandler<dim, spacedim> &                         dof,
-      const AffineConstraints<typename VectorType::value_type> &constraints,
-      const Quadrature<dim> &                                   quadrature,
-      const std::function<typename VectorType::value_type(
-        const typename DoFHandler<dim, spacedim>::active_cell_iterator &,
-        const unsigned int)> &                                  func,
-      VectorType &                                              vec_result)
-    {
-      using Number = typename VectorType::value_type;
-      Assert(dof.get_fe(0).n_components() == 1,
-             ExcDimensionMismatch(dof.get_fe(0).n_components(), 1));
-      Assert(vec_result.size() == dof.n_dofs(),
-             ExcDimensionMismatch(vec_result.size(), dof.n_dofs()));
-      Assert(fe_degree == -1 ||
-               dof.get_fe().degree == static_cast<unsigned int>(fe_degree),
-             ExcDimensionMismatch(fe_degree, dof.get_fe().degree));
-
-      // set up mass matrix and right hand side
-      typename MatrixFree<dim, Number>::AdditionalData additional_data;
-      additional_data.tasks_parallel_scheme =
-        MatrixFree<dim, Number>::AdditionalData::partition_color;
-      additional_data.mapping_update_flags =
-        (update_values | update_JxW_values);
-      std::shared_ptr<MatrixFree<dim, Number>> matrix_free(
-        new MatrixFree<dim, Number>());
-      matrix_free->reinit(mapping,
-                          dof,
-                          constraints,
-                          QGauss<1>(dof.get_fe().degree + 2),
-                          additional_data);
-      using MatrixType = MatrixFreeOperators::MassOperator<
-        dim,
-        fe_degree,
-        fe_degree + 2,
-        1,
-        LinearAlgebra::distributed::Vector<Number>>;
-      MatrixType mass_matrix;
-      mass_matrix.initialize(matrix_free);
-      mass_matrix.compute_diagonal();
-
-      using LocalVectorType = LinearAlgebra::distributed::Vector<Number>;
-      LocalVectorType vec, rhs, inhomogeneities;
-      matrix_free->initialize_dof_vector(vec);
-      matrix_free->initialize_dof_vector(rhs);
-      matrix_free->initialize_dof_vector(inhomogeneities);
-      constraints.distribute(inhomogeneities);
-      inhomogeneities *= -1.;
-
-      // assemble right hand side:
-      {
-        FEValues<dim> fe_values(mapping,
-                                dof.get_fe(),
-                                quadrature,
-                                update_values | update_JxW_values);
-
-        const unsigned int dofs_per_cell = dof.get_fe().dofs_per_cell;
-        const unsigned int n_q_points    = quadrature.size();
-        Vector<Number>     cell_rhs(dofs_per_cell);
-        std::vector<types::global_dof_index> local_dof_indices(dofs_per_cell);
-        typename DoFHandler<dim, spacedim>::active_cell_iterator
-          cell = dof.begin_active(),
-          endc = dof.end();
-        for (; cell != endc; ++cell)
-          if (cell->is_locally_owned())
-            {
-              cell_rhs = 0;
-              fe_values.reinit(cell);
-              for (unsigned int q_point = 0; q_point < n_q_points; ++q_point)
-                {
-                  const double val_q = func(cell, q_point);
-                  for (unsigned int i = 0; i < dofs_per_cell; ++i)
-                    cell_rhs(i) += (fe_values.shape_value(i, q_point) * val_q *
-                                    fe_values.JxW(q_point));
-                }
-
-              cell->get_dof_indices(local_dof_indices);
-              constraints.distribute_local_to_global(cell_rhs,
-                                                     local_dof_indices,
-                                                     rhs);
-            }
-        rhs.compress(VectorOperation::add);
-      }
-
-      mass_matrix.vmult_add(rhs, inhomogeneities);
-
-      // now invert the matrix
-      // Allow for a maximum of 5*n steps to reduce the residual by 10^-12. n
-      // steps may not be sufficient, since roundoff errors may accumulate for
-      // badly conditioned matrices. This behavior can be observed, e.g. for
-      // FE_Q_Hierarchical for degree higher than three.
-      ReductionControl control(5 * rhs.size(), 0., 1e-12, false, false);
-      SolverCG<LinearAlgebra::distributed::Vector<Number>>    cg(control);
-      typename PreconditionJacobi<MatrixType>::AdditionalData data(0.8);
-      PreconditionJacobi<MatrixType>                          preconditioner;
-      preconditioner.initialize(mass_matrix, data);
-      cg.solve(mass_matrix, vec, rhs, preconditioner);
-      vec += inhomogeneities;
-
-      constraints.distribute(vec);
-
-      const IndexSet &          locally_owned_dofs = dof.locally_owned_dofs();
-      IndexSet::ElementIterator it                 = locally_owned_dofs.begin();
-      for (; it != locally_owned_dofs.end(); ++it)
-        ::dealii::internal::ElementAccess<VectorType>::set(vec(*it),
-                                                           *it,
-                                                           vec_result);
-      vec_result.compress(VectorOperation::insert);
-    }
-
-
-
-    template <int dim,
-              typename VectorType,
-              int spacedim,
-              int fe_degree,
-              int n_q_points_1d>
-    void
-    project_parallel(
-      std::shared_ptr<const MatrixFree<dim, typename VectorType::value_type>>
-                                                                matrix_free,
-      const AffineConstraints<typename VectorType::value_type> &constraints,
-      const std::function<VectorizedArray<typename VectorType::value_type>(
-        const unsigned int,
-        const unsigned int)> &                                  func,
-      VectorType &                                              vec_result,
-      const unsigned int                                        fe_component)
-    {
-      const DoFHandler<dim, spacedim> &dof =
-        matrix_free->get_dof_handler(fe_component);
-
-      using Number = typename VectorType::value_type;
-      Assert(dof.get_fe(0).n_components() == 1,
-             ExcDimensionMismatch(dof.get_fe(0).n_components(), 1));
-      Assert(vec_result.size() == dof.n_dofs(),
-             ExcDimensionMismatch(vec_result.size(), dof.n_dofs()));
-      Assert(fe_degree == -1 ||
-               dof.get_fe().degree == static_cast<unsigned int>(fe_degree),
-             ExcDimensionMismatch(fe_degree, dof.get_fe().degree));
-
-      using MatrixType = MatrixFreeOperators::MassOperator<
-        dim,
-        fe_degree,
-        n_q_points_1d,
-        1,
-        LinearAlgebra::distributed::Vector<Number>>;
-      MatrixType mass_matrix;
-      mass_matrix.initialize(matrix_free, {fe_component});
-      mass_matrix.compute_diagonal();
-
-      using LocalVectorType = LinearAlgebra::distributed::Vector<Number>;
-      LocalVectorType vec, rhs, inhomogeneities;
-      matrix_free->initialize_dof_vector(vec, fe_component);
-      matrix_free->initialize_dof_vector(rhs, fe_component);
-      matrix_free->initialize_dof_vector(inhomogeneities, fe_component);
-      constraints.distribute(inhomogeneities);
-      inhomogeneities *= -1.;
-
-      // assemble right hand side:
-      {
-        FEEvaluation<dim, fe_degree, n_q_points_1d, 1, Number> fe_eval(
-          *matrix_free, fe_component);
-        const unsigned int n_cells    = matrix_free->n_macro_cells();
-        const unsigned int n_q_points = fe_eval.n_q_points;
-
-        for (unsigned int cell = 0; cell < n_cells; ++cell)
-          {
-            fe_eval.reinit(cell);
-            for (unsigned int q = 0; q < n_q_points; ++q)
-              fe_eval.submit_value(func(cell, q), q);
-
-            fe_eval.integrate(true, false);
-            fe_eval.distribute_local_to_global(rhs);
-          }
-        rhs.compress(VectorOperation::add);
-      }
-
-      mass_matrix.vmult_add(rhs, inhomogeneities);
-
-      // now invert the matrix
-      // Allow for a maximum of 5*n steps to reduce the residual by 10^-12. n
-      // steps may not be sufficient, since roundoff errors may accumulate for
-      // badly conditioned matrices. This behavior can be observed, e.g. for
-      // FE_Q_Hierarchical for degree higher than three.
-      ReductionControl control(5 * rhs.size(), 0., 1e-12, false, false);
-      SolverCG<LinearAlgebra::distributed::Vector<Number>>    cg(control);
-      typename PreconditionJacobi<MatrixType>::AdditionalData data(0.8);
-      PreconditionJacobi<MatrixType>                          preconditioner;
-      preconditioner.initialize(mass_matrix, data);
-      cg.solve(mass_matrix, vec, rhs, preconditioner);
-      vec += inhomogeneities;
-
-      constraints.distribute(vec);
-
-      const IndexSet &          locally_owned_dofs = dof.locally_owned_dofs();
-      IndexSet::ElementIterator it                 = locally_owned_dofs.begin();
-      for (; it != locally_owned_dofs.end(); ++it)
-        ::dealii::internal::ElementAccess<VectorType>::set(vec(*it),
-                                                           *it,
-                                                           vec_result);
-      vec_result.compress(VectorOperation::insert);
-    }
-  } // namespace internal
-
-
-
-  template <int dim, typename VectorType, int spacedim>
-  void
-  project(const Mapping<dim, spacedim> &                            mapping,
-          const DoFHandler<dim, spacedim> &                         dof,
-          const AffineConstraints<typename VectorType::value_type> &constraints,
-          const Quadrature<dim> &                                   quadrature,
-          const std::function<typename VectorType::value_type(
-            const typename DoFHandler<dim, spacedim>::active_cell_iterator &,
-            const unsigned int)> &                                  func,
-          VectorType &                                              vec_result)
-  {
-    switch (dof.get_fe().degree)
-      {
-        case 1:
-          internal::project_parallel<dim, VectorType, spacedim, 1>(
-            mapping, dof, constraints, quadrature, func, vec_result);
-          break;
-        case 2:
-          internal::project_parallel<dim, VectorType, spacedim, 2>(
-            mapping, dof, constraints, quadrature, func, vec_result);
-          break;
-        case 3:
-          internal::project_parallel<dim, VectorType, spacedim, 3>(
-            mapping, dof, constraints, quadrature, func, vec_result);
-          break;
-        default:
-          internal::project_parallel<dim, VectorType, spacedim, -1>(
-            mapping, dof, constraints, quadrature, func, vec_result);
-      }
-  }
-
-
-
-  template <int dim, typename VectorType>
-  void
-  project(std::shared_ptr<const MatrixFree<
-            dim,
-            typename VectorType::value_type,
-            VectorizedArray<typename VectorType::value_type>>>      matrix_free,
-          const AffineConstraints<typename VectorType::value_type> &constraints,
-          const unsigned int      n_q_points_1d,
-          const std::function<VectorizedArray<typename VectorType::value_type>(
-            const unsigned int,
-            const unsigned int)> &func,
-          VectorType &            vec_result,
-          const unsigned int      fe_component)
-  {
-    const unsigned int fe_degree =
-      matrix_free->get_dof_handler(fe_component).get_fe().degree;
-
-    if (fe_degree + 1 == n_q_points_1d)
-      switch (fe_degree)
-        {
-          case 1:
-            internal::project_parallel<dim, VectorType, dim, 1, 2>(
-              matrix_free, constraints, func, vec_result, fe_component);
-            break;
-          case 2:
-            internal::project_parallel<dim, VectorType, dim, 2, 3>(
-              matrix_free, constraints, func, vec_result, fe_component);
-            break;
-          case 3:
-            internal::project_parallel<dim, VectorType, dim, 3, 4>(
-              matrix_free, constraints, func, vec_result, fe_component);
-            break;
-          default:
-            internal::project_parallel<dim, VectorType, dim, -1, 0>(
-              matrix_free, constraints, func, vec_result, fe_component);
-        }
-    else
-      internal::project_parallel<dim, VectorType, dim, -1, 0>(
-        matrix_free, constraints, func, vec_result, fe_component);
-  }
-
-
-
-  template <int dim, typename VectorType>
-  void
-  project(std::shared_ptr<const MatrixFree<
-            dim,
-            typename VectorType::value_type,
-            VectorizedArray<typename VectorType::value_type>>>      matrix_free,
-          const AffineConstraints<typename VectorType::value_type> &constraints,
-          const std::function<VectorizedArray<typename VectorType::value_type>(
-            const unsigned int,
-            const unsigned int)> &                                  func,
-          VectorType &                                              vec_result,
-          const unsigned int fe_component)
-  {
-    project(matrix_free,
-            constraints,
-            matrix_free->get_dof_handler(fe_component).get_fe().degree + 1,
-            func,
-            vec_result,
-            fe_component);
-  }
-
-
-
-  template <int dim, typename VectorType, int spacedim>
-  void
-  project(const Mapping<dim, spacedim> &                            mapping,
-          const DoFHandler<dim, spacedim> &                         dof,
-          const AffineConstraints<typename VectorType::value_type> &constraints,
-          const Quadrature<dim> &                                   quadrature,
-          const Function<spacedim, typename VectorType::value_type> &function,
-          VectorType &                                               vec_result,
-          const bool                 enforce_zero_boundary,
-          const Quadrature<dim - 1> &q_boundary,
-          const bool                 project_to_boundary_first)
-  {
-    if (dim == spacedim)
-      {
-        const Mapping<dim> *const mapping_ptr =
-          dynamic_cast<const Mapping<dim> *>(&mapping);
-        const DoFHandler<dim> *const dof_ptr =
-          dynamic_cast<const DoFHandler<dim> *>(&dof);
-        const Function<dim,
-                       typename VectorType::value_type> *const function_ptr =
-          dynamic_cast<const Function<dim, typename VectorType::value_type> *>(
-            &function);
-        Assert(mapping_ptr != nullptr, ExcInternalError());
-        Assert(dof_ptr != nullptr, ExcInternalError());
-        internal::project<VectorType, dim>(*mapping_ptr,
-                                           *dof_ptr,
-                                           constraints,
-                                           quadrature,
-                                           *function_ptr,
-                                           vec_result,
-                                           enforce_zero_boundary,
-                                           q_boundary,
-                                           project_to_boundary_first);
-      }
-    else
-      {
-        Assert(
-          (dynamic_cast<const parallel::TriangulationBase<dim, spacedim> *>(
-             &(dof.get_triangulation())) == nullptr),
-          ExcNotImplemented());
-        internal::do_project(mapping,
-                             dof,
-                             constraints,
-                             quadrature,
-                             function,
-                             vec_result,
-                             enforce_zero_boundary,
-                             q_boundary,
-                             project_to_boundary_first);
-      }
-  }
-
-
-
-  template <int dim, typename VectorType, int spacedim>
-  void
-  project(const DoFHandler<dim, spacedim> &                         dof,
-          const AffineConstraints<typename VectorType::value_type> &constraints,
-          const Quadrature<dim> &                                   quadrature,
-          const Function<spacedim, typename VectorType::value_type> &function,
-          VectorType &                                               vec,
-          const bool                 enforce_zero_boundary,
-          const Quadrature<dim - 1> &q_boundary,
-          const bool                 project_to_boundary_first)
-  {
-#ifdef _MSC_VER
-    Assert(false,
-           ExcMessage("Please specify the mapping explicitly "
-                      "when building with MSVC!"));
-#else
-    project(StaticMappingQ1<dim, spacedim>::mapping,
-            dof,
-            constraints,
-            quadrature,
-            function,
-            vec,
-            enforce_zero_boundary,
-            q_boundary,
-            project_to_boundary_first);
-#endif
-  }
-
-
-
-  template <int dim, typename VectorType, int spacedim>
-  void
-  project(const hp::MappingCollection<dim, spacedim> &              mapping,
-          const hp::DoFHandler<dim, spacedim> &                     dof,
-          const AffineConstraints<typename VectorType::value_type> &constraints,
-          const hp::QCollection<dim> &                              quadrature,
-          const Function<spacedim, typename VectorType::value_type> &function,
-          VectorType &                                               vec_result,
-          const bool                      enforce_zero_boundary,
-          const hp::QCollection<dim - 1> &q_boundary,
-          const bool                      project_to_boundary_first)
-  {
-    Assert((dynamic_cast<const parallel::TriangulationBase<dim, spacedim> *>(
-              &(dof.get_triangulation())) == nullptr),
-           ExcNotImplemented());
-
-    internal::do_project(mapping,
-                         dof,
-                         constraints,
-                         quadrature,
-                         function,
-                         vec_result,
-                         enforce_zero_boundary,
-                         q_boundary,
-                         project_to_boundary_first);
-  }
-
-
-  template <int dim, typename VectorType, int spacedim>
-  void
-  project(const hp::DoFHandler<dim, spacedim> &                     dof,
-          const AffineConstraints<typename VectorType::value_type> &constraints,
-          const hp::QCollection<dim> &                              quadrature,
-          const Function<spacedim, typename VectorType::value_type> &function,
-          VectorType &                                               vec,
-          const bool                      enforce_zero_boundary,
-          const hp::QCollection<dim - 1> &q_boundary,
-          const bool                      project_to_boundary_first)
-  {
-    project(hp::StaticMappingQ1<dim, spacedim>::mapping_collection,
-            dof,
-            constraints,
-            quadrature,
-            function,
-            vec,
-            enforce_zero_boundary,
-            q_boundary,
-            project_to_boundary_first);
-  }
-
-
-
-  template <int dim, int spacedim, typename VectorType>
-  void
-  create_right_hand_side(
-    const Mapping<dim, spacedim> &                             mapping,
-    const DoFHandler<dim, spacedim> &                          dof_handler,
-    const Quadrature<dim> &                                    quadrature,
-    const Function<spacedim, typename VectorType::value_type> &rhs_function,
-    VectorType &                                               rhs_vector,
-    const AffineConstraints<typename VectorType::value_type> & constraints)
-  {
-    using Number = typename VectorType::value_type;
-
-    const FiniteElement<dim, spacedim> &fe = dof_handler.get_fe();
-    Assert(fe.n_components() == rhs_function.n_components,
-           ExcDimensionMismatch(fe.n_components(), rhs_function.n_components));
-    Assert(rhs_vector.size() == dof_handler.n_dofs(),
-           ExcDimensionMismatch(rhs_vector.size(), dof_handler.n_dofs()));
-    rhs_vector = typename VectorType::value_type(0.);
-
-    UpdateFlags update_flags =
-      UpdateFlags(update_values | update_quadrature_points | update_JxW_values);
-    FEValues<dim, spacedim> fe_values(mapping, fe, quadrature, update_flags);
-
-    const unsigned int dofs_per_cell = fe_values.dofs_per_cell,
-                       n_q_points    = fe_values.n_quadrature_points,
-                       n_components  = fe.n_components();
-
-    std::vector<types::global_dof_index> dofs(dofs_per_cell);
-    Vector<Number>                       cell_vector(dofs_per_cell);
-
-    typename DoFHandler<dim, spacedim>::active_cell_iterator
-      cell = dof_handler.begin_active(),
-      endc = dof_handler.end();
-
-    if (n_components == 1)
-      {
-        std::vector<Number> rhs_values(n_q_points);
-
-        for (; cell != endc; ++cell)
-          if (cell->is_locally_owned())
-            {
-              fe_values.reinit(cell);
-
-              const std::vector<double> &weights = fe_values.get_JxW_values();
-              rhs_function.value_list(fe_values.get_quadrature_points(),
-                                      rhs_values);
-
-              cell_vector = 0;
-              for (unsigned int point = 0; point < n_q_points; ++point)
-                for (unsigned int i = 0; i < dofs_per_cell; ++i)
-                  cell_vector(i) += rhs_values[point] *
-                                    fe_values.shape_value(i, point) *
-                                    weights[point];
-
-              cell->get_dof_indices(dofs);
-
-              constraints.distribute_local_to_global(cell_vector,
-                                                     dofs,
-                                                     rhs_vector);
-            }
-      }
-    else
-      {
-        std::vector<Vector<Number>> rhs_values(n_q_points,
-                                               Vector<Number>(n_components));
-
-        for (; cell != endc; ++cell)
-          if (cell->is_locally_owned())
-            {
-              fe_values.reinit(cell);
-
-              const std::vector<double> &weights = fe_values.get_JxW_values();
-              rhs_function.vector_value_list(fe_values.get_quadrature_points(),
-                                             rhs_values);
-
-              cell_vector = 0;
-              // Use the faster code if the
-              // FiniteElement is primitive
-              if (fe.is_primitive())
-                {
-                  for (unsigned int point = 0; point < n_q_points; ++point)
-                    for (unsigned int i = 0; i < dofs_per_cell; ++i)
-                      {
-                        const unsigned int component =
-                          fe.system_to_component_index(i).first;
-
-                        cell_vector(i) += rhs_values[point](component) *
-                                          fe_values.shape_value(i, point) *
-                                          weights[point];
-                      }
-                }
-              else
-                {
-                  // Otherwise do it the way
-                  // proposed for vector valued
-                  // elements
-                  for (unsigned int point = 0; point < n_q_points; ++point)
-                    for (unsigned int i = 0; i < dofs_per_cell; ++i)
-                      for (unsigned int comp_i = 0; comp_i < n_components;
-                           ++comp_i)
-                        if (fe.get_nonzero_components(i)[comp_i])
-                          {
-                            cell_vector(i) +=
-                              rhs_values[point](comp_i) *
-                              fe_values.shape_value_component(i,
-                                                              point,
-                                                              comp_i) *
-                              weights[point];
-                          }
-                }
-              cell->get_dof_indices(dofs);
-
-              constraints.distribute_local_to_global(cell_vector,
-                                                     dofs,
-                                                     rhs_vector);
-            }
-      }
-  }
-
-
-
-  template <int dim, int spacedim, typename VectorType>
-  void
-  create_right_hand_side(
-    const DoFHandler<dim, spacedim> &                          dof_handler,
-    const Quadrature<dim> &                                    quadrature,
-    const Function<spacedim, typename VectorType::value_type> &rhs_function,
-    VectorType &                                               rhs_vector,
-    const AffineConstraints<typename VectorType::value_type> & constraints)
-  {
-    create_right_hand_side(StaticMappingQ1<dim, spacedim>::mapping,
-                           dof_handler,
-                           quadrature,
-                           rhs_function,
-                           rhs_vector,
-                           constraints);
-  }
-
-
-
-  template <int dim, int spacedim, typename VectorType>
-  void
-  create_right_hand_side(
-    const hp::MappingCollection<dim, spacedim> &               mapping,
-    const hp::DoFHandler<dim, spacedim> &                      dof_handler,
-    const hp::QCollection<dim> &                               quadrature,
-    const Function<spacedim, typename VectorType::value_type> &rhs_function,
-    VectorType &                                               rhs_vector,
-    const AffineConstraints<typename VectorType::value_type> & constraints)
-  {
-    using Number = typename VectorType::value_type;
-
-    const hp::FECollection<dim, spacedim> &fe = dof_handler.get_fe_collection();
-    Assert(fe.n_components() == rhs_function.n_components,
-           ExcDimensionMismatch(fe.n_components(), rhs_function.n_components));
-    Assert(rhs_vector.size() == dof_handler.n_dofs(),
-           ExcDimensionMismatch(rhs_vector.size(), dof_handler.n_dofs()));
-    rhs_vector = 0;
-
-    UpdateFlags update_flags =
-      UpdateFlags(update_values | update_quadrature_points | update_JxW_values);
-    hp::FEValues<dim, spacedim> x_fe_values(mapping,
-                                            fe,
-                                            quadrature,
-                                            update_flags);
-
-    const unsigned int n_components = fe.n_components();
-
-    std::vector<types::global_dof_index> dofs(fe.max_dofs_per_cell());
-    Vector<Number>                       cell_vector(fe.max_dofs_per_cell());
-
-    typename hp::DoFHandler<dim, spacedim>::active_cell_iterator
-      cell = dof_handler.begin_active(),
-      endc = dof_handler.end();
-
-    if (n_components == 1)
-      {
-        std::vector<Number> rhs_values;
-
-        for (; cell != endc; ++cell)
-          if (cell->is_locally_owned())
-            {
-              x_fe_values.reinit(cell);
-
-              const FEValues<dim, spacedim> &fe_values =
-                x_fe_values.get_present_fe_values();
-
-              const unsigned int dofs_per_cell = fe_values.dofs_per_cell,
-                                 n_q_points    = fe_values.n_quadrature_points;
-              rhs_values.resize(n_q_points);
-              dofs.resize(dofs_per_cell);
-              cell_vector.reinit(dofs_per_cell);
-
-              const std::vector<Number> &weights = fe_values.get_JxW_values();
-              rhs_function.value_list(fe_values.get_quadrature_points(),
-                                      rhs_values);
-
-              cell_vector = 0;
-              for (unsigned int point = 0; point < n_q_points; ++point)
-                for (unsigned int i = 0; i < dofs_per_cell; ++i)
-                  cell_vector(i) += rhs_values[point] *
-                                    fe_values.shape_value(i, point) *
-                                    weights[point];
-
-              cell->get_dof_indices(dofs);
-
-              constraints.distribute_local_to_global(cell_vector,
-                                                     dofs,
-                                                     rhs_vector);
-            }
-      }
-    else
-      {
-        std::vector<Vector<Number>> rhs_values;
-
-        for (; cell != endc; ++cell)
-          if (cell->is_locally_owned())
-            {
-              x_fe_values.reinit(cell);
-
-              const FEValues<dim, spacedim> &fe_values =
-                x_fe_values.get_present_fe_values();
-
-              const unsigned int dofs_per_cell = fe_values.dofs_per_cell,
-                                 n_q_points    = fe_values.n_quadrature_points;
-              rhs_values.resize(n_q_points, Vector<Number>(n_components));
-              dofs.resize(dofs_per_cell);
-              cell_vector.reinit(dofs_per_cell);
-
-              const std::vector<Number> &weights = fe_values.get_JxW_values();
-              rhs_function.vector_value_list(fe_values.get_quadrature_points(),
-                                             rhs_values);
-
-              cell_vector = 0;
-
-              // Use the faster code if the
-              // FiniteElement is primitive
-              if (cell->get_fe().is_primitive())
-                {
-                  for (unsigned int point = 0; point < n_q_points; ++point)
-                    for (unsigned int i = 0; i < dofs_per_cell; ++i)
-                      {
-                        const unsigned int component =
-                          cell->get_fe().system_to_component_index(i).first;
-
-                        cell_vector(i) += rhs_values[point](component) *
-                                          fe_values.shape_value(i, point) *
-                                          weights[point];
-                      }
-                }
-              else
-                {
-                  // Otherwise do it the way proposed
-                  // for vector valued elements
-                  for (unsigned int point = 0; point < n_q_points; ++point)
-                    for (unsigned int i = 0; i < dofs_per_cell; ++i)
-                      for (unsigned int comp_i = 0; comp_i < n_components;
-                           ++comp_i)
-                        if (cell->get_fe().get_nonzero_components(i)[comp_i])
-                          {
-                            cell_vector(i) +=
-                              rhs_values[point](comp_i) *
-                              fe_values.shape_value_component(i,
-                                                              point,
-                                                              comp_i) *
-                              weights[point];
-                          }
-                }
-
-              cell->get_dof_indices(dofs);
-
-              constraints.distribute_local_to_global(cell_vector,
-                                                     dofs,
-                                                     rhs_vector);
-            }
-      }
-  }
-
-
-
-  template <int dim, int spacedim, typename VectorType>
-  void
-  create_right_hand_side(
-    const hp::DoFHandler<dim, spacedim> &                      dof_handler,
-    const hp::QCollection<dim> &                               quadrature,
-    const Function<spacedim, typename VectorType::value_type> &rhs_function,
-    VectorType &                                               rhs_vector,
-    const AffineConstraints<typename VectorType::value_type> & constraints)
-  {
-    create_right_hand_side(
-      hp::StaticMappingQ1<dim, spacedim>::mapping_collection,
-      dof_handler,
-      quadrature,
-      rhs_function,
-      rhs_vector,
-      constraints);
-  }
-
-
-
-  template <int dim, int spacedim>
-  void
-  create_point_source_vector(const Mapping<dim, spacedim> &   mapping,
-                             const DoFHandler<dim, spacedim> &dof_handler,
-                             const Point<spacedim> &          p,
-                             Vector<double> &                 rhs_vector)
-  {
-    Assert(rhs_vector.size() == dof_handler.n_dofs(),
-           ExcDimensionMismatch(rhs_vector.size(), dof_handler.n_dofs()));
-    Assert(dof_handler.get_fe(0).n_components() == 1,
-           ExcMessage("This function only works for scalar finite elements"));
-
-    rhs_vector = 0;
-
-    std::pair<typename DoFHandler<dim, spacedim>::active_cell_iterator,
-              Point<spacedim>>
-      cell_point =
-        GridTools::find_active_cell_around_point(mapping, dof_handler, p);
-
-    Quadrature<dim> q(
-      GeometryInfo<dim>::project_to_unit_cell(cell_point.second));
-
-    FEValues<dim, spacedim> fe_values(mapping,
-                                      dof_handler.get_fe(),
-                                      q,
-                                      UpdateFlags(update_values));
-    fe_values.reinit(cell_point.first);
-
-    const unsigned int dofs_per_cell = dof_handler.get_fe().dofs_per_cell;
-
-    std::vector<types::global_dof_index> local_dof_indices(dofs_per_cell);
-    cell_point.first->get_dof_indices(local_dof_indices);
-
-    for (unsigned int i = 0; i < dofs_per_cell; i++)
-      rhs_vector(local_dof_indices[i]) = fe_values.shape_value(i, 0);
-  }
-
-
-
-  template <int dim, int spacedim>
-  void
-  create_point_source_vector(const DoFHandler<dim, spacedim> &dof_handler,
-                             const Point<spacedim> &          p,
-                             Vector<double> &                 rhs_vector)
-  {
-    create_point_source_vector(StaticMappingQ1<dim, spacedim>::mapping,
-                               dof_handler,
-                               p,
-                               rhs_vector);
-  }
-
-
-  template <int dim, int spacedim>
-  void
-  create_point_source_vector(
-    const hp::MappingCollection<dim, spacedim> &mapping,
-    const hp::DoFHandler<dim, spacedim> &       dof_handler,
-    const Point<spacedim> &                     p,
-    Vector<double> &                            rhs_vector)
-  {
-    Assert(rhs_vector.size() == dof_handler.n_dofs(),
-           ExcDimensionMismatch(rhs_vector.size(), dof_handler.n_dofs()));
-    Assert(dof_handler.get_fe(0).n_components() == 1,
-           ExcMessage("This function only works for scalar finite elements"));
-
-    rhs_vector = 0;
-
-    std::pair<typename hp::DoFHandler<dim, spacedim>::active_cell_iterator,
-              Point<spacedim>>
-      cell_point =
-        GridTools::find_active_cell_around_point(mapping, dof_handler, p);
-
-    Quadrature<dim> q(
-      GeometryInfo<dim>::project_to_unit_cell(cell_point.second));
-
-    FEValues<dim> fe_values(mapping[cell_point.first->active_fe_index()],
-                            cell_point.first->get_fe(),
-                            q,
-                            UpdateFlags(update_values));
-    fe_values.reinit(cell_point.first);
-
-    const unsigned int dofs_per_cell = cell_point.first->get_fe().dofs_per_cell;
-
-    std::vector<types::global_dof_index> local_dof_indices(dofs_per_cell);
-    cell_point.first->get_dof_indices(local_dof_indices);
-
-    for (unsigned int i = 0; i < dofs_per_cell; i++)
-      rhs_vector(local_dof_indices[i]) = fe_values.shape_value(i, 0);
-  }
-
-
-
-  template <int dim, int spacedim>
-  void
-  create_point_source_vector(const hp::DoFHandler<dim, spacedim> &dof_handler,
-                             const Point<spacedim> &              p,
-                             Vector<double> &                     rhs_vector)
-  {
-    create_point_source_vector(hp::StaticMappingQ1<dim>::mapping_collection,
-                               dof_handler,
-                               p,
-                               rhs_vector);
-  }
-
-
-
-  template <int dim, int spacedim>
-  void
-  create_point_source_vector(const Mapping<dim, spacedim> &   mapping,
-                             const DoFHandler<dim, spacedim> &dof_handler,
-                             const Point<spacedim> &          p,
-                             const Point<dim> &               orientation,
-                             Vector<double> &                 rhs_vector)
-  {
-    Assert(rhs_vector.size() == dof_handler.n_dofs(),
-           ExcDimensionMismatch(rhs_vector.size(), dof_handler.n_dofs()));
-    Assert(dof_handler.get_fe(0).n_components() == dim,
-           ExcMessage(
-             "This function only works for vector-valued finite elements."));
-
-    rhs_vector = 0;
-
-    const std::pair<typename DoFHandler<dim, spacedim>::active_cell_iterator,
-                    Point<spacedim>>
-      cell_point =
-        GridTools::find_active_cell_around_point(mapping, dof_handler, p);
-
-    const Quadrature<dim> q(
-      GeometryInfo<dim>::project_to_unit_cell(cell_point.second));
-
-    const FEValuesExtractors::Vector vec(0);
-    FEValues<dim, spacedim>          fe_values(mapping,
-                                      dof_handler.get_fe(),
-                                      q,
-                                      UpdateFlags(update_values));
-    fe_values.reinit(cell_point.first);
-
-    const unsigned int dofs_per_cell = dof_handler.get_fe().dofs_per_cell;
-
-    std::vector<types::global_dof_index> local_dof_indices(dofs_per_cell);
-    cell_point.first->get_dof_indices(local_dof_indices);
-
-    for (unsigned int i = 0; i < dofs_per_cell; i++)
-      rhs_vector(local_dof_indices[i]) =
-        orientation * fe_values[vec].value(i, 0);
-  }
-
-
-
-  template <int dim, int spacedim>
-  void
-  create_point_source_vector(const DoFHandler<dim, spacedim> &dof_handler,
-                             const Point<spacedim> &          p,
-                             const Point<dim> &               orientation,
-                             Vector<double> &                 rhs_vector)
-  {
-    create_point_source_vector(StaticMappingQ1<dim, spacedim>::mapping,
-                               dof_handler,
-                               p,
-                               orientation,
-                               rhs_vector);
-  }
-
-
-  template <int dim, int spacedim>
-  void
-  create_point_source_vector(
-    const hp::MappingCollection<dim, spacedim> &mapping,
-    const hp::DoFHandler<dim, spacedim> &       dof_handler,
-    const Point<spacedim> &                     p,
-    const Point<dim> &                          orientation,
-    Vector<double> &                            rhs_vector)
-  {
-    Assert(rhs_vector.size() == dof_handler.n_dofs(),
-           ExcDimensionMismatch(rhs_vector.size(), dof_handler.n_dofs()));
-    Assert(dof_handler.get_fe(0).n_components() == dim,
-           ExcMessage(
-             "This function only works for vector-valued finite elements."));
-
-    rhs_vector = 0;
-
-    std::pair<typename hp::DoFHandler<dim, spacedim>::active_cell_iterator,
-              Point<spacedim>>
-      cell_point =
-        GridTools::find_active_cell_around_point(mapping, dof_handler, p);
-
-    Quadrature<dim> q(
-      GeometryInfo<dim>::project_to_unit_cell(cell_point.second));
-
-    const FEValuesExtractors::Vector vec(0);
-    FEValues<dim> fe_values(mapping[cell_point.first->active_fe_index()],
-                            cell_point.first->get_fe(),
-                            q,
-                            UpdateFlags(update_values));
-    fe_values.reinit(cell_point.first);
-
-    const unsigned int dofs_per_cell = cell_point.first->get_fe().dofs_per_cell;
-
-    std::vector<types::global_dof_index> local_dof_indices(dofs_per_cell);
-    cell_point.first->get_dof_indices(local_dof_indices);
-
-    for (unsigned int i = 0; i < dofs_per_cell; i++)
-      rhs_vector(local_dof_indices[i]) =
-        orientation * fe_values[vec].value(i, 0);
-  }
-
-
-
-  template <int dim, int spacedim>
-  void
-  create_point_source_vector(const hp::DoFHandler<dim, spacedim> &dof_handler,
-                             const Point<spacedim> &              p,
-                             const Point<dim> &                   orientation,
-                             Vector<double> &                     rhs_vector)
-  {
-    create_point_source_vector(hp::StaticMappingQ1<dim>::mapping_collection,
-                               dof_handler,
-                               p,
-                               orientation,
-                               rhs_vector);
-  }
-
-
-
-  template <int dim, int spacedim, typename VectorType>
-  void
-  create_boundary_right_hand_side(
-    const Mapping<dim, spacedim> &                             mapping,
-    const DoFHandler<dim, spacedim> &                          dof_handler,
-    const Quadrature<dim - 1> &                                quadrature,
-    const Function<spacedim, typename VectorType::value_type> &rhs_function,
-    VectorType &                                               rhs_vector,
-    const std::set<types::boundary_id> &                       boundary_ids)
-  {
-    const FiniteElement<dim> &fe = dof_handler.get_fe();
-    Assert(fe.n_components() == rhs_function.n_components,
-           ExcDimensionMismatch(fe.n_components(), rhs_function.n_components));
-    Assert(rhs_vector.size() == dof_handler.n_dofs(),
-           ExcDimensionMismatch(rhs_vector.size(), dof_handler.n_dofs()));
-
-    rhs_vector = 0;
-
-    UpdateFlags update_flags =
-      UpdateFlags(update_values | update_quadrature_points | update_JxW_values);
-    FEFaceValues<dim> fe_values(mapping, fe, quadrature, update_flags);
-
-    const unsigned int dofs_per_cell = fe_values.dofs_per_cell,
-                       n_q_points    = fe_values.n_quadrature_points,
-                       n_components  = fe.n_components();
-
-    std::vector<types::global_dof_index> dofs(dofs_per_cell);
-    Vector<double>                       cell_vector(dofs_per_cell);
-
-    typename DoFHandler<dim, spacedim>::active_cell_iterator
-      cell = dof_handler.begin_active(),
-      endc = dof_handler.end();
-
-    if (n_components == 1)
-      {
-        std::vector<double> rhs_values(n_q_points);
-
-        for (; cell != endc; ++cell)
-          for (unsigned int face : GeometryInfo<dim>::face_indices())
-            if (cell->face(face)->at_boundary() &&
-                (boundary_ids.empty() ||
-                 (boundary_ids.find(cell->face(face)->boundary_id()) !=
-                  boundary_ids.end())))
-              {
-                fe_values.reinit(cell, face);
-
-                const std::vector<double> &weights = fe_values.get_JxW_values();
-                rhs_function.value_list(fe_values.get_quadrature_points(),
-                                        rhs_values);
-
-                cell_vector = 0;
-                for (unsigned int point = 0; point < n_q_points; ++point)
-                  for (unsigned int i = 0; i < dofs_per_cell; ++i)
-                    cell_vector(i) += rhs_values[point] *
-                                      fe_values.shape_value(i, point) *
-                                      weights[point];
-
-                cell->get_dof_indices(dofs);
-
-                for (unsigned int i = 0; i < dofs_per_cell; ++i)
-                  rhs_vector(dofs[i]) += cell_vector(i);
-              }
-      }
-    else
-      {
-        std::vector<Vector<double>> rhs_values(n_q_points,
-                                               Vector<double>(n_components));
-
-        for (; cell != endc; ++cell)
-          for (unsigned int face : GeometryInfo<dim>::face_indices())
-            if (cell->face(face)->at_boundary() &&
-                (boundary_ids.empty() ||
-                 (boundary_ids.find(cell->face(face)->boundary_id()) !=
-                  boundary_ids.end())))
-              {
-                fe_values.reinit(cell, face);
-
-                const std::vector<double> &weights = fe_values.get_JxW_values();
-                rhs_function.vector_value_list(
-                  fe_values.get_quadrature_points(), rhs_values);
-
-                cell_vector = 0;
-
-                // Use the faster code if the
-                // FiniteElement is primitive
-                if (fe.is_primitive())
-                  {
-                    for (unsigned int point = 0; point < n_q_points; ++point)
-                      for (unsigned int i = 0; i < dofs_per_cell; ++i)
-                        {
-                          const unsigned int component =
-                            fe.system_to_component_index(i).first;
-
-                          cell_vector(i) += rhs_values[point](component) *
-                                            fe_values.shape_value(i, point) *
-                                            weights[point];
-                        }
-                  }
-                else
-                  {
-                    // And the full featured
-                    // code, if vector valued
-                    // FEs are used
-                    for (unsigned int point = 0; point < n_q_points; ++point)
-                      for (unsigned int i = 0; i < dofs_per_cell; ++i)
-                        for (unsigned int comp_i = 0; comp_i < n_components;
-                             ++comp_i)
-                          if (fe.get_nonzero_components(i)[comp_i])
-                            {
-                              cell_vector(i) +=
-                                rhs_values[point](comp_i) *
-                                fe_values.shape_value_component(i,
-                                                                point,
-                                                                comp_i) *
-                                weights[point];
-                            }
-                  }
-
-                cell->get_dof_indices(dofs);
-
-                for (unsigned int i = 0; i < dofs_per_cell; ++i)
-                  rhs_vector(dofs[i]) += cell_vector(i);
-              }
-      }
-  }
-
-
-
-  template <int dim, int spacedim, typename VectorType>
-  void
-  create_boundary_right_hand_side(
-    const DoFHandler<dim, spacedim> &                          dof_handler,
-    const Quadrature<dim - 1> &                                quadrature,
-    const Function<spacedim, typename VectorType::value_type> &rhs_function,
-    VectorType &                                               rhs_vector,
-    const std::set<types::boundary_id> &                       boundary_ids)
-  {
-    create_boundary_right_hand_side(StaticMappingQ1<dim>::mapping,
-                                    dof_handler,
-                                    quadrature,
-                                    rhs_function,
-                                    rhs_vector,
-                                    boundary_ids);
-  }
-
-
-
-  template <int dim, int spacedim, typename VectorType>
-  void
-  create_boundary_right_hand_side(
-    const hp::MappingCollection<dim, spacedim> &               mapping,
-    const hp::DoFHandler<dim, spacedim> &                      dof_handler,
-    const hp::QCollection<dim - 1> &                           quadrature,
-    const Function<spacedim, typename VectorType::value_type> &rhs_function,
-    VectorType &                                               rhs_vector,
-    const std::set<types::boundary_id> &                       boundary_ids)
-  {
-    const hp::FECollection<dim> &fe = dof_handler.get_fe_collection();
-    Assert(fe.n_components() == rhs_function.n_components,
-           ExcDimensionMismatch(fe.n_components(), rhs_function.n_components));
-    Assert(rhs_vector.size() == dof_handler.n_dofs(),
-           ExcDimensionMismatch(rhs_vector.size(), dof_handler.n_dofs()));
-
-    rhs_vector = 0;
-
-    UpdateFlags update_flags =
-      UpdateFlags(update_values | update_quadrature_points | update_JxW_values);
-    hp::FEFaceValues<dim> x_fe_values(mapping, fe, quadrature, update_flags);
-
-    const unsigned int n_components = fe.n_components();
-
-    std::vector<types::global_dof_index> dofs(fe.max_dofs_per_cell());
-    Vector<double>                       cell_vector(fe.max_dofs_per_cell());
-
-    typename hp::DoFHandler<dim, spacedim>::active_cell_iterator
-      cell = dof_handler.begin_active(),
-      endc = dof_handler.end();
-
-    if (n_components == 1)
-      {
-        std::vector<double> rhs_values;
-
-        for (; cell != endc; ++cell)
-          for (unsigned int face : GeometryInfo<dim>::face_indices())
-            if (cell->face(face)->at_boundary() &&
-                (boundary_ids.empty() ||
-                 (boundary_ids.find(cell->face(face)->boundary_id()) !=
-                  boundary_ids.end())))
-              {
-                x_fe_values.reinit(cell, face);
-
-                const FEFaceValues<dim> &fe_values =
-                  x_fe_values.get_present_fe_values();
-
-                const unsigned int dofs_per_cell = fe_values.dofs_per_cell,
-                                   n_q_points = fe_values.n_quadrature_points;
-                rhs_values.resize(n_q_points);
-
-                const std::vector<double> &weights = fe_values.get_JxW_values();
-                rhs_function.value_list(fe_values.get_quadrature_points(),
-                                        rhs_values);
-
-                cell_vector = 0;
-                for (unsigned int point = 0; point < n_q_points; ++point)
-                  for (unsigned int i = 0; i < dofs_per_cell; ++i)
-                    cell_vector(i) += rhs_values[point] *
-                                      fe_values.shape_value(i, point) *
-                                      weights[point];
-
-                dofs.resize(dofs_per_cell);
-                cell->get_dof_indices(dofs);
-
-                for (unsigned int i = 0; i < dofs_per_cell; ++i)
-                  rhs_vector(dofs[i]) += cell_vector(i);
-              }
-      }
-    else
-      {
-        std::vector<Vector<double>> rhs_values;
-
-        for (; cell != endc; ++cell)
-          for (unsigned int face : GeometryInfo<dim>::face_indices())
-            if (cell->face(face)->at_boundary() &&
-                (boundary_ids.empty() ||
-                 (boundary_ids.find(cell->face(face)->boundary_id()) !=
-                  boundary_ids.end())))
-              {
-                x_fe_values.reinit(cell, face);
-
-                const FEFaceValues<dim> &fe_values =
-                  x_fe_values.get_present_fe_values();
-
-                const unsigned int dofs_per_cell = fe_values.dofs_per_cell,
-                                   n_q_points = fe_values.n_quadrature_points;
-                rhs_values.resize(n_q_points, Vector<double>(n_components));
-
-                const std::vector<double> &weights = fe_values.get_JxW_values();
-                rhs_function.vector_value_list(
-                  fe_values.get_quadrature_points(), rhs_values);
-
-                cell_vector = 0;
-
-                // Use the faster code if the
-                // FiniteElement is primitive
-                if (cell->get_fe().is_primitive())
-                  {
-                    for (unsigned int point = 0; point < n_q_points; ++point)
-                      for (unsigned int i = 0; i < dofs_per_cell; ++i)
-                        {
-                          const unsigned int component =
-                            cell->get_fe().system_to_component_index(i).first;
-
-                          cell_vector(i) += rhs_values[point](component) *
-                                            fe_values.shape_value(i, point) *
-                                            weights[point];
-                        }
-                  }
-                else
-                  {
-                    // And the full featured
-                    // code, if vector valued
-                    // FEs are used
-                    for (unsigned int point = 0; point < n_q_points; ++point)
-                      for (unsigned int i = 0; i < dofs_per_cell; ++i)
-                        for (unsigned int comp_i = 0; comp_i < n_components;
-                             ++comp_i)
-                          if (cell->get_fe().get_nonzero_components(i)[comp_i])
-                            {
-                              cell_vector(i) +=
-                                rhs_values[point](comp_i) *
-                                fe_values.shape_value_component(i,
-                                                                point,
-                                                                comp_i) *
-                                weights[point];
-                            }
-                  }
-                dofs.resize(dofs_per_cell);
-                cell->get_dof_indices(dofs);
-
-                for (unsigned int i = 0; i < dofs_per_cell; ++i)
-                  rhs_vector(dofs[i]) += cell_vector(i);
-              }
-      }
-  }
-
-
-
-  template <int dim, int spacedim, typename VectorType>
-  void
-  create_boundary_right_hand_side(
-    const hp::DoFHandler<dim, spacedim> &                      dof_handler,
-    const hp::QCollection<dim - 1> &                           quadrature,
-    const Function<spacedim, typename VectorType::value_type> &rhs_function,
-    VectorType &                                               rhs_vector,
-    const std::set<types::boundary_id> &                       boundary_ids)
-  {
-    create_boundary_right_hand_side(
-      hp::StaticMappingQ1<dim>::mapping_collection,
-      dof_handler,
-      quadrature,
-      rhs_function,
-      rhs_vector,
-      boundary_ids);
-  }
-
-
-
-  // ----------- interpolate_boundary_values for std::map --------------------
-
-  namespace internal
-  {
-    template <int dim,
-              int spacedim,
-              typename number,
-              template <int, int> class DoFHandlerType,
-              template <int, int> class M_or_MC>
-    static inline void
-    do_interpolate_boundary_values(
-      const M_or_MC<dim, spacedim> &       mapping,
-      const DoFHandlerType<dim, spacedim> &dof,
-      const std::map<types::boundary_id, const Function<spacedim, number> *>
-        &                                        function_map,
-      std::map<types::global_dof_index, number> &boundary_values,
-      const ComponentMask &                      component_mask)
-    {
-      Assert(
-        component_mask.represents_n_components(dof.get_fe(0).n_components()),
-        ExcMessage("The number of components in the mask has to be either "
-                   "zero or equal to the number of components in the finite "
-                   "element."));
-
-
-      // if for whatever reason we were passed an empty map, return
-      // immediately
-      if (function_map.size() == 0)
-        return;
-
-      Assert(function_map.find(numbers::internal_face_boundary_id) ==
-               function_map.end(),
-             ExcMessage("You cannot specify the special boundary indicator "
-                        "for interior faces in your function map."));
-
-      const unsigned int n_components = DoFTools::n_components(dof);
-      for (typename std::map<types::boundary_id,
-                             const Function<spacedim, number> *>::const_iterator
-             i = function_map.begin();
-           i != function_map.end();
-           ++i)
-        Assert(n_components == i->second->n_components,
-               ExcDimensionMismatch(n_components, i->second->n_components));
-
-
-      // interpolate boundary values in 1d. in higher dimensions, we
-      // use FEValues to figure out what to do on faces, but in 1d
-      // faces are points and it is far easier to simply work on
-      // individual vertices
-      if (dim == 1)
-        {
-          for (const auto &cell : dof.active_cell_iterators())
-            for (const unsigned int direction :
-                 GeometryInfo<dim>::face_indices())
-              if (cell->at_boundary(direction) &&
-                  (function_map.find(cell->face(direction)->boundary_id()) !=
-                   function_map.end()))
-                {
-                  const Function<spacedim, number> &boundary_function =
-                    *function_map.find(cell->face(direction)->boundary_id())
-                       ->second;
-
-                  // get the FE corresponding to this cell
-                  const FiniteElement<dim, spacedim> &fe = cell->get_fe();
-                  Assert(fe.n_components() == boundary_function.n_components,
-                         ExcDimensionMismatch(fe.n_components(),
-                                              boundary_function.n_components));
-
-                  Assert(component_mask.n_selected_components(
-                           fe.n_components()) > 0,
-                         ComponentMask::ExcNoComponentSelected());
-
-                  // now set the value of the vertex degree of
-                  // freedom. setting also creates the entry in the
-                  // map if it did not exist beforehand
-                  //
-                  // save some time by requesting values only once for
-                  // each point, irrespective of the number of
-                  // components of the function
-                  Vector<number> function_values(fe.n_components());
-                  if (fe.n_components() == 1)
-                    function_values(0) =
-                      boundary_function.value(cell->vertex(direction));
-                  else
-                    boundary_function.vector_value(cell->vertex(direction),
-                                                   function_values);
-
-                  for (unsigned int i = 0; i < fe.dofs_per_vertex; ++i)
-                    if (component_mask[fe.face_system_to_component_index(i)
-                                         .first])
-                      boundary_values[cell->vertex_dof_index(
-                        direction, i, cell->active_fe_index())] =
-                        function_values(
-                          fe.face_system_to_component_index(i).first);
-                }
-        }
-      else // dim > 1
-        {
-          const bool fe_is_system = (n_components != 1);
-
-          // field to store the indices
-          std::vector<types::global_dof_index> face_dofs;
-          face_dofs.reserve(DoFTools::max_dofs_per_face(dof));
-
-          // array to store the values of the boundary function at the boundary
-          // points. have two arrays for scalar and vector functions to use the
-          // more efficient one respectively
-          std::vector<number>         dof_values_scalar;
-          std::vector<Vector<number>> dof_values_system;
-          dof_values_scalar.reserve(DoFTools::max_dofs_per_face(dof));
-          dof_values_system.reserve(DoFTools::max_dofs_per_face(dof));
-
-          // before we start with the loop over all cells create an hp::FEValues
-          // object that holds the interpolation points of all finite elements
-          // that may ever be in use
-          const dealii::hp::FECollection<dim, spacedim> &finite_elements =
-            dof.get_fe_collection();
-          dealii::hp::QCollection<dim - 1> q_collection;
-          for (unsigned int f = 0; f < finite_elements.size(); ++f)
-            {
-              const FiniteElement<dim, spacedim> &fe = finite_elements[f];
-
-              // generate a quadrature rule on the face from the unit support
-              // points. this will be used to obtain the quadrature points on
-              // the real cell's face
-              //
-              // to do this, we check whether the FE has support points on the
-              // face at all:
-              if (fe.has_face_support_points())
-                q_collection.push_back(
-                  Quadrature<dim - 1>(fe.get_unit_face_support_points()));
-              else
-                {
-                  // if not, then we should try a more clever way. the idea is
-                  // that a finite element may not offer support points for all
-                  // its shape functions, but maybe only some. if it offers
-                  // support points for the components we are interested in in
-                  // this function, then that's fine. if not, the function we
-                  // call in the finite element will raise an exception. the
-                  // support points for the other shape functions are left
-                  // uninitialized (well, initialized by the default
-                  // constructor), since we don't need them anyway.
-                  //
-                  // As a detour, we must make sure we only query
-                  // face_system_to_component_index if the index corresponds to
-                  // a primitive shape function. since we know that all the
-                  // components we are interested in are primitive (by the above
-                  // check), we can safely put such a check in front
-                  std::vector<Point<dim - 1>> unit_support_points(
-                    fe.dofs_per_face);
-
-                  for (unsigned int i = 0; i < fe.dofs_per_face; ++i)
-                    if (fe.is_primitive(fe.face_to_cell_index(i, 0)))
-                      if (component_mask[fe.face_system_to_component_index(i)
-                                           .first] == true)
-                        unit_support_points[i] = fe.unit_face_support_point(i);
-
-                  q_collection.push_back(
-                    Quadrature<dim - 1>(unit_support_points));
-                }
-            }
-          // now that we have a q_collection object with all the right
-          // quadrature points, create an hp::FEFaceValues object that we can
-          // use to evaluate the boundary values at
-          const auto mapping_collection =
-            dealii::hp::MappingCollection<dim, spacedim>(mapping);
-          dealii::hp::FEFaceValues<dim, spacedim> x_fe_values(
-            mapping_collection,
-            finite_elements,
-            q_collection,
-            update_quadrature_points);
-
-          typename DoFHandlerType<dim, spacedim>::active_cell_iterator
-            cell = dof.begin_active(),
-            endc = dof.end();
-          for (; cell != endc; ++cell)
-            if (!cell->is_artificial())
-              for (const unsigned int face_no :
-                   GeometryInfo<dim>::face_indices())
-                {
-                  const FiniteElement<dim, spacedim> &fe = cell->get_fe();
-
-                  // we can presently deal only with primitive elements for
-                  // boundary values. this does not preclude us using
-                  // non-primitive elements in components that we aren't
-                  // interested in, however. make sure that all shape functions
-                  // that are non-zero for the components we are interested in,
-                  // are in fact primitive
-                  for (unsigned int i = 0; i < cell->get_fe().dofs_per_cell;
-                       ++i)
-                    {
-                      const ComponentMask &nonzero_component_array =
-                        cell->get_fe().get_nonzero_components(i);
-                      for (unsigned int c = 0; c < n_components; ++c)
-                        if ((nonzero_component_array[c] == true) &&
-                            (component_mask[c] == true))
-                          Assert(
-                            cell->get_fe().is_primitive(i),
-                            ExcMessage(
-                              "This function can only deal with requested boundary "
-                              "values that correspond to primitive (scalar) base "
-                              "elements. You may want to look up in the deal.II "
-                              "glossary what the term 'primitive' means."
-                              "\n\n"
-                              "There are alternative boundary value interpolation "
-                              "functions in namespace 'VectorTools' that you can "
-                              "use for non-primitive finite elements."));
-                    }
-
-                  const typename DoFHandlerType<dim, spacedim>::face_iterator
-                                           face = cell->face(face_no);
-                  const types::boundary_id boundary_component =
-                    face->boundary_id();
-
-                  // see if this face is part of the boundaries for which we are
-                  // supposed to do something, and also see if the finite
-                  // element in use here has DoFs on the face at all
-                  if ((function_map.find(boundary_component) !=
-                       function_map.end()) &&
-                      (cell->get_fe().dofs_per_face > 0))
-                    {
-                      // face is of the right component
-                      x_fe_values.reinit(cell, face_no);
-                      const dealii::FEFaceValues<dim, spacedim> &fe_values =
-                        x_fe_values.get_present_fe_values();
-
-                      // get indices, physical location and boundary values of
-                      // dofs on this face
-                      face_dofs.resize(fe.dofs_per_face);
-                      face->get_dof_indices(face_dofs, cell->active_fe_index());
-                      const std::vector<Point<spacedim>> &dof_locations =
-                        fe_values.get_quadrature_points();
-
-                      if (fe_is_system)
-                        {
-                          // resize array. avoid construction of a memory
-                          // allocating temporary if possible
-                          if (dof_values_system.size() < fe.dofs_per_face)
-                            dof_values_system.resize(fe.dofs_per_face,
-                                                     Vector<number>(
-                                                       fe.n_components()));
-                          else
-                            dof_values_system.resize(fe.dofs_per_face);
-
-                          function_map.find(boundary_component)
-                            ->second->vector_value_list(dof_locations,
-                                                        dof_values_system);
-
-                          // enter those dofs into the list that match the
-                          // component signature. avoid the usual complication
-                          // that we can't just use *_system_to_component_index
-                          // for non-primitive FEs
-                          for (unsigned int i = 0; i < face_dofs.size(); ++i)
-                            {
-                              unsigned int component;
-                              if (fe.is_primitive())
-                                component =
-                                  fe.face_system_to_component_index(i).first;
-                              else
-                                {
-                                  // non-primitive case. make sure that this
-                                  // particular shape function _is_ primitive,
-                                  // and get at it's component. use usual trick
-                                  // to transfer face dof index to cell dof
-                                  // index
-                                  const unsigned int cell_i =
-                                    (dim == 1 ?
-                                       i :
-                                       (dim == 2 ?
-                                          (i < 2 * fe.dofs_per_vertex ?
-                                             i :
-                                             i + 2 * fe.dofs_per_vertex) :
-                                          (dim == 3 ?
-                                             (i < 4 * fe.dofs_per_vertex ?
-                                                i :
-                                                (i < 4 * fe.dofs_per_vertex +
-                                                       4 * fe.dofs_per_line ?
-                                                   i + 4 * fe.dofs_per_vertex :
-                                                   i + 4 * fe.dofs_per_vertex +
-                                                     8 * fe.dofs_per_line)) :
-                                             numbers::invalid_unsigned_int)));
-                                  Assert(cell_i < fe.dofs_per_cell,
-                                         ExcInternalError());
-
-                                  // make sure that if this is not a primitive
-                                  // shape function, then all the corresponding
-                                  // components in the mask are not set
-                                  if (!fe.is_primitive(cell_i))
-                                    for (unsigned int c = 0; c < n_components;
-                                         ++c)
-                                      if (fe.get_nonzero_components(cell_i)[c])
-                                        Assert(component_mask[c] == false,
-                                               FETools::ExcFENotPrimitive());
-
-                                  // let's pick the first of possibly more than
-                                  // one non-zero components. if shape function
-                                  // is non-primitive, then we will ignore the
-                                  // result in the following anyway, otherwise
-                                  // there's only one non-zero component which
-                                  // we will use
-                                  component = fe.get_nonzero_components(cell_i)
-                                                .first_selected_component();
-                                }
-
-                              if (component_mask[component] == true)
-                                boundary_values[face_dofs[i]] =
-                                  dof_values_system[i](component);
-                            }
-                        }
-                      else
-                        // fe has only one component, so save some computations
-                        {
-                          // get only the one component that this function has
-                          dof_values_scalar.resize(fe.dofs_per_face);
-                          function_map.find(boundary_component)
-                            ->second->value_list(dof_locations,
-                                                 dof_values_scalar,
-                                                 0);
-
-                          // enter into list
-
-                          for (unsigned int i = 0; i < face_dofs.size(); ++i)
-                            boundary_values[face_dofs[i]] =
-                              dof_values_scalar[i];
-                        }
-                    }
-                }
-        }
-    } // end of interpolate_boundary_values
-  }   // namespace internal
-
-
-
-  template <int dim,
-            int spacedim,
-            template <int, int> class DoFHandlerType,
-            typename number>
-  void
-  interpolate_boundary_values(
-    const Mapping<dim, spacedim> &       mapping,
-    const DoFHandlerType<dim, spacedim> &dof,
-    const std::map<types::boundary_id, const Function<spacedim, number> *>
-      &                                        function_map,
-    std::map<types::global_dof_index, number> &boundary_values,
-    const ComponentMask &                      component_mask_)
-  {
-    internal::do_interpolate_boundary_values(
-      mapping, dof, function_map, boundary_values, component_mask_);
-  }
-
-
-
-  template <int dim,
-            int spacedim,
-            template <int, int> class DoFHandlerType,
-            typename number>
-  void
-  interpolate_boundary_values(
-    const Mapping<dim, spacedim> &             mapping,
-    const DoFHandlerType<dim, spacedim> &      dof,
-    const types::boundary_id                   boundary_component,
-    const Function<spacedim, number> &         boundary_function,
-    std::map<types::global_dof_index, number> &boundary_values,
-    const ComponentMask &                      component_mask)
-  {
-    std::map<types::boundary_id, const Function<spacedim, number> *>
-      function_map;
-    function_map[boundary_component] = &boundary_function;
-    interpolate_boundary_values(
-      mapping, dof, function_map, boundary_values, component_mask);
-  }
-
-
-  template <int dim, int spacedim, typename number>
-  void
-  interpolate_boundary_values(
-    const hp::MappingCollection<dim, spacedim> &mapping,
-    const hp::DoFHandler<dim, spacedim> &       dof,
-    const std::map<types::boundary_id, const Function<spacedim, number> *>
-      &                                        function_map,
-    std::map<types::global_dof_index, number> &boundary_values,
-    const ComponentMask &                      component_mask_)
-  {
-    internal::do_interpolate_boundary_values(
-      mapping, dof, function_map, boundary_values, component_mask_);
-  }
-
-
-
-  template <int dim,
-            int spacedim,
-            template <int, int> class DoFHandlerType,
-            typename number>
-  void
-  interpolate_boundary_values(
-    const DoFHandlerType<dim, spacedim> &      dof,
-    const types::boundary_id                   boundary_component,
-    const Function<spacedim, number> &         boundary_function,
-    std::map<types::global_dof_index, number> &boundary_values,
-    const ComponentMask &                      component_mask)
-  {
-    interpolate_boundary_values(StaticMappingQ1<dim, spacedim>::mapping,
-                                dof,
-                                boundary_component,
-                                boundary_function,
-                                boundary_values,
-                                component_mask);
-  }
-
-
-
-  template <int dim,
-            int spacedim,
-            template <int, int> class DoFHandlerType,
-            typename number>
-  void
-  interpolate_boundary_values(
-    const DoFHandlerType<dim, spacedim> &dof,
-    const std::map<types::boundary_id, const Function<spacedim, number> *>
-      &                                        function_map,
-    std::map<types::global_dof_index, number> &boundary_values,
-    const ComponentMask &                      component_mask)
-  {
-    interpolate_boundary_values(StaticMappingQ1<dim, spacedim>::mapping,
-                                dof,
-                                function_map,
-                                boundary_values,
-                                component_mask);
-  }
-
-
-
-  // ----------- interpolate_boundary_values for AffineConstraints
-  // --------------
-
-
-
-  template <int dim,
-            int spacedim,
-            template <int, int> class DoFHandlerType,
-            typename number>
-  void
-  interpolate_boundary_values(
-    const Mapping<dim, spacedim> &       mapping,
-    const DoFHandlerType<dim, spacedim> &dof,
-    const std::map<types::boundary_id, const Function<spacedim, number> *>
-      &                        function_map,
-    AffineConstraints<number> &constraints,
-    const ComponentMask &      component_mask_)
-  {
-    std::map<types::global_dof_index, number> boundary_values;
-    interpolate_boundary_values(
-      mapping, dof, function_map, boundary_values, component_mask_);
-    typename std::map<types::global_dof_index, number>::const_iterator
-      boundary_value = boundary_values.begin();
-    for (; boundary_value != boundary_values.end(); ++boundary_value)
-      {
-        if (constraints.can_store_line(boundary_value->first) &&
-            !constraints.is_constrained(boundary_value->first))
-          {
-            constraints.add_line(boundary_value->first);
-            constraints.set_inhomogeneity(boundary_value->first,
-                                          boundary_value->second);
-          }
-      }
-  }
-
-
-
-  template <int dim,
-            int spacedim,
-            template <int, int> class DoFHandlerType,
-            typename number>
-  void
-  interpolate_boundary_values(
-    const Mapping<dim, spacedim> &       mapping,
-    const DoFHandlerType<dim, spacedim> &dof,
-    const types::boundary_id             boundary_component,
-    const Function<spacedim, number> &   boundary_function,
-    AffineConstraints<number> &          constraints,
-    const ComponentMask &                component_mask)
-  {
-    std::map<types::boundary_id, const Function<spacedim, number> *>
-      function_map;
-    function_map[boundary_component] = &boundary_function;
-    interpolate_boundary_values(
-      mapping, dof, function_map, constraints, component_mask);
-  }
-
-
-
-  template <int dim,
-            int spacedim,
-            template <int, int> class DoFHandlerType,
-            typename number>
-  void
-  interpolate_boundary_values(
-    const DoFHandlerType<dim, spacedim> &dof,
-    const types::boundary_id             boundary_component,
-    const Function<spacedim, number> &   boundary_function,
-    AffineConstraints<number> &          constraints,
-    const ComponentMask &                component_mask)
-  {
-    interpolate_boundary_values(StaticMappingQ1<dim, spacedim>::mapping,
-                                dof,
-                                boundary_component,
-                                boundary_function,
-                                constraints,
-                                component_mask);
-  }
-
-
-
-  template <int dim,
-            int spacedim,
-            template <int, int> class DoFHandlerType,
-            typename number>
-  void
-  interpolate_boundary_values(
-    const DoFHandlerType<dim, spacedim> &dof,
-    const std::map<types::boundary_id, const Function<spacedim, number> *>
-      &                        function_map,
-    AffineConstraints<number> &constraints,
-    const ComponentMask &      component_mask)
-  {
-    interpolate_boundary_values(StaticMappingQ1<dim, spacedim>::mapping,
-                                dof,
-                                function_map,
-                                constraints,
-                                component_mask);
-  }
-
-
-
-  // -------- implementation for project_boundary_values with std::map --------
-
-
-  namespace internal
-  {
-    // keep the first argument non-reference since we use it
-    // with 1e-8 * number
-    template <typename number1, typename number2>
-    bool
-    real_part_bigger_than(const number1 a, const number2 &b)
-    {
-      return a > b;
-    }
-
-    template <typename number1, typename number2>
-    bool
-    real_part_bigger_than(const number1 a, const std::complex<number2> b)
-    {
-      Assert(std::abs(b.imag()) <= 1e-15 * std::abs(b), ExcInternalError());
-      return a > b.real();
-    }
-
-    template <typename number1, typename number2>
-    bool
-    real_part_bigger_than(const std::complex<number1> a, const number2 b)
-    {
-      Assert(std::abs(a.imag()) <= 1e-15 * std::abs(a), ExcInternalError());
-      return a.real() > b;
-    }
-
-    template <typename number1, typename number2>
-    bool
-    real_part_bigger_than(const std::complex<number1> a,
-                          const std::complex<number2> b)
-    {
-      Assert(std::abs(a.imag()) <= 1e-15 * std::abs(a), ExcInternalError());
-      Assert(std::abs(b.imag()) <= 1e-15 * std::abs(b), ExcInternalError());
-      return a.real() > b.real();
-    }
-
-    // this function is needed to get an idea where
-    // rhs.norm_sqr()  is too small for a given type.
-    template <typename number>
-    number
-    min_number(const number & /*dummy*/)
-    {
-      return std::numeric_limits<number>::min();
-    }
-
-    // Sine rhs.norm_sqr() is non-negative real, in complex case we
-    // take the numeric limits of the underlying type used in std::complex<>.
-    template <typename number>
-    number
-    min_number(const std::complex<number> & /*dummy*/)
-    {
-      return std::numeric_limits<number>::min();
-    }
-
-    template <int dim,
-              int spacedim,
-              template <int, int> class DoFHandlerType,
-              template <int, int> class M_or_MC,
-              template <int> class Q_or_QC,
-              typename number>
-    void
-    do_project_boundary_values(
-      const M_or_MC<dim, spacedim> &       mapping,
-      const DoFHandlerType<dim, spacedim> &dof,
-      const std::map<types::boundary_id, const Function<spacedim, number> *>
-        &                                        boundary_functions,
-      const Q_or_QC<dim - 1> &                   q,
-      std::map<types::global_dof_index, number> &boundary_values,
-      std::vector<unsigned int>                  component_mapping)
-    {
-      // in 1d, projection onto the 0d end points == interpolation
-      if (dim == 1)
-        {
-          Assert(component_mapping.size() == 0, ExcNotImplemented());
-          interpolate_boundary_values(
-            mapping, dof, boundary_functions, boundary_values, ComponentMask());
-          return;
-        }
-
-      // TODO:[?] In project_boundary_values, no condensation of sparsity
-      //    structures, matrices and right hand sides or distribution of
-      //    solution vectors is performed. This is ok for dim<3 because then
-      //    there are no constrained nodes on the boundary, but is not
-      //    acceptable for higher dimensions. Fix this.
-
-      if (component_mapping.size() == 0)
-        {
-          AssertDimension(dof.get_fe(0).n_components(),
-                          boundary_functions.begin()->second->n_components);
-          // I still do not see why i
-          // should create another copy
-          // here
-          component_mapping.resize(dof.get_fe(0).n_components());
-          for (unsigned int i = 0; i < component_mapping.size(); ++i)
-            component_mapping[i] = i;
-        }
-      else
-        AssertDimension(dof.get_fe(0).n_components(), component_mapping.size());
-
-      std::vector<types::global_dof_index> dof_to_boundary_mapping;
-      std::set<types::boundary_id>         selected_boundary_components;
-      for (typename std::map<types::boundary_id,
-                             const Function<spacedim, number> *>::const_iterator
-             i = boundary_functions.begin();
-           i != boundary_functions.end();
-           ++i)
-        selected_boundary_components.insert(i->first);
-
-      DoFTools::map_dof_to_boundary_indices(dof,
-                                            selected_boundary_components,
-                                            dof_to_boundary_mapping);
-
-      // Done if no degrees of freedom on the boundary
-      if (dof.n_boundary_dofs(boundary_functions) == 0)
-        return;
-
-      // set up sparsity structure
-      DynamicSparsityPattern dsp(dof.n_boundary_dofs(boundary_functions),
-                                 dof.n_boundary_dofs(boundary_functions));
-      DoFTools::make_boundary_sparsity_pattern(dof,
-                                               boundary_functions,
-                                               dof_to_boundary_mapping,
-                                               dsp);
-      SparsityPattern sparsity;
-      sparsity.copy_from(dsp);
-
-
-
-      // note: for three or more dimensions, there
-      // may be constrained nodes on the boundary
-      // in this case the boundary mass matrix has
-      // to be condensed and the solution is to
-      // be distributed afterwards, which is not
-      // yet implemented. The reason for this is
-      // that we cannot simply use the condense
-      // family of functions, since the matrices
-      // and vectors do not use the global
-      // numbering but rather the boundary
-      // numbering, i.e. the condense function
-      // needs to use another indirection. There
-      // should be not many technical problems,
-      // but it needs to be implemented
-      if (dim >= 3)
-        {
-#ifdef DEBUG
-          // Assert that there are no hanging nodes at the boundary
-          int level = -1;
-          for (const auto &cell : dof.active_cell_iterators())
-            for (auto f : GeometryInfo<dim>::face_indices())
-              {
-                if (cell->at_boundary(f))
-                  {
-                    if (level == -1)
-                      level = cell->level();
-                    else
-                      {
-                        Assert(
-                          level == cell->level(),
-                          ExcMessage(
-                            "The mesh you use in projecting boundary values "
-                            "has hanging nodes at the boundary. This would require "
-                            "dealing with hanging node constraints when solving "
-                            "the linear system on the boundary, but this is not "
-                            "currently implemented."));
-                      }
-                  }
-              }
-#endif
-        }
-      sparsity.compress();
-
-
-      // make mass matrix and right hand side
-      SparseMatrix<number> mass_matrix(sparsity);
-      Vector<number>       rhs(sparsity.n_rows());
-
-
-      MatrixCreator::create_boundary_mass_matrix(
-        mapping,
-        dof,
-        q,
-        mass_matrix,
-        boundary_functions,
-        rhs,
-        dof_to_boundary_mapping,
-        static_cast<const Function<spacedim, number> *>(nullptr),
-        component_mapping);
-
-      Vector<number> boundary_projection(rhs.size());
-
-      // cannot reduce residual in a useful way if we are close to the square
-      // root of the minimal double value
-      if (rhs.norm_sqr() < 1e28 * min_number(number()))
-        boundary_projection = 0;
-      else
-        {
-          invert_mass_matrix(mass_matrix, rhs, boundary_projection);
-        }
-      // fill in boundary values
-      for (unsigned int i = 0; i < dof_to_boundary_mapping.size(); ++i)
-        if (dof_to_boundary_mapping[i] != numbers::invalid_dof_index)
-          {
-            AssertIsFinite(boundary_projection(dof_to_boundary_mapping[i]));
-
-            // this dof is on one of the
-            // interesting boundary parts
-            //
-            // remember: i is the global dof
-            // number, dof_to_boundary_mapping[i]
-            // is the number on the boundary and
-            // thus in the solution vector
-            boundary_values[i] =
-              boundary_projection(dof_to_boundary_mapping[i]);
-          }
-    }
-  } // namespace internal
-
-  template <int dim, int spacedim, typename number>
-  void
-  project_boundary_values(
-    const Mapping<dim, spacedim> &   mapping,
-    const DoFHandler<dim, spacedim> &dof,
-    const std::map<types::boundary_id, const Function<spacedim, number> *>
-      &                                        boundary_functions,
-    const Quadrature<dim - 1> &                q,
-    std::map<types::global_dof_index, number> &boundary_values,
-    std::vector<unsigned int>                  component_mapping)
-  {
-    internal::do_project_boundary_values(
-      mapping, dof, boundary_functions, q, boundary_values, component_mapping);
-  }
-
-
-
-  template <int dim, int spacedim, typename number>
-  void
-  project_boundary_values(
-    const DoFHandler<dim, spacedim> &dof,
-    const std::map<types::boundary_id, const Function<spacedim, number> *>
-      &                                        boundary_functions,
-    const Quadrature<dim - 1> &                q,
-    std::map<types::global_dof_index, number> &boundary_values,
-    std::vector<unsigned int>                  component_mapping)
-  {
-    project_boundary_values(StaticMappingQ1<dim, spacedim>::mapping,
-                            dof,
-                            boundary_functions,
-                            q,
-                            boundary_values,
-                            component_mapping);
-  }
-
-
-
-  template <int dim, int spacedim, typename number>
-  void
-  project_boundary_values(
-    const hp::MappingCollection<dim, spacedim> &mapping,
-    const hp::DoFHandler<dim, spacedim> &       dof,
-    const std::map<types::boundary_id, const Function<spacedim, number> *>
-      &                                        boundary_functions,
-    const hp::QCollection<dim - 1> &           q,
-    std::map<types::global_dof_index, number> &boundary_values,
-    std::vector<unsigned int>                  component_mapping)
-  {
-    internal::do_project_boundary_values(
-      mapping, dof, boundary_functions, q, boundary_values, component_mapping);
-  }
-
-
-
-  template <int dim, int spacedim, typename number>
-  void
-  project_boundary_values(
-    const hp::DoFHandler<dim, spacedim> &dof,
-    const std::map<types::boundary_id, const Function<spacedim, number> *>
-      &                                        boundary_function,
-    const hp::QCollection<dim - 1> &           q,
-    std::map<types::global_dof_index, number> &boundary_values,
-    std::vector<unsigned int>                  component_mapping)
-  {
-    project_boundary_values(
-      hp::StaticMappingQ1<dim, spacedim>::mapping_collection,
-      dof,
-      boundary_function,
-      q,
-      boundary_values,
-      component_mapping);
-  }
-
-
-  // ---- implementation for project_boundary_values with AffineConstraints ----
-
-
-
-  template <int dim, int spacedim, typename number>
-  void
-  project_boundary_values(
-    const Mapping<dim, spacedim> &   mapping,
-    const DoFHandler<dim, spacedim> &dof,
-    const std::map<types::boundary_id, const Function<spacedim, number> *>
-      &                        boundary_functions,
-    const Quadrature<dim - 1> &q,
-    AffineConstraints<number> &constraints,
-    std::vector<unsigned int>  component_mapping)
-  {
-    std::map<types::global_dof_index, number> boundary_values;
-    project_boundary_values(
-      mapping, dof, boundary_functions, q, boundary_values, component_mapping);
-    typename std::map<types::global_dof_index, number>::const_iterator
-      boundary_value = boundary_values.begin();
-    for (; boundary_value != boundary_values.end(); ++boundary_value)
-      {
-        if (!constraints.is_constrained(boundary_value->first))
-          {
-            constraints.add_line(boundary_value->first);
-            constraints.set_inhomogeneity(boundary_value->first,
-                                          boundary_value->second);
-          }
-      }
-  }
-
-
-
-  template <int dim, int spacedim, typename number>
-  void
-  project_boundary_values(
-    const DoFHandler<dim, spacedim> &dof,
-    const std::map<types::boundary_id, const Function<spacedim, number> *>
-      &                        boundary_functions,
-    const Quadrature<dim - 1> &q,
-    AffineConstraints<number> &constraints,
-    std::vector<unsigned int>  component_mapping)
-  {
-    project_boundary_values(StaticMappingQ1<dim, spacedim>::mapping,
-                            dof,
-                            boundary_functions,
-                            q,
-                            constraints,
-                            component_mapping);
-  }
-
-
-
-  namespace internal
-  {
-    /**
-     * A structure that stores the dim DoF indices that correspond to a
-     * vector-valued quantity at a single support point.
-     */
-    template <int dim>
-    struct VectorDoFTuple
-    {
-      types::global_dof_index dof_indices[dim];
-
-      VectorDoFTuple()
-      {
-        for (unsigned int i = 0; i < dim; ++i)
-          dof_indices[i] = numbers::invalid_dof_index;
-      }
-
-
-      bool
-      operator<(const VectorDoFTuple<dim> &other) const
-      {
-        for (unsigned int i = 0; i < dim; ++i)
-          if (dof_indices[i] < other.dof_indices[i])
-            return true;
-          else if (dof_indices[i] > other.dof_indices[i])
-            return false;
-        return false;
-      }
-
-      bool
-      operator==(const VectorDoFTuple<dim> &other) const
-      {
-        for (unsigned int i = 0; i < dim; ++i)
-          if (dof_indices[i] != other.dof_indices[i])
-            return false;
-
-        return true;
-      }
-
-      bool
-      operator!=(const VectorDoFTuple<dim> &other) const
-      {
-        return !(*this == other);
-      }
-    };
-
-
-    template <int dim>
-    std::ostream &
-    operator<<(std::ostream &out, const VectorDoFTuple<dim> &vdt)
-    {
-      for (unsigned int d = 0; d < dim; ++d)
-        out << vdt.dof_indices[d] << (d < dim - 1 ? " " : "");
-      return out;
-    }
-
-
-
-    /**
-     * Add the constraint $\vec n \cdot \vec u = inhom$ to the list of
-     * constraints.
-     *
-     * Here, $\vec u$ is represented by the set of given DoF indices, and
-     * $\vec n$ by the vector specified as the second argument.
-     *
-     * The function does not add constraints if a degree of freedom is already
-     * constrained in the constraints object.
-     */
-    template <int dim>
-    void
-    add_constraint(const VectorDoFTuple<dim> &dof_indices,
-                   const Tensor<1, dim> &     constraining_vector,
-                   AffineConstraints<double> &constraints,
-                   const double               inhomogeneity = 0)
-    {
-      // choose the DoF that has the largest component in the
-      // constraining_vector as the one to be constrained as this makes the
-      // process stable in cases where the constraining_vector has the form
-      // n=(1,0) or n=(0,1)
-      //
-      // we get constraints of the form x0 = a_1*x1 + a2*x2 + ... if one of
-      // the weights is essentially zero then skip this part. the
-      // AffineConstraints can also deal with cases like x0 = 0 if
-      // necessary
-      //
-      // there is a problem if we have a normal vector of the form
-      // (a,a,small) or (a,a,a). Depending on round-off we may choose the
-      // first or second component (or third, in the latter case) as the
-      // largest one, and depending on our choice one or another degree of
-      // freedom will be constrained. On a single processor this is not
-      // much of a problem, but it's a nightmare when we run in parallel
-      // and two processors disagree on which DoF should be constrained.
-      // This led to an incredibly difficult to find bug in step-32 when
-      // running in parallel with 9 or more processors.
-      //
-      // in practice, such normal vectors of the form (a,a,small) or
-      // (a,a,a) happen not infrequently since they lie on the diagonals
-      // where vertices frequently happen to land upon mesh refinement if
-      // one starts from a symmetric and regular body. we work around this
-      // problem in the following way: if we have a normal vector of the
-      // form (a,b) (similarly algorithm in 3d), we choose 'a' as the
-      // largest coefficient not if a>b but if a>b+1e-10. this shifts the
-      // problem away from the frequently visited diagonal to a line that
-      // is off the diagonal. there will of course be problems where the
-      // exact values of a and b differ by exactly 1e-10 and we get into
-      // the same instability, but from a practical viewpoint such problems
-      // should be much rarer. in particular, meshes have to be very fine
-      // for a vertex to land on this line if the original body had a
-      // vertex on the diagonal as well
-      switch (dim)
-        {
-          case 2:
-            {
-              if (std::fabs(constraining_vector[0]) >
-                  std::fabs(constraining_vector[1]) + 1e-10)
-                {
-                  if (!constraints.is_constrained(dof_indices.dof_indices[0]) &&
-                      constraints.can_store_line(dof_indices.dof_indices[0]))
-                    {
-                      constraints.add_line(dof_indices.dof_indices[0]);
-
-                      if (std::fabs(constraining_vector[1] /
-                                    constraining_vector[0]) >
-                          std::numeric_limits<double>::epsilon())
-                        constraints.add_entry(dof_indices.dof_indices[0],
-                                              dof_indices.dof_indices[1],
-                                              -constraining_vector[1] /
-                                                constraining_vector[0]);
-
-                      if (std::fabs(inhomogeneity / constraining_vector[0]) >
-                          std::numeric_limits<double>::epsilon())
-                        constraints.set_inhomogeneity(
-                          dof_indices.dof_indices[0],
-                          inhomogeneity / constraining_vector[0]);
-                    }
-                }
-              else
-                {
-                  if (!constraints.is_constrained(dof_indices.dof_indices[1]) &&
-                      constraints.can_store_line(dof_indices.dof_indices[1]))
-                    {
-                      constraints.add_line(dof_indices.dof_indices[1]);
-
-                      if (std::fabs(constraining_vector[0] /
-                                    constraining_vector[1]) >
-                          std::numeric_limits<double>::epsilon())
-                        constraints.add_entry(dof_indices.dof_indices[1],
-                                              dof_indices.dof_indices[0],
-                                              -constraining_vector[0] /
-                                                constraining_vector[1]);
-
-                      if (std::fabs(inhomogeneity / constraining_vector[1]) >
-                          std::numeric_limits<double>::epsilon())
-                        constraints.set_inhomogeneity(
-                          dof_indices.dof_indices[1],
-                          inhomogeneity / constraining_vector[1]);
-                    }
-                }
-              break;
-            }
-
-          case 3:
-            {
-              if ((std::fabs(constraining_vector[0]) >=
-                   std::fabs(constraining_vector[1]) + 1e-10) &&
-                  (std::fabs(constraining_vector[0]) >=
-                   std::fabs(constraining_vector[2]) + 2e-10))
-                {
-                  if (!constraints.is_constrained(dof_indices.dof_indices[0]) &&
-                      constraints.can_store_line(dof_indices.dof_indices[0]))
-                    {
-                      constraints.add_line(dof_indices.dof_indices[0]);
-
-                      if (std::fabs(constraining_vector[1] /
-                                    constraining_vector[0]) >
-                          std::numeric_limits<double>::epsilon())
-                        constraints.add_entry(dof_indices.dof_indices[0],
-                                              dof_indices.dof_indices[1],
-                                              -constraining_vector[1] /
-                                                constraining_vector[0]);
-
-                      if (std::fabs(constraining_vector[2] /
-                                    constraining_vector[0]) >
-                          std::numeric_limits<double>::epsilon())
-                        constraints.add_entry(dof_indices.dof_indices[0],
-                                              dof_indices.dof_indices[2],
-                                              -constraining_vector[2] /
-                                                constraining_vector[0]);
-
-                      if (std::fabs(inhomogeneity / constraining_vector[0]) >
-                          std::numeric_limits<double>::epsilon())
-                        constraints.set_inhomogeneity(
-                          dof_indices.dof_indices[0],
-                          inhomogeneity / constraining_vector[0]);
-                    }
-                }
-              else if ((std::fabs(constraining_vector[1]) + 1e-10 >=
-                        std::fabs(constraining_vector[0])) &&
-                       (std::fabs(constraining_vector[1]) >=
-                        std::fabs(constraining_vector[2]) + 1e-10))
-                {
-                  if (!constraints.is_constrained(dof_indices.dof_indices[1]) &&
-                      constraints.can_store_line(dof_indices.dof_indices[1]))
-                    {
-                      constraints.add_line(dof_indices.dof_indices[1]);
-
-                      if (std::fabs(constraining_vector[0] /
-                                    constraining_vector[1]) >
-                          std::numeric_limits<double>::epsilon())
-                        constraints.add_entry(dof_indices.dof_indices[1],
-                                              dof_indices.dof_indices[0],
-                                              -constraining_vector[0] /
-                                                constraining_vector[1]);
-
-                      if (std::fabs(constraining_vector[2] /
-                                    constraining_vector[1]) >
-                          std::numeric_limits<double>::epsilon())
-                        constraints.add_entry(dof_indices.dof_indices[1],
-                                              dof_indices.dof_indices[2],
-                                              -constraining_vector[2] /
-                                                constraining_vector[1]);
-
-                      if (std::fabs(inhomogeneity / constraining_vector[1]) >
-                          std::numeric_limits<double>::epsilon())
-                        constraints.set_inhomogeneity(
-                          dof_indices.dof_indices[1],
-                          inhomogeneity / constraining_vector[1]);
-                    }
-                }
-              else
-                {
-                  if (!constraints.is_constrained(dof_indices.dof_indices[2]) &&
-                      constraints.can_store_line(dof_indices.dof_indices[2]))
-                    {
-                      constraints.add_line(dof_indices.dof_indices[2]);
-
-                      if (std::fabs(constraining_vector[0] /
-                                    constraining_vector[2]) >
-                          std::numeric_limits<double>::epsilon())
-                        constraints.add_entry(dof_indices.dof_indices[2],
-                                              dof_indices.dof_indices[0],
-                                              -constraining_vector[0] /
-                                                constraining_vector[2]);
-
-                      if (std::fabs(constraining_vector[1] /
-                                    constraining_vector[2]) >
-                          std::numeric_limits<double>::epsilon())
-                        constraints.add_entry(dof_indices.dof_indices[2],
-                                              dof_indices.dof_indices[1],
-                                              -constraining_vector[1] /
-                                                constraining_vector[2]);
-
-                      if (std::fabs(inhomogeneity / constraining_vector[2]) >
-                          std::numeric_limits<double>::epsilon())
-                        constraints.set_inhomogeneity(
-                          dof_indices.dof_indices[2],
-                          inhomogeneity / constraining_vector[2]);
-                    }
-                }
-
-              break;
-            }
-
-          default:
-            Assert(false, ExcNotImplemented());
-        }
-    }
-
-
-    /**
-     * Add the constraint $(\vec u-\vec u_\Gamma) \| \vec t$ to the list of
-     * constraints. In 2d, this is a single constraint, in 3d these are two
-     * constraints.
-     *
-     * Here, $\vec u$ is represented by the set of given DoF indices, and
-     * $\vec t$ by the vector specified as the second argument.
-     *
-     * The function does not add constraints if a degree of freedom is already
-     * constrained in the constraints object.
-     */
-    template <int dim>
-    void
-    add_tangentiality_constraints(
-      const VectorDoFTuple<dim> &dof_indices,
-      const Tensor<1, dim> &     tangent_vector,
-      AffineConstraints<double> &constraints,
-      const Vector<double> &     b_values = Vector<double>(dim))
-    {
-      // choose the DoF that has the
-      // largest component in the
-      // tangent_vector as the
-      // independent component, and
-      // then constrain the others to
-      // it. specifically, if, say,
-      // component 0 of the tangent
-      // vector t is largest by
-      // magnitude, then
-      // x1=(b[1]*t[0]-b[0]*t[1])/t[0]+t[1]/t[0]*x_0, etc.
-      unsigned int largest_component = 0;
-      for (unsigned int d = 1; d < dim; ++d)
-        if (std::fabs(tangent_vector[d]) >
-            std::fabs(tangent_vector[largest_component]) + 1e-10)
-          largest_component = d;
-
-      // then constrain all of the
-      // other degrees of freedom in
-      // terms of the one just found
-      for (unsigned int d = 0; d < dim; ++d)
-        if (d != largest_component)
-          if (!constraints.is_constrained(dof_indices.dof_indices[d]) &&
-              constraints.can_store_line(dof_indices.dof_indices[d]))
-            {
-              constraints.add_line(dof_indices.dof_indices[d]);
-
-              if (std::fabs(tangent_vector[d] /
-                            tangent_vector[largest_component]) >
-                  std::numeric_limits<double>::epsilon())
-                constraints.add_entry(
-                  dof_indices.dof_indices[d],
-                  dof_indices.dof_indices[largest_component],
-                  tangent_vector[d] / tangent_vector[largest_component]);
-
-              const double inhomogeneity =
-                (b_values(d) * tangent_vector[largest_component] -
-                 b_values(largest_component) * tangent_vector[d]) /
-                tangent_vector[largest_component];
-
-              if (std::fabs(inhomogeneity) >
-                  std::numeric_limits<double>::epsilon())
-                constraints.set_inhomogeneity(dof_indices.dof_indices[d],
-                                              inhomogeneity);
-            }
-    }
-
-
-
-    /**
-     * Given a vector, compute a set of dim-1 vectors that are orthogonal to
-     * the first one and mutually orthonormal as well.
-     */
-    template <int dim>
-    void
-    compute_orthonormal_vectors(const Tensor<1, dim> &vector,
-                                Tensor<1, dim> (&orthonormals)[dim - 1])
-    {
-      switch (dim)
-        {
-          case 3:
-            {
-              // to do this in 3d, take
-              // one vector that is
-              // guaranteed to be not
-              // aligned with the
-              // average tangent and
-              // form the cross
-              // product. this yields
-              // one vector that is
-              // certainly
-              // perpendicular to the
-              // tangent; then take the
-              // cross product between
-              // this vector and the
-              // tangent and get one
-              // vector that is
-              // perpendicular to both
-
-              // construct a
-              // temporary vector
-              // by swapping the
-              // larger two
-              // components and
-              // flipping one
-              // sign; this can
-              // not be collinear
-              // with the average
-              // tangent
-              Tensor<1, dim> tmp = vector;
-              if ((std::fabs(tmp[0]) > std::fabs(tmp[1])) &&
-                  (std::fabs(tmp[0]) > std::fabs(tmp[2])))
-                {
-                  // entry zero
-                  // is the
-                  // largest
-                  if ((std::fabs(tmp[1]) > std::fabs(tmp[2])))
-                    std::swap(tmp[0], tmp[1]);
-                  else
-                    std::swap(tmp[0], tmp[2]);
-
-                  tmp[0] *= -1;
-                }
-              else if ((std::fabs(tmp[1]) > std::fabs(tmp[0])) &&
-                       (std::fabs(tmp[1]) > std::fabs(tmp[2])))
-                {
-                  // entry one
-                  // is the
-                  // largest
-                  if ((std::fabs(tmp[0]) > std::fabs(tmp[2])))
-                    std::swap(tmp[1], tmp[0]);
-                  else
-                    std::swap(tmp[1], tmp[2]);
-
-                  tmp[1] *= -1;
-                }
-              else
-                {
-                  // entry two
-                  // is the
-                  // largest
-                  if ((std::fabs(tmp[0]) > std::fabs(tmp[1])))
-                    std::swap(tmp[2], tmp[0]);
-                  else
-                    std::swap(tmp[2], tmp[1]);
-
-                  tmp[2] *= -1;
-                }
-
-              // make sure the two vectors
-              // are indeed not collinear
-              Assert(std::fabs(vector * tmp / vector.norm() / tmp.norm()) <
-                       (1 - 1e-12),
-                     ExcInternalError());
-
-              // now compute the
-              // two normals
-              orthonormals[0] = cross_product_3d(vector, tmp);
-              orthonormals[1] = cross_product_3d(vector, orthonormals[0]);
-
-              break;
-            }
-
-          default:
-            Assert(false, ExcNotImplemented());
-        }
-    }
-  } // namespace internal
-
-
-  namespace internals
-  {
-    // This function computes the
-    // projection of the boundary
-    // function on edges for 3D.
-    template <typename cell_iterator>
-    void
-    compute_edge_projection(const cell_iterator &cell,
-                            const unsigned int   face,
-                            const unsigned int   line,
-                            hp::FEValues<3> &    hp_fe_values,
-                            const Function<3> &  boundary_function,
-                            const unsigned int   first_vector_component,
-                            std::vector<double> &dof_values,
-                            std::vector<bool> &  dofs_processed)
-    {
-      const double tol =
-        0.5 * cell->face(face)->line(line)->diameter() / cell->get_fe().degree;
-      const unsigned int dim      = 3;
-      const unsigned int spacedim = 3;
-
-      hp_fe_values.reinit(
-        cell,
-        (cell->active_fe_index() * GeometryInfo<dim>::faces_per_cell + face) *
-            GeometryInfo<dim>::lines_per_face +
-          line);
-
-      // Initialize the required
-      // objects.
-      const FEValues<dim> &fe_values = hp_fe_values.get_present_fe_values();
-      const FiniteElement<dim> &                           fe = cell->get_fe();
-      const std::vector<DerivativeForm<1, dim, spacedim>> &jacobians =
-        fe_values.get_jacobians();
-      const std::vector<Point<dim>> &quadrature_points =
-        fe_values.get_quadrature_points();
-
-      std::vector<Tensor<1, dim>> tangentials(fe_values.n_quadrature_points);
-      std::vector<Vector<double>> values(fe_values.n_quadrature_points,
-                                         Vector<double>(fe.n_components()));
-
-      // Get boundary function values
-      // at quadrature points.
-      boundary_function.vector_value_list(quadrature_points, values);
-
-      const std::vector<Point<dim>> &reference_quadrature_points =
-        fe_values.get_quadrature().get_points();
-      std::pair<unsigned int, unsigned int> base_indices(0, 0);
-
-      if (dynamic_cast<const FESystem<dim> *>(&cell->get_fe()) != nullptr)
-        {
-          unsigned int fe_index     = 0;
-          unsigned int fe_index_old = 0;
-          unsigned int i            = 0;
-
-          for (; i < fe.n_base_elements(); ++i)
-            {
-              fe_index_old = fe_index;
-              fe_index +=
-                fe.element_multiplicity(i) * fe.base_element(i).n_components();
-
-              if (fe_index > first_vector_component)
-                break;
-            }
-
-          base_indices.first  = i;
-          base_indices.second = (first_vector_component - fe_index_old) /
-                                fe.base_element(i).n_components();
-        }
-
-      // coordinate directions of
-      // the edges of the face.
-      const unsigned int
-        edge_coordinate_direction[GeometryInfo<dim>::faces_per_cell]
-                                 [GeometryInfo<dim>::lines_per_face] = {
-                                   {2, 2, 1, 1},
-                                   {2, 2, 1, 1},
-                                   {0, 0, 2, 2},
-                                   {0, 0, 2, 2},
-                                   {1, 1, 0, 0},
-                                   {1, 1, 0, 0}};
-      const FEValuesExtractors::Vector vec(first_vector_component);
-
-      // The interpolation for the
-      // lowest order edge shape
-      // functions is just the mean
-      // value of the tangential
-      // components of the boundary
-      // function on the edge.
-      for (unsigned int q_point = 0; q_point < fe_values.n_quadrature_points;
-           ++q_point)
-        {
-          // Therefore compute the
-          // tangential of the edge at
-          // the quadrature point.
-          Point<dim> shifted_reference_point_1 =
-            reference_quadrature_points[q_point];
-          Point<dim> shifted_reference_point_2 =
-            reference_quadrature_points[q_point];
-
-          shifted_reference_point_1(edge_coordinate_direction[face][line]) +=
-            tol;
-          shifted_reference_point_2(edge_coordinate_direction[face][line]) -=
-            tol;
-          tangentials[q_point] =
-            (0.5 *
-             (fe_values.get_mapping().transform_unit_to_real_cell(
-                cell, shifted_reference_point_1) -
-              fe_values.get_mapping().transform_unit_to_real_cell(
-                cell, shifted_reference_point_2)) /
-             tol);
-          tangentials[q_point] /= tangentials[q_point].norm();
-
-          // Compute the degrees of
-          // freedom.
-          for (unsigned int i = 0; i < fe.dofs_per_face; ++i)
-            if (((dynamic_cast<const FESystem<dim> *>(&fe) != nullptr) &&
-                 (fe.system_to_base_index(fe.face_to_cell_index(i, face))
-                    .first == base_indices) &&
-                 (fe.base_element(base_indices.first)
-                    .face_to_cell_index(line * fe.degree, face) <=
-                  fe.system_to_base_index(fe.face_to_cell_index(i, face))
-                    .second) &&
-                 (fe.system_to_base_index(fe.face_to_cell_index(i, face))
-                    .second <=
-                  fe.base_element(base_indices.first)
-                    .face_to_cell_index((line + 1) * fe.degree - 1, face))) ||
-                ((dynamic_cast<const FE_Nedelec<dim> *>(&fe) != nullptr) &&
-                 (line * fe.degree <= i) && (i < (line + 1) * fe.degree)))
-              {
-                const double tangential_solution_component =
-                  (values[q_point](first_vector_component) *
-                     tangentials[q_point][0] +
-                   values[q_point](first_vector_component + 1) *
-                     tangentials[q_point][1] +
-                   values[q_point](first_vector_component + 2) *
-                     tangentials[q_point][2]);
-                dof_values[i] +=
-                  (fe_values.JxW(q_point) * tangential_solution_component *
-                   (fe_values[vec].value(fe.face_to_cell_index(i, face),
-                                         q_point) *
-                    tangentials[q_point]) /
-                   std::sqrt(
-                     jacobians[q_point][0]
-                              [edge_coordinate_direction[face][line]] *
-                       jacobians[q_point][0]
-                                [edge_coordinate_direction[face][line]] +
-                     jacobians[q_point][1]
-                              [edge_coordinate_direction[face][line]] *
-                       jacobians[q_point][1]
-                                [edge_coordinate_direction[face][line]] +
-                     jacobians[q_point][2]
-                              [edge_coordinate_direction[face][line]] *
-                       jacobians[q_point][2]
-                                [edge_coordinate_direction[face][line]]));
-
-                if (q_point == 0)
-                  dofs_processed[i] = true;
-              }
-        }
-    }
-
-    // dummy implementation of above
-    // function for all other
-    // dimensions
-    template <int dim, typename cell_iterator>
-    void
-    compute_edge_projection(const cell_iterator &,
-                            const unsigned int,
-                            const unsigned int,
-                            hp::FEValues<dim> &,
-                            const Function<dim> &,
-                            const unsigned int,
-                            std::vector<double> &,
-                            std::vector<bool> &)
-    {
-      Assert(false, ExcInternalError());
-    }
-
-    // This function computes the
-    // projection of the boundary
-    // function on the interior of
-    // faces.
-    template <int dim, typename cell_iterator, typename number>
-    void
-    compute_face_projection_curl_conforming(
-      const cell_iterator &        cell,
-      const unsigned int           face,
-      hp::FEValues<dim> &          hp_fe_values,
-      const Function<dim, number> &boundary_function,
-      const unsigned int           first_vector_component,
-      std::vector<double> &        dof_values,
-      std::vector<bool> &          dofs_processed)
-    {
-      const unsigned int spacedim = dim;
-      hp_fe_values.reinit(cell,
-                          cell->active_fe_index() *
-                              GeometryInfo<dim>::faces_per_cell +
-                            face);
-      // Initialize the required
-      // objects.
-      const FEValues<dim> &fe_values = hp_fe_values.get_present_fe_values();
-      const FiniteElement<dim> &                           fe = cell->get_fe();
-      const std::vector<DerivativeForm<1, dim, spacedim>> &jacobians =
-        fe_values.get_jacobians();
-      const std::vector<Point<dim>> &quadrature_points =
-        fe_values.get_quadrature_points();
-      const unsigned int                    degree = fe.degree - 1;
-      std::pair<unsigned int, unsigned int> base_indices(0, 0);
-
-      if (dynamic_cast<const FESystem<dim> *>(&cell->get_fe()) != nullptr)
-        {
-          unsigned int fe_index     = 0;
-          unsigned int fe_index_old = 0;
-          unsigned int i            = 0;
-
-          for (; i < fe.n_base_elements(); ++i)
-            {
-              fe_index_old = fe_index;
-              fe_index +=
-                fe.element_multiplicity(i) * fe.base_element(i).n_components();
-
-              if (fe_index > first_vector_component)
-                break;
-            }
-
-          base_indices.first  = i;
-          base_indices.second = (first_vector_component - fe_index_old) /
-                                fe.base_element(i).n_components();
-        }
-
-      std::vector<Vector<double>> values(fe_values.n_quadrature_points,
-                                         Vector<double>(fe.n_components()));
-
-      // Get boundary function
-      // values at quadrature
-      // points.
-      boundary_function.vector_value_list(quadrature_points, values);
-
-      switch (dim)
-        {
-          case 2:
-            {
-              const double tol =
-                0.5 * cell->face(face)->diameter() / cell->get_fe().degree;
-              std::vector<Tensor<1, dim>> tangentials(
-                fe_values.n_quadrature_points);
-
-              const std::vector<Point<dim>> &reference_quadrature_points =
-                fe_values.get_quadrature().get_points();
-
-              // coordinate directions
-              // of the face.
-              const unsigned int
-                face_coordinate_direction[GeometryInfo<dim>::faces_per_cell] = {
-                  1, 1, 0, 0};
-              const FEValuesExtractors::Vector vec(first_vector_component);
-
-              // The interpolation for
-              // the lowest order face
-              // shape functions is just
-              // the mean value of the
-              // tangential  components
-              // of the boundary function
-              // on the edge.
-              for (unsigned int q_point = 0;
-                   q_point < fe_values.n_quadrature_points;
-                   ++q_point)
-                {
-                  // Therefore compute the
-                  // tangential of the
-                  // face at the quadrature
-                  // point.
-                  Point<dim> shifted_reference_point_1 =
-                    reference_quadrature_points[q_point];
-                  Point<dim> shifted_reference_point_2 =
-                    reference_quadrature_points[q_point];
-
-                  shifted_reference_point_1(face_coordinate_direction[face]) +=
-                    tol;
-                  shifted_reference_point_2(face_coordinate_direction[face]) -=
-                    tol;
-                  tangentials[q_point] =
-                    (fe_values.get_mapping().transform_unit_to_real_cell(
-                       cell, shifted_reference_point_1) -
-                     fe_values.get_mapping().transform_unit_to_real_cell(
-                       cell, shifted_reference_point_2)) /
-                    tol;
-                  tangentials[q_point] /= tangentials[q_point].norm();
-
-                  // Compute the degrees
-                  // of freedom.
-                  for (unsigned int i = 0; i < fe.dofs_per_face; ++i)
-                    if (((dynamic_cast<const FESystem<dim> *>(&fe) !=
-                          nullptr) &&
-                         (fe.system_to_base_index(
-                              fe.face_to_cell_index(i, face))
-                            .first == base_indices)) ||
-                        (dynamic_cast<const FE_Nedelec<dim> *>(&fe) != nullptr))
-                      {
-                        dof_values[i] +=
-                          fe_values.JxW(q_point) *
-                          (values[q_point](first_vector_component) *
-                             tangentials[q_point][0] +
-                           values[q_point](first_vector_component + 1) *
-                             tangentials[q_point][1]) *
-                          (fe_values[vec].value(fe.face_to_cell_index(i, face),
-                                                q_point) *
-                           tangentials[q_point]);
-
-                        if (q_point == 0)
-                          dofs_processed[i] = true;
-                      }
-                }
-
-              break;
-            }
-
-          case 3:
-            {
-              const FEValuesExtractors::Vector vec(first_vector_component);
-              FullMatrix<double>               assembling_matrix(
-                degree * fe.degree, dim * fe_values.n_quadrature_points);
-              Vector<double>     assembling_vector(assembling_matrix.n());
-              Vector<double>     cell_rhs(assembling_matrix.m());
-              FullMatrix<double> cell_matrix(assembling_matrix.m(),
-                                             assembling_matrix.m());
-              FullMatrix<double> cell_matrix_inv(assembling_matrix.m(),
-                                                 assembling_matrix.m());
-              Vector<double>     solution(cell_matrix.m());
-
-              // Get coordinate directions
-              // of the face.
-              const unsigned int global_face_coordinate_directions
-                [GeometryInfo<3>::faces_per_cell][2] = {
-                  {1, 2}, {1, 2}, {2, 0}, {2, 0}, {0, 1}, {0, 1}};
-
-              // The projection is divided into two steps.  In the first step we
-              // project the boundary function on the horizontal shape
-              // functions. Then the boundary function is projected on the
-              // vertical shape functions.  We begin with the horizontal shape
-              // functions and set up a linear system of equations to get the
-              // values for degrees of freedom associated with the interior of
-              // the face.
-              for (unsigned int q_point = 0;
-                   q_point < fe_values.n_quadrature_points;
-                   ++q_point)
-                {
-                  // The right hand
-                  // side of the
-                  // corresponding problem
-                  // is the residual
-                  // of the boundary
-                  // function and
-                  // the already
-                  // interpolated part
-                  // on the edges.
-                  Tensor<1, dim> tmp;
-
-                  for (unsigned int d = 0; d < dim; ++d)
-                    tmp[d] = values[q_point](first_vector_component + d);
-
-                  for (unsigned int i = 0; i < fe.dofs_per_face; ++i)
-                    if (((dynamic_cast<const FESystem<dim> *>(&fe) !=
-                          nullptr) &&
-                         (fe.system_to_base_index(
-                              fe.face_to_cell_index(i, face))
-                            .first == base_indices) &&
-                         (fe.base_element(base_indices.first)
-                            .face_to_cell_index(2 * fe.degree, face) <=
-                          fe.system_to_base_index(
-                              fe.face_to_cell_index(i, face))
-                            .second) &&
-                         (fe.system_to_base_index(
-                              fe.face_to_cell_index(i, face))
-                            .second <=
-                          fe.base_element(base_indices.first)
-                            .face_to_cell_index(4 * fe.degree - 1, face))) ||
-                        ((dynamic_cast<const FE_Nedelec<dim> *>(&fe) !=
-                          nullptr) &&
-                         (2 * fe.degree <= i) && (i < 4 * fe.degree)))
-                      tmp -=
-                        dof_values[i] *
-                        fe_values[vec].value(fe.face_to_cell_index(i, face),
-                                             q_point);
-
-                  const double JxW = std::sqrt(
-                    fe_values.JxW(q_point) /
-                    ((jacobians[q_point][0]
-                               [global_face_coordinate_directions[face][0]] *
-                        jacobians[q_point][0]
-                                 [global_face_coordinate_directions[face][0]] +
-                      jacobians[q_point][1]
-                               [global_face_coordinate_directions[face][0]] *
-                        jacobians[q_point][1]
-                                 [global_face_coordinate_directions[face][0]] +
-                      jacobians[q_point][2]
-                               [global_face_coordinate_directions[face][0]] *
-                        jacobians[q_point][2]
-                                 [global_face_coordinate_directions[face][0]]) *
-                     (jacobians[q_point][0]
-                               [global_face_coordinate_directions[face][1]] *
-                        jacobians[q_point][0]
-                                 [global_face_coordinate_directions[face][1]] +
-                      jacobians[q_point][1]
-                               [global_face_coordinate_directions[face][1]] *
-                        jacobians[q_point][1]
-                                 [global_face_coordinate_directions[face][1]] +
-                      jacobians[q_point][2]
-                               [global_face_coordinate_directions[face][1]] *
-                        jacobians[q_point][2]
-                                 [global_face_coordinate_directions[face]
-                                                                   [1]])));
-
-                  // In the weak form
-                  // the right hand
-                  // side function
-                  // is multiplicated
-                  // by the horizontal
-                  // shape functions
-                  // defined in the
-                  // interior of
-                  // the face.
-                  for (unsigned int d = 0; d < dim; ++d)
-                    assembling_vector(dim * q_point + d) = JxW * tmp[d];
-
-                  unsigned int index = 0;
-
-                  for (unsigned int i = 0; i < fe.dofs_per_face; ++i)
-                    if (((dynamic_cast<const FESystem<dim> *>(&fe) !=
-                          nullptr) &&
-                         (fe.system_to_base_index(
-                              fe.face_to_cell_index(i, face))
-                            .first == base_indices) &&
-                         (fe.base_element(base_indices.first)
-                            .face_to_cell_index(
-                              GeometryInfo<dim>::lines_per_face * fe.degree,
-                              face) <=
-                          fe.system_to_base_index(
-                              fe.face_to_cell_index(i, face))
-                            .second) &&
-                         (fe.system_to_base_index(
-                              fe.face_to_cell_index(i, face))
-                            .second <
-                          fe.base_element(base_indices.first)
-                            .face_to_cell_index(
-                              (degree + GeometryInfo<dim>::lines_per_face) *
-                                fe.degree,
-                              face))) ||
-                        ((dynamic_cast<const FE_Nedelec<dim> *>(&fe) !=
-                          nullptr) &&
-                         (GeometryInfo<dim>::lines_per_face * fe.degree <= i) &&
-                         (i < (degree + GeometryInfo<dim>::lines_per_face) *
-                                fe.degree)))
-                      {
-                        const Tensor<1, dim> shape_value =
-                          (JxW *
-                           fe_values[vec].value(fe.face_to_cell_index(i, face),
-                                                q_point));
-
-                        for (unsigned int d = 0; d < dim; ++d)
-                          assembling_matrix(index, dim * q_point + d) =
-                            shape_value[d];
-
-                        ++index;
-                      }
-                }
-
-              // Create the system matrix by multiplying the assembling matrix
-              // with its transposed and the right hand side vector by
-              // multiplying the assembling matrix with the assembling vector.
-              // Invert the system matrix.
-              assembling_matrix.mTmult(cell_matrix, assembling_matrix);
-              cell_matrix_inv.invert(cell_matrix);
-              assembling_matrix.vmult(cell_rhs, assembling_vector);
-              cell_matrix_inv.vmult(solution, cell_rhs);
-
-              // Store the computed
-              // values.
-              {
-                unsigned int index = 0;
-
-                for (unsigned int i = 0; i < fe.dofs_per_face; ++i)
-                  if (((dynamic_cast<const FESystem<dim> *>(&fe) != nullptr) &&
-                       (fe.system_to_base_index(fe.face_to_cell_index(i, face))
-                          .first == base_indices) &&
-                       (fe.base_element(base_indices.first)
-                          .face_to_cell_index(
-                            GeometryInfo<dim>::lines_per_face * fe.degree,
-                            face) <=
-                        fe.system_to_base_index(fe.face_to_cell_index(i, face))
-                          .second) &&
-                       (fe.system_to_base_index(fe.face_to_cell_index(i, face))
-                          .second <
-                        fe.base_element(base_indices.first)
-                          .face_to_cell_index(
-                            (degree + GeometryInfo<dim>::lines_per_face) *
-                              fe.degree,
-                            face))) ||
-                      ((dynamic_cast<const FE_Nedelec<dim> *>(&fe) !=
-                        nullptr) &&
-                       (GeometryInfo<dim>::lines_per_face * fe.degree <= i) &&
-                       (i < (degree + GeometryInfo<dim>::lines_per_face) *
-                              fe.degree)))
-                    {
-                      dof_values[i]     = solution(index);
-                      dofs_processed[i] = true;
-                      ++index;
-                    }
-              }
-
-              // Now we do the same as above with the vertical shape functions
-              // instead of the horizontal ones.
-              for (unsigned int q_point = 0;
-                   q_point < fe_values.n_quadrature_points;
-                   ++q_point)
-                {
-                  Tensor<1, dim> tmp;
-
-                  for (unsigned int d = 0; d < dim; ++d)
-                    tmp[d] = values[q_point](first_vector_component + d);
-
-                  for (unsigned int i = 0; i < fe.dofs_per_face; ++i)
-                    if (((dynamic_cast<const FESystem<dim> *>(&fe) !=
-                          nullptr) &&
-                         (fe.system_to_base_index(
-                              fe.face_to_cell_index(i, face))
-                            .first == base_indices) &&
-                         (fe.system_to_base_index(
-                              fe.face_to_cell_index(i, face))
-                            .second <=
-                          fe.base_element(base_indices.first)
-                            .face_to_cell_index(2 * fe.degree - 1, face)) &&
-                         (fe.system_to_base_index(
-                              fe.face_to_cell_index(i, face))
-                            .second >= fe.base_element(base_indices.first)
-                                         .face_to_cell_index(0, face))) ||
-                        ((dynamic_cast<const FE_Nedelec<dim> *>(&fe) !=
-                          nullptr) &&
-                         (i < 2 * fe.degree)))
-                      tmp -=
-                        dof_values[i] *
-                        fe_values[vec].value(fe.face_to_cell_index(i, face),
-                                             q_point);
-
-                  const double JxW = std::sqrt(
-                    fe_values.JxW(q_point) /
-                    ((jacobians[q_point][0]
-                               [global_face_coordinate_directions[face][0]] *
-                        jacobians[q_point][0]
-                                 [global_face_coordinate_directions[face][0]] +
-                      jacobians[q_point][1]
-                               [global_face_coordinate_directions[face][0]] *
-                        jacobians[q_point][1]
-                                 [global_face_coordinate_directions[face][0]] +
-                      jacobians[q_point][2]
-                               [global_face_coordinate_directions[face][0]] *
-                        jacobians[q_point][2]
-                                 [global_face_coordinate_directions[face][0]]) *
-                     (jacobians[q_point][0]
-                               [global_face_coordinate_directions[face][1]] *
-                        jacobians[q_point][0]
-                                 [global_face_coordinate_directions[face][1]] +
-                      jacobians[q_point][1]
-                               [global_face_coordinate_directions[face][1]] *
-                        jacobians[q_point][1]
-                                 [global_face_coordinate_directions[face][1]] +
-                      jacobians[q_point][2]
-                               [global_face_coordinate_directions[face][1]] *
-                        jacobians[q_point][2]
-                                 [global_face_coordinate_directions[face]
-                                                                   [1]])));
-
-                  for (unsigned int d = 0; d < dim; ++d)
-                    assembling_vector(dim * q_point + d) = JxW * tmp[d];
-
-                  unsigned int index = 0;
-
-                  for (unsigned int i = 0; i < fe.dofs_per_face; ++i)
-                    if (((dynamic_cast<const FESystem<dim> *>(&fe) !=
-                          nullptr) &&
-                         (fe.system_to_base_index(
-                              fe.face_to_cell_index(i, face))
-                            .first == base_indices) &&
-                         (fe.base_element(base_indices.first)
-                            .face_to_cell_index(
-                              (degree + GeometryInfo<dim>::lines_per_face) *
-                                fe.degree,
-                              face) <=
-                          fe.system_to_base_index(
-                              fe.face_to_cell_index(i, face))
-                            .second)) ||
-                        ((dynamic_cast<const FE_Nedelec<dim> *>(&fe) !=
-                          nullptr) &&
-                         ((degree + GeometryInfo<dim>::lines_per_face) *
-                            fe.degree <=
-                          i)))
-                      {
-                        const Tensor<1, dim> shape_value =
-                          JxW *
-                          fe_values[vec].value(fe.face_to_cell_index(i, face),
-                                               q_point);
-
-                        for (unsigned int d = 0; d < dim; ++d)
-                          assembling_matrix(index, dim * q_point + d) =
-                            shape_value[d];
-
-                        ++index;
-                      }
-                }
-
-              assembling_matrix.mTmult(cell_matrix, assembling_matrix);
-              cell_matrix_inv.invert(cell_matrix);
-              assembling_matrix.vmult(cell_rhs, assembling_vector);
-              cell_matrix_inv.vmult(solution, cell_rhs);
-
-              unsigned int index = 0;
-
-              for (unsigned int i = 0; i < fe.dofs_per_face; ++i)
-                if (((dynamic_cast<const FESystem<dim> *>(&fe) != nullptr) &&
-                     (fe.system_to_base_index(fe.face_to_cell_index(i, face))
-                        .first == base_indices) &&
-                     (fe.base_element(base_indices.first)
-                        .face_to_cell_index(
-                          (degree + GeometryInfo<dim>::lines_per_face) *
-                            fe.degree,
-                          face) <=
-                      fe.system_to_base_index(fe.face_to_cell_index(i, face))
-                        .second)) ||
-                    ((dynamic_cast<const FE_Nedelec<dim> *>(&fe) != nullptr) &&
-                     ((degree + GeometryInfo<dim>::lines_per_face) *
-                        fe.degree <=
-                      i)))
-                  {
-                    dof_values[i]     = solution(index);
-                    dofs_processed[i] = true;
-                    ++index;
-                  }
-
-              break;
-            }
-
-          default:
-            Assert(false, ExcNotImplemented());
-        }
-    }
-  } // namespace internals
-
-
-
-  template <int dim>
-  void
-
-  project_boundary_values_curl_conforming(
-    const DoFHandler<dim> &    dof_handler,
-    const unsigned int         first_vector_component,
-    const Function<dim> &      boundary_function,
-    const types::boundary_id   boundary_component,
-    AffineConstraints<double> &constraints,
-    const Mapping<dim> &       mapping)
-  {
-    // Projection-based interpolation is performed in two (in 2D) respectively
-    // three (in 3D) steps. First the tangential component of the function is
-    // interpolated on each edge.  This gives the values for the degrees of
-    // freedom corresponding to the edge shape functions. Now we are done for
-    // 2D, but in 3D we possibly have also degrees of freedom, which are
-    // located in the interior of the faces. Therefore we compute the residual
-    // of the function describing the boundary values and the interpolated
-    // part, which we have computed in the last step. On the faces there are
-    // two kinds of shape functions, the horizontal and the vertical
-    // ones. Thus we have to solve two linear systems of equations of size
-    // <tt>degree * (degree + 1)<tt> to obtain the values for the
-    // corresponding degrees of freedom.
-    const unsigned int    superdegree = dof_handler.get_fe().degree;
-    const QGauss<dim - 1> reference_face_quadrature(2 * superdegree);
-    const unsigned int    dofs_per_face = dof_handler.get_fe().dofs_per_face;
-    const hp::FECollection<dim> &fe_collection(dof_handler.get_fe_collection());
-    const hp::MappingCollection<dim> mapping_collection(mapping);
-    hp::QCollection<dim>             face_quadrature_collection;
-
-    for (unsigned int face : GeometryInfo<dim>::face_indices())
-      face_quadrature_collection.push_back(
-        QProjector<dim>::project_to_face(reference_face_quadrature, face));
-
-    hp::FEValues<dim> fe_face_values(mapping_collection,
-                                     fe_collection,
-                                     face_quadrature_collection,
-                                     update_jacobians | update_JxW_values |
-                                       update_quadrature_points |
-                                       update_values);
-
-    std::vector<bool>                    dofs_processed(dofs_per_face);
-    std::vector<double>                  dof_values(dofs_per_face);
-    std::vector<types::global_dof_index> face_dof_indices(dofs_per_face);
-    typename DoFHandler<dim>::active_cell_iterator cell =
-      dof_handler.begin_active();
-
-    switch (dim)
-      {
-        case 2:
-          {
-            for (; cell != dof_handler.end(); ++cell)
-              if (cell->at_boundary() && cell->is_locally_owned())
-                for (const unsigned int face :
-                     GeometryInfo<dim>::face_indices())
-                  if (cell->face(face)->boundary_id() == boundary_component)
-                    {
-                      // if the FE is a
-                      // FE_Nothing object
-                      // there is no work to
-                      // do
-                      if (dynamic_cast<const FE_Nothing<dim> *>(
-                            &cell->get_fe()) != nullptr)
-                        return;
-
-                      // This is only
-                      // implemented, if the
-                      // FE is a Nedelec
-                      // element. If the FE
-                      // is a FESystem, we
-                      // cannot check this.
-                      if (dynamic_cast<const FESystem<dim> *>(
-                            &cell->get_fe()) == nullptr)
-                        {
-                          AssertThrow(
-                            dynamic_cast<const FE_Nedelec<dim> *>(
-                              &cell->get_fe()) != nullptr,
-                            (typename FiniteElement<
-                              dim>::ExcInterpolationNotImplemented()));
-                        }
-
-                      for (unsigned int dof = 0; dof < dofs_per_face; ++dof)
-                        {
-                          dof_values[dof]     = 0.0;
-                          dofs_processed[dof] = false;
-                        }
-
-                      // Compute the
-                      // projection of the
-                      // boundary function on
-                      // the edge.
-                      internals ::compute_face_projection_curl_conforming(
-                        cell,
-                        face,
-                        fe_face_values,
-                        boundary_function,
-                        first_vector_component,
-                        dof_values,
-                        dofs_processed);
-                      cell->face(face)->get_dof_indices(
-                        face_dof_indices, cell->active_fe_index());
-
-                      // Add the computed constraints to the constraints
-                      // object, if the degree of freedom is not already
-                      // constrained.
-                      for (unsigned int dof = 0; dof < dofs_per_face; ++dof)
-                        if (dofs_processed[dof] &&
-                            constraints.can_store_line(face_dof_indices[dof]) &&
-                            !(constraints.is_constrained(
-                              face_dof_indices[dof])))
-                          {
-                            constraints.add_line(face_dof_indices[dof]);
-
-                            if (std::abs(dof_values[dof]) > 1e-13)
-                              constraints.set_inhomogeneity(
-                                face_dof_indices[dof], dof_values[dof]);
-                          }
-                    }
-
-            break;
-          }
-
-        case 3:
-          {
-            const QGauss<dim - 2> reference_edge_quadrature(2 * superdegree);
-            const unsigned int    degree = superdegree - 1;
-            hp::QCollection<dim>  edge_quadrature_collection;
-
-            for (const unsigned int face : GeometryInfo<dim>::face_indices())
-              for (unsigned int line = 0;
-                   line < GeometryInfo<dim>::lines_per_face;
-                   ++line)
-                edge_quadrature_collection.push_back(
-                  QProjector<dim>::project_to_face(
-                    QProjector<dim - 1>::project_to_face(
-                      reference_edge_quadrature, line),
-                    face));
-
-            hp::FEValues<dim> fe_edge_values(mapping_collection,
-                                             fe_collection,
-                                             edge_quadrature_collection,
-                                             update_jacobians |
-                                               update_JxW_values |
-                                               update_quadrature_points |
-                                               update_values);
-
-            for (; cell != dof_handler.end(); ++cell)
-              if (cell->at_boundary() && cell->is_locally_owned())
-                for (const unsigned int face :
-                     GeometryInfo<dim>::face_indices())
-                  if (cell->face(face)->boundary_id() == boundary_component)
-                    {
-                      // if the FE is a
-                      // FE_Nothing object
-                      // there is no work to
-                      // do
-                      if (dynamic_cast<const FE_Nothing<dim> *>(
-                            &cell->get_fe()) != nullptr)
-                        return;
-
-                      // This is only
-                      // implemented, if the
-                      // FE is a Nedelec
-                      // element. If the FE is
-                      // a FESystem we cannot
-                      // check this.
-                      if (dynamic_cast<const FESystem<dim> *>(
-                            &cell->get_fe()) == nullptr)
-                        {
-                          AssertThrow(dynamic_cast<const FE_Nedelec<dim> *>(
-                                        &cell->get_fe()) != nullptr,
-                                      typename FiniteElement<
-                                        dim>::ExcInterpolationNotImplemented());
-                        }
-
-                      for (unsigned int dof = 0; dof < dofs_per_face; ++dof)
-                        {
-                          dof_values[dof]     = 0.0;
-                          dofs_processed[dof] = false;
-                        }
-
-                      // First we compute the
-                      // projection on the
-                      // edges.
-                      for (unsigned int line = 0;
-                           line < GeometryInfo<3>::lines_per_face;
-                           ++line)
-                        internals ::compute_edge_projection(
-                          cell,
-                          face,
-                          line,
-                          fe_edge_values,
-                          boundary_function,
-                          first_vector_component,
-                          dof_values,
-                          dofs_processed);
-
-                      // If there are higher
-                      // order shape
-                      // functions, there is
-                      // still some work
-                      // left.
-                      if (degree > 0)
-                        internals ::compute_face_projection_curl_conforming(
-                          cell,
-                          face,
-                          fe_face_values,
-                          boundary_function,
-                          first_vector_component,
-                          dof_values,
-                          dofs_processed);
-
-                      // Store the computed
-                      // values in the global
-                      // vector.
-                      cell->face(face)->get_dof_indices(
-                        face_dof_indices, cell->active_fe_index());
-
-                      for (unsigned int dof = 0; dof < dofs_per_face; ++dof)
-                        if (dofs_processed[dof] &&
-                            constraints.can_store_line(face_dof_indices[dof]) &&
-                            !(constraints.is_constrained(
-                              face_dof_indices[dof])))
-                          {
-                            constraints.add_line(face_dof_indices[dof]);
-
-                            if (std::abs(dof_values[dof]) > 1e-13)
-                              constraints.set_inhomogeneity(
-                                face_dof_indices[dof], dof_values[dof]);
-                          }
-                    }
-
-            break;
-          }
-
-        default:
-          Assert(false, ExcNotImplemented());
-      }
-  }
-
-
-
-  template <int dim>
-  void
-
-  project_boundary_values_curl_conforming(
-    const hp::DoFHandler<dim> &       dof_handler,
-    const unsigned int                first_vector_component,
-    const Function<dim> &             boundary_function,
-    const types::boundary_id          boundary_component,
-    AffineConstraints<double> &       constraints,
-    const hp::MappingCollection<dim> &mapping_collection)
-  {
-    const hp::FECollection<dim> &fe_collection(dof_handler.get_fe_collection());
-    hp::QCollection<dim>         face_quadrature_collection;
-
-    for (unsigned int i = 0; i < fe_collection.size(); ++i)
-      {
-        const QGauss<dim - 1> reference_face_quadrature(
-          2 * fe_collection[i].degree);
-
-        for (unsigned int face : GeometryInfo<dim>::face_indices())
-          face_quadrature_collection.push_back(
-            QProjector<dim>::project_to_face(reference_face_quadrature, face));
-      }
-
-    hp::FEValues<dim>                    fe_face_values(mapping_collection,
-                                     fe_collection,
-                                     face_quadrature_collection,
-                                     update_jacobians | update_JxW_values |
-                                       update_quadrature_points |
-                                       update_values);
-    std::vector<bool>                    dofs_processed;
-    std::vector<double>                  dof_values;
-    std::vector<types::global_dof_index> face_dof_indices;
-    typename hp::DoFHandler<dim>::active_cell_iterator cell =
-      dof_handler.begin_active();
-
-    switch (dim)
-      {
-        case 2:
-          {
-            for (; cell != dof_handler.end(); ++cell)
-              if (cell->at_boundary() && cell->is_locally_owned())
-                for (const unsigned int face :
-                     GeometryInfo<dim>::face_indices())
-                  if (cell->face(face)->boundary_id() == boundary_component)
-                    {
-                      // if the FE is a FE_Nothing object there is no work to do
-                      if (dynamic_cast<const FE_Nothing<dim> *>(
-                            &cell->get_fe()) != nullptr)
-                        return;
-
-                      // This is only implemented, if the FE is a Nedelec
-                      // element. If the FE is a FESystem we cannot check this.
-                      if (dynamic_cast<const FESystem<dim> *>(
-                            &cell->get_fe()) == nullptr)
-                        {
-                          AssertThrow(dynamic_cast<const FE_Nedelec<dim> *>(
-                                        &cell->get_fe()) != nullptr,
-                                      typename FiniteElement<
-                                        dim>::ExcInterpolationNotImplemented());
-                        }
-
-                      const unsigned int dofs_per_face =
-                        cell->get_fe().dofs_per_face;
-
-                      dofs_processed.resize(dofs_per_face);
-                      dof_values.resize(dofs_per_face);
-
-                      for (unsigned int dof = 0; dof < dofs_per_face; ++dof)
-                        {
-                          dof_values[dof]     = 0.0;
-                          dofs_processed[dof] = false;
-                        }
-
-                      internals ::compute_face_projection_curl_conforming(
-                        cell,
-                        face,
-                        fe_face_values,
-                        boundary_function,
-                        first_vector_component,
-                        dof_values,
-                        dofs_processed);
-                      face_dof_indices.resize(dofs_per_face);
-                      cell->face(face)->get_dof_indices(
-                        face_dof_indices, cell->active_fe_index());
-
-                      for (unsigned int dof = 0; dof < dofs_per_face; ++dof)
-                        if (dofs_processed[dof] &&
-                            constraints.can_store_line(face_dof_indices[dof]) &&
-                            !(constraints.is_constrained(
-                              face_dof_indices[dof])))
-                          {
-                            constraints.add_line(face_dof_indices[dof]);
-
-                            if (std::abs(dof_values[dof]) > 1e-13)
-                              constraints.set_inhomogeneity(
-                                face_dof_indices[dof], dof_values[dof]);
-                          }
-                    }
-
-            break;
-          }
-
-        case 3:
-          {
-            hp::QCollection<dim> edge_quadrature_collection;
-
-            for (unsigned int i = 0; i < fe_collection.size(); ++i)
-              {
-                const QGauss<dim - 2> reference_edge_quadrature(
-                  2 * fe_collection[i].degree);
-
-                for (const unsigned int face :
-                     GeometryInfo<dim>::face_indices())
-                  for (unsigned int line = 0;
-                       line < GeometryInfo<dim>::lines_per_face;
-                       ++line)
-                    edge_quadrature_collection.push_back(
-                      QProjector<dim>::project_to_face(
-                        QProjector<dim - 1>::project_to_face(
-                          reference_edge_quadrature, line),
-                        face));
-              }
-
-            hp::FEValues<dim> fe_edge_values(mapping_collection,
-                                             fe_collection,
-                                             edge_quadrature_collection,
-                                             update_jacobians |
-                                               update_JxW_values |
-                                               update_quadrature_points |
-                                               update_values);
-
-            for (; cell != dof_handler.end(); ++cell)
-              if (cell->at_boundary() && cell->is_locally_owned())
-                for (const unsigned int face :
-                     GeometryInfo<dim>::face_indices())
-                  if (cell->face(face)->boundary_id() == boundary_component)
-                    {
-                      // if the FE is a FE_Nothing object there is no work to do
-                      if (dynamic_cast<const FE_Nothing<dim> *>(
-                            &cell->get_fe()) != nullptr)
-                        return;
-
-                      // This is only implemented, if the FE is a Nedelec
-                      // element. If the FE is a FESystem we cannot check this.
-                      if (dynamic_cast<const FESystem<dim> *>(
-                            &cell->get_fe()) == nullptr)
-                        {
-                          AssertThrow(dynamic_cast<const FE_Nedelec<dim> *>(
-                                        &cell->get_fe()) != nullptr,
-                                      typename FiniteElement<
-                                        dim>::ExcInterpolationNotImplemented());
-                        }
-
-                      const unsigned int superdegree = cell->get_fe().degree;
-                      const unsigned int degree      = superdegree - 1;
-                      const unsigned int dofs_per_face =
-                        cell->get_fe().dofs_per_face;
-
-                      dofs_processed.resize(dofs_per_face);
-                      dof_values.resize(dofs_per_face);
-
-                      for (unsigned int dof = 0; dof < dofs_per_face; ++dof)
-                        {
-                          dof_values[dof]     = 0.0;
-                          dofs_processed[dof] = false;
-                        }
-
-                      for (unsigned int line = 0;
-                           line < GeometryInfo<dim>::lines_per_face;
-                           ++line)
-                        internals ::compute_edge_projection(
-                          cell,
-                          face,
-                          line,
-                          fe_edge_values,
-                          boundary_function,
-                          first_vector_component,
-                          dof_values,
-                          dofs_processed);
-
-                      // If there are higher order shape functions, there is
-                      // still some work left.
-                      if (degree > 0)
-                        internals ::compute_face_projection_curl_conforming(
-                          cell,
-                          face,
-                          fe_face_values,
-                          boundary_function,
-                          first_vector_component,
-                          dof_values,
-                          dofs_processed);
-
-
-                      face_dof_indices.resize(dofs_per_face);
-                      cell->face(face)->get_dof_indices(
-                        face_dof_indices, cell->active_fe_index());
-
-                      for (unsigned int dof = 0; dof < dofs_per_face; ++dof)
-                        if (dofs_processed[dof] &&
-                            constraints.can_store_line(face_dof_indices[dof]) &&
-                            !(constraints.is_constrained(
-                              face_dof_indices[dof])))
-                          {
-                            constraints.add_line(face_dof_indices[dof]);
-
-                            if (std::abs(dof_values[dof]) > 1e-13)
-                              constraints.set_inhomogeneity(
-                                face_dof_indices[dof], dof_values[dof]);
-                          }
-                    }
-
-            break;
-          }
-
-        default:
-          Assert(false, ExcNotImplemented());
-      }
-  }
-
-
-  namespace internals
-  {
-    template <int dim, typename cell_iterator, typename number>
-    typename std::enable_if<dim == 3>::type
-    compute_edge_projection_l2(const cell_iterator &        cell,
-                               const unsigned int           face,
-                               const unsigned int           line,
-                               hp::FEValues<dim> &          hp_fe_values,
-                               const Function<dim, number> &boundary_function,
-                               const unsigned int   first_vector_component,
-                               std::vector<number> &dof_values,
-                               std::vector<bool> &  dofs_processed)
-    {
-      // This function computes the L2-projection of the given
-      // boundary function on 3D edges and returns the constraints
-      // associated with the edge functions for the given cell.
-      //
-      // In the context of this function, by associated DoFs we mean:
-      // the DoFs corresponding to the group of components making up the vector
-      // with first component first_vector_component (length dim).
-      const FiniteElement<dim> &fe = cell->get_fe();
-
-      // reinit for this cell, face and line.
-      hp_fe_values.reinit(
-        cell,
-        (cell->active_fe_index() * GeometryInfo<dim>::faces_per_cell + face) *
-            GeometryInfo<dim>::lines_per_face +
-          line);
-
-      // Initialize the required objects.
-      const FEValues<dim> &fe_values = hp_fe_values.get_present_fe_values();
-
-      const std::vector<Point<dim>> &quadrature_points =
-        fe_values.get_quadrature_points();
-      std::vector<Vector<number>> values(fe_values.n_quadrature_points,
-                                         Vector<number>(fe.n_components()));
-
-      // Get boundary function values
-      // at quadrature points.
-      boundary_function.vector_value_list(quadrature_points, values);
-
-      // Find the group of vector components we want to project onto
-      // (dim of them, starting at first_vector_component) within the
-      // overall finite element (which may be an FESystem).
-      std::pair<unsigned int, unsigned int> base_indices(0, 0);
-      if (dynamic_cast<const FESystem<dim> *>(&cell->get_fe()) != nullptr)
-        {
-          unsigned int fe_index     = 0;
-          unsigned int fe_index_old = 0;
-          unsigned int i            = 0;
-
-          // Find base element:
-          // base_indices.first
-          //
-          // Then select which copy of that base element
-          // [ each copy is of length
-          // fe.base_element(base_indices.first).n_components() ] corresponds to
-          // first_vector_component: base_index.second
-          for (; i < fe.n_base_elements(); ++i)
-            {
-              fe_index_old = fe_index;
-              fe_index +=
-                fe.element_multiplicity(i) * fe.base_element(i).n_components();
-
-              if (fe_index > first_vector_component)
-                break;
-            }
-
-          base_indices.first  = i;
-          base_indices.second = (first_vector_component - fe_index_old) /
-                                fe.base_element(i).n_components();
-        }
-      else
-        // The only other element we know how to deal with (so far) is
-        // FE_Nedelec, which has one base element and one copy of it
-        // (with 3 components). In that case, the values of
-        // 'base_indices' as initialized above are correct.
-        Assert((dynamic_cast<const FE_Nedelec<dim> *>(&cell->get_fe()) !=
-                nullptr) ||
-                 (dynamic_cast<const FE_NedelecSZ<dim> *>(&cell->get_fe()) !=
-                  nullptr),
-               ExcNotImplemented());
-
-
-      // Store the 'degree' of the Nedelec element as fe.degree-1. For
-      // Nedelec elements, FE_Nedelec<dim>(0) returns fe.degree = 1
-      // because fe.degree stores the *polynomial* degree, not the
-      // degree of the element (which is typically defined based on
-      // the largest polynomial space that is *complete* within the
-      // finite element).
-      const unsigned int degree =
-        fe.base_element(base_indices.first).degree - 1;
-
-      // Find DoFs we want to constrain: There are
-      // fe.base_element(base_indices.first).dofs_per_line DoFs
-      // associated with the given line on the given face on the given
-      // cell.
-      //
-      // We need to know which of these DoFs (there are degree+1 of interest)
-      // are associated with the components given by first_vector_component.
-      // Then we can make a map from the associated line DoFs to the face DoFs.
-      //
-      // For a single FE_Nedelec<3> element this is simple:
-      //    We know the ordering of local DoFs goes
-      //    lines -> faces -> cells
-      //
-      // For a set of FESystem<3> elements we need to pick out the matching base
-      // element and the index within this ordering.
-      //
-      // We call the map associated_edge_dof_to_face_dof
-      std::vector<unsigned int> associated_edge_dof_to_face_dof(
-        degree + 1, numbers::invalid_unsigned_int);
-
-      // Lowest DoF in the base element allowed for this edge:
-      const unsigned int lower_bound =
-        fe.base_element(base_indices.first)
-          .face_to_cell_index(line * (degree + 1), face);
-      // Highest DoF in the base element allowed for this edge:
-      const unsigned int upper_bound =
-        fe.base_element(base_indices.first)
-          .face_to_cell_index((line + 1) * (degree + 1) - 1, face);
-
-      unsigned int associated_edge_dof_index = 0;
-      for (unsigned int line_dof_idx = 0; line_dof_idx < fe.dofs_per_line;
-           ++line_dof_idx)
-        {
-          // For each DoF associated with the (interior of) the line, we need
-          // to figure out which base element it belongs to and then if
-          // that's the correct base element. This is complicated by the
-          // fact that the FiniteElement class has functions that translate
-          // from face to cell, but not from edge to cell index systems. So
-          // we have to do that step by step.
-          //
-          // DoFs on a face in 3d are numbered in order by vertices then lines
-          // then faces.
-          // i.e. line 0 has degree+1 dofs numbered 0,..,degree
-          //      line 1 has degree+1 dofs numbered (degree+1),..,2*(degree+1)
-          //      and so on.
-
-          const unsigned int face_dof_idx =
-            GeometryInfo<dim>::vertices_per_face * fe.dofs_per_vertex +
-            line * fe.dofs_per_line + line_dof_idx;
-
-          // Note, assuming that the edge orientations are "standard"
-          //       i.e. cell->line_orientation(line) = true.
-          Assert(cell->line_orientation(line),
-                 ExcMessage("Edge orientation does not meet expectation."));
-          // Next, translate from face to cell. Note, this might be assuming
-          // that the edge orientations are "standard" (not sure any more at
-          // this time), i.e.
-          //       cell->line_orientation(line) = true.
-          const unsigned int cell_dof_idx =
-            fe.face_to_cell_index(face_dof_idx, face);
-
-          // Check that this cell_idx belongs to the correct base_element,
-          // component and line. We do this for each of the supported elements
-          // separately
-          bool dof_is_of_interest = false;
-          if (dynamic_cast<const FESystem<dim> *>(&fe) != nullptr)
-            {
-              dof_is_of_interest =
-                (fe.system_to_base_index(cell_dof_idx).first == base_indices) &&
-                (lower_bound <= fe.system_to_base_index(cell_dof_idx).second) &&
-                (fe.system_to_base_index(cell_dof_idx).second <= upper_bound);
-            }
-          else if ((dynamic_cast<const FE_Nedelec<dim> *>(&fe) != nullptr) ||
-                   (dynamic_cast<const FE_NedelecSZ<dim> *>(&fe) != nullptr))
-            {
-              Assert((line * (degree + 1) <= face_dof_idx) &&
-                       (face_dof_idx < (line + 1) * (degree + 1)),
-                     ExcInternalError());
-              dof_is_of_interest = true;
-            }
-          else
-            Assert(false, ExcNotImplemented());
-
-          if (dof_is_of_interest)
-            {
-              associated_edge_dof_to_face_dof[associated_edge_dof_index] =
-                face_dof_idx;
-              ++associated_edge_dof_index;
-            }
-        }
-      // Sanity check:
-      const unsigned int n_associated_edge_dofs = associated_edge_dof_index;
-      Assert(n_associated_edge_dofs == degree + 1,
-             ExcMessage("Error: Unexpected number of 3D edge DoFs"));
-
-      // Matrix and RHS vectors to store linear system:
-      // We have (degree+1) basis functions for an edge
-      FullMatrix<number> edge_matrix(degree + 1, degree + 1);
-      FullMatrix<number> edge_matrix_inv(degree + 1, degree + 1);
-      Vector<number>     edge_rhs(degree + 1);
-      Vector<number>     edge_solution(degree + 1);
-
-      const FEValuesExtractors::Vector vec(first_vector_component);
-
-      // coordinate directions of
-      // the edges of the face.
-      const unsigned int
-        edge_coordinate_direction[GeometryInfo<dim>::faces_per_cell]
-                                 [GeometryInfo<dim>::lines_per_face] = {
-                                   {2, 2, 1, 1},
-                                   {2, 2, 1, 1},
-                                   {0, 0, 2, 2},
-                                   {0, 0, 2, 2},
-                                   {1, 1, 0, 0},
-                                   {1, 1, 0, 0}};
-
-      const double tol =
-        0.5 * cell->face(face)->line(line)->diameter() / fe.degree;
-      const std::vector<Point<dim>> &reference_quadrature_points =
-        fe_values.get_quadrature().get_points();
-
-      // Project the boundary function onto the shape functions for this edge
-      // and set up a linear system of equations to get the values for the DoFs
-      // associated with this edge.
-      for (unsigned int q_point = 0; q_point < fe_values.n_quadrature_points;
-           ++q_point)
-        {
-          // Compute the tangential
-          // of the edge at
-          // the quadrature point.
-          Point<dim> shifted_reference_point_1 =
-            reference_quadrature_points[q_point];
-          Point<dim> shifted_reference_point_2 =
-            reference_quadrature_points[q_point];
-
-          shifted_reference_point_1(edge_coordinate_direction[face][line]) +=
-            tol;
-          shifted_reference_point_2(edge_coordinate_direction[face][line]) -=
-            tol;
-          Tensor<1, dim> tangential =
-            (0.5 *
-             (fe_values.get_mapping().transform_unit_to_real_cell(
-                cell, shifted_reference_point_1) -
-              fe_values.get_mapping().transform_unit_to_real_cell(
-                cell, shifted_reference_point_2)) /
-             tol);
-          tangential /= tangential.norm();
-
-          // Compute the entries of the linear system
-          // Note the system is symmetric so we could only compute the
-          // lower/upper triangle.
-          //
-          // The matrix entries are
-          // \int_{edge}
-          // (tangential*edge_shape_function_i)*(tangential*edge_shape_function_j)
-          // dS
-          //
-          // The RHS entries are:
-          // \int_{edge}
-          // (tangential*boundary_value)*(tangential*edge_shape_function_i) dS.
-          for (unsigned int j = 0; j < n_associated_edge_dofs; ++j)
-            {
-              const unsigned int j_face_idx =
-                associated_edge_dof_to_face_dof[j];
-              const unsigned int j_cell_idx =
-                fe.face_to_cell_index(j_face_idx, face);
-              for (unsigned int i = 0; i < n_associated_edge_dofs; ++i)
-                {
-                  const unsigned int i_face_idx =
-                    associated_edge_dof_to_face_dof[i];
-                  const unsigned int i_cell_idx =
-                    fe.face_to_cell_index(i_face_idx, face);
-
-                  edge_matrix(i, j) +=
-                    fe_values.JxW(q_point) *
-                    (fe_values[vec].value(i_cell_idx, q_point) * tangential) *
-                    (fe_values[vec].value(j_cell_idx, q_point) * tangential);
-                }
-              // Compute the RHS entries:
-              edge_rhs(j) +=
-                fe_values.JxW(q_point) *
-                (values[q_point](first_vector_component) * tangential[0] +
-                 values[q_point](first_vector_component + 1) * tangential[1] +
-                 values[q_point](first_vector_component + 2) * tangential[2]) *
-                (fe_values[vec].value(j_cell_idx, q_point) * tangential);
-            }
-        }
-
-      // Invert linear system
-      edge_matrix_inv.invert(edge_matrix);
-      edge_matrix_inv.vmult(edge_solution, edge_rhs);
-
-      // Store computed DoFs
-      for (unsigned int i = 0; i < n_associated_edge_dofs; ++i)
-        {
-          dof_values[associated_edge_dof_to_face_dof[i]]     = edge_solution(i);
-          dofs_processed[associated_edge_dof_to_face_dof[i]] = true;
-        }
-    }
-
-
-    template <int dim, typename cell_iterator, typename number>
-    typename std::enable_if<dim != 3>::type
-    compute_edge_projection_l2(const cell_iterator &,
-                               const unsigned int,
-                               const unsigned int,
-                               hp::FEValues<dim> &,
-                               const Function<dim, number> &,
-                               const unsigned int,
-                               std::vector<number> &,
-                               std::vector<bool> &)
-    {
-      // dummy implementation of above function
-      // for all other dimensions
-      Assert(false, ExcInternalError());
-    }
-
-    template <int dim, typename cell_iterator, typename number>
-    void
-    compute_face_projection_curl_conforming_l2(
-      const cell_iterator &        cell,
-      const unsigned int           face,
-      hp::FEFaceValues<dim> &      hp_fe_face_values,
-      const Function<dim, number> &boundary_function,
-      const unsigned int           first_vector_component,
-      std::vector<number> &        dof_values,
-      std::vector<bool> &          dofs_processed)
-    {
-      // This function computes the L2-projection of the boundary
-      // function on the interior of faces only. In 3D, this should only be
-      // called after first calling compute_edge_projection_l2, as it relies on
-      // edge constraints which are found.
-
-      // In the context of this function, by associated DoFs we mean:
-      // the DoFs corresponding to the group of components making up the vector
-      // with first component first_vector_component (with total components
-      // dim).
-
-      // Copy to the standard FEFaceValues object:
-      hp_fe_face_values.reinit(cell, face);
-      const FEFaceValues<dim> &fe_face_values =
-        hp_fe_face_values.get_present_fe_values();
-
-      // Initialize the required objects.
-      const FiniteElement<dim> &     fe = cell->get_fe();
-      const std::vector<Point<dim>> &quadrature_points =
-        fe_face_values.get_quadrature_points();
-
-      std::vector<Vector<number>> values(fe_face_values.n_quadrature_points,
-                                         Vector<number>(fe.n_components()));
-
-      // Get boundary function values at quadrature points.
-      boundary_function.vector_value_list(quadrature_points, values);
-
-      // Find where the group of vector components (dim of them,
-      // starting at first_vector_component) are within an FESystem.
-      //
-      // If not using FESystem then must be using FE_Nedelec,
-      // which has one base element and one copy of it (with 3 components).
-      std::pair<unsigned int, unsigned int> base_indices(0, 0);
-      if (dynamic_cast<const FESystem<dim> *>(&cell->get_fe()) != nullptr)
-        {
-          unsigned int fe_index     = 0;
-          unsigned int fe_index_old = 0;
-          unsigned int i            = 0;
-
-          // Find base element:
-          // base_indices.first
-          //
-          // Then select which copy of that base element
-          // [ each copy is of length
-          // fe.base_element(base_indices.first).n_components() ] corresponds to
-          // first_vector_component: base_index.second
-          for (; i < fe.n_base_elements(); ++i)
-            {
-              fe_index_old = fe_index;
-              fe_index +=
-                fe.element_multiplicity(i) * fe.base_element(i).n_components();
-
-              if (fe_index > first_vector_component)
-                break;
-            }
-          base_indices.first  = i;
-          base_indices.second = (first_vector_component - fe_index_old) /
-                                fe.base_element(i).n_components();
-        }
-      else
-        {
-          // Assert that the FE is in fact an FE_Nedelec, so that the default
-          // base_indices == (0,0) is correct.
-          Assert((dynamic_cast<const FE_Nedelec<dim> *>(&cell->get_fe()) !=
-                  nullptr) ||
-                   (dynamic_cast<const FE_NedelecSZ<dim> *>(&cell->get_fe()) !=
-                    nullptr),
-                 ExcNotImplemented());
-        }
-      const unsigned int degree =
-        fe.base_element(base_indices.first).degree - 1;
-
-      switch (dim)
-        {
-          case 2:
-            // NOTE: This is very similar to compute_edge_projection as used in
-            // 3D,
-            //       and contains a lot of overlap with that function.
-            {
-              // Find the DoFs we want to constrain. There are degree+1 in
-              // total. Create a map from these to the face index Note:
-              //    - for a single FE_Nedelec<2> element this is
-              //      simply 0 to fe.dofs_per_face
-              //    - for FESystem<2> this just requires matching the
-              //      base element, fe.system_to_base_index.first.first
-              //      and the copy of the base element we're interested
-              //      in, fe.system_to_base_index.first.second
-              std::vector<unsigned int> associated_edge_dof_to_face_dof(degree +
-                                                                        1);
-
-              unsigned int associated_edge_dof_index = 0;
-              for (unsigned int face_idx = 0; face_idx < fe.dofs_per_face;
-                   ++face_idx)
-                {
-                  const unsigned int cell_idx =
-                    fe.face_to_cell_index(face_idx, face);
-                  if (((dynamic_cast<const FESystem<dim> *>(&fe) != nullptr) &&
-                       (fe.system_to_base_index(cell_idx).first ==
-                        base_indices)) ||
-                      (dynamic_cast<const FE_Nedelec<dim> *>(&fe) != nullptr) ||
-                      (dynamic_cast<const FE_NedelecSZ<dim> *>(&fe) != nullptr))
-                    {
-                      associated_edge_dof_to_face_dof
-                        [associated_edge_dof_index] = face_idx;
-                      ++associated_edge_dof_index;
-                    }
-                }
-              // Sanity check:
-              const unsigned int associated_edge_dofs =
-                associated_edge_dof_index;
-              Assert(associated_edge_dofs == degree + 1,
-                     ExcMessage("Error: Unexpected number of 2D edge DoFs"));
-
-              // Matrix and RHS vectors to store:
-              // We have (degree+1) edge basis functions
-              FullMatrix<number> edge_matrix(degree + 1, degree + 1);
-              FullMatrix<number> edge_matrix_inv(degree + 1, degree + 1);
-              Vector<number>     edge_rhs(degree + 1);
-              Vector<number>     edge_solution(degree + 1);
-
-              const FEValuesExtractors::Vector vec(first_vector_component);
-
-              // Project the boundary function onto the shape functions for this
-              // edge and set up a linear system of equations to get the values
-              // for the DoFs associated with this edge.
-              for (unsigned int q_point = 0;
-                   q_point < fe_face_values.n_quadrature_points;
-                   ++q_point)
-                {
-                  // Compute the entries of the linear system
-                  // Note the system is symmetric so we could only compute the
-                  // lower/upper triangle.
-                  //
-                  // The matrix entries are
-                  // \int_{edge} (tangential * edge_shape_function_i) *
-                  // (tangential * edge_shape_function_j) dS
-                  //
-                  // The RHS entries are:
-                  // \int_{edge} (tangential* boundary_value) * (tangential *
-                  // edge_shape_function_i) dS.
-                  //
-                  // In 2D, tangential*vector is equivalent to
-                  // cross_product_3d(normal, vector), so we use this instead.
-                  // This avoids possible issues with the computation of the
-                  // tangent.
-
-                  // Store the normal at this quad point:
-                  Tensor<1, dim> normal_at_q_point =
-                    fe_face_values.normal_vector(q_point);
-                  for (unsigned int j = 0; j < associated_edge_dofs; ++j)
-                    {
-                      const unsigned int j_face_idx =
-                        associated_edge_dof_to_face_dof[j];
-                      const unsigned int j_cell_idx =
-                        fe.face_to_cell_index(j_face_idx, face);
-
-                      Tensor<1, dim> phi_j =
-                        fe_face_values[vec].value(j_cell_idx, q_point);
-                      for (unsigned int i = 0; i < associated_edge_dofs; ++i)
-                        {
-                          const unsigned int i_face_idx =
-                            associated_edge_dof_to_face_dof[i];
-                          const unsigned int i_cell_idx =
-                            fe.face_to_cell_index(i_face_idx, face);
-
-                          Tensor<1, dim> phi_i =
-                            fe_face_values[vec].value(i_cell_idx, q_point);
-
-                          // Using n cross phi
-                          edge_matrix(i, j) +=
-                            fe_face_values.JxW(q_point) *
-                            ((phi_i[1] * normal_at_q_point[0] -
-                              phi_i[0] * normal_at_q_point[1]) *
-                             (phi_j[1] * normal_at_q_point[0] -
-                              phi_j[0] * normal_at_q_point[1]));
-                        }
-                      // Using n cross phi
-                      edge_rhs(j) +=
-                        fe_face_values.JxW(q_point) *
-                        ((values[q_point](first_vector_component + 1) *
-                            normal_at_q_point[0] -
-                          values[q_point](first_vector_component) *
-                            normal_at_q_point[1]) *
-                         (phi_j[1] * normal_at_q_point[0] -
-                          phi_j[0] * normal_at_q_point[1]));
-                    }
-                }
-
-              // Invert linear system
-              edge_matrix_inv.invert(edge_matrix);
-              edge_matrix_inv.vmult(edge_solution, edge_rhs);
-
-              // Store computed DoFs
-              for (unsigned int associated_edge_dof_index = 0;
-                   associated_edge_dof_index < associated_edge_dofs;
-                   ++associated_edge_dof_index)
-                {
-                  dof_values[associated_edge_dof_to_face_dof
-                               [associated_edge_dof_index]] =
-                    edge_solution(associated_edge_dof_index);
-                  dofs_processed[associated_edge_dof_to_face_dof
-                                   [associated_edge_dof_index]] = true;
-                }
-              break;
-            }
-
-          case 3:
-            {
-              const FEValuesExtractors::Vector vec(first_vector_component);
-
-              // First group DoFs associated with edges which we already know.
-              // Sort these into groups of dofs (0 -> degree+1 of them) by each
-              // edge. This will help when computing the residual for the face
-              // projections.
-              //
-              // This matches with the search done in compute_edge_projection.
-              const unsigned int lines_per_face =
-                GeometryInfo<dim>::lines_per_face;
-              std::vector<std::vector<unsigned int>>
-                                        associated_edge_dof_to_face_dof(lines_per_face,
-                                                                        std::vector<unsigned int>(degree +
-                                                                        1));
-              std::vector<unsigned int> associated_edge_dofs(lines_per_face);
-
-              for (unsigned int line = 0; line < lines_per_face; ++line)
-                {
-                  // Lowest DoF in the base element allowed for this edge:
-                  const unsigned int lower_bound =
-                    fe.base_element(base_indices.first)
-                      .face_to_cell_index(line * (degree + 1), face);
-                  // Highest DoF in the base element allowed for this edge:
-                  const unsigned int upper_bound =
-                    fe.base_element(base_indices.first)
-                      .face_to_cell_index((line + 1) * (degree + 1) - 1, face);
-                  unsigned int associated_edge_dof_index = 0;
-
-                  for (unsigned int line_dof_idx = 0;
-                       line_dof_idx < fe.dofs_per_line;
-                       ++line_dof_idx)
-                    {
-                      // For each DoF associated with the (interior of) the
-                      // line, we need to figure out which base element it
-                      // belongs to and then if that's the correct base element.
-                      // This is complicated by the fact that the FiniteElement
-                      // class has functions that translate from face to cell,
-                      // but not from edge to cell index systems. So we have to
-                      // do that step by step.
-                      //
-                      // DoFs on a face in 3d are numbered in order by vertices
-                      // then lines then faces. i.e. line 0 has degree+1 dofs
-                      // numbered 0,..,degree
-                      //      line 1 has degree+1 dofs numbered
-                      //      (degree+1),..,2*(degree+1) and so on.
-                      const unsigned int face_dof_idx =
-                        GeometryInfo<dim>::vertices_per_face *
-                          fe.dofs_per_vertex +
-                        line * fe.dofs_per_line + line_dof_idx;
-
-                      // Next, translate from face to cell. Note, this might be
-                      // assuming that the edge orientations are "standard" (not
-                      // sure any more at this time), i.e.
-                      //       cell->line_orientation(line) = true.
-                      const unsigned int cell_dof_idx =
-                        fe.face_to_cell_index(face_dof_idx, face);
-
-                      // Check that this cell_idx belongs to the correct
-                      // base_element, component and line. We do this for each
-                      // of the supported elements separately
-                      bool dof_is_of_interest = false;
-                      if (dynamic_cast<const FESystem<dim> *>(&fe) != nullptr)
-                        {
-                          dof_is_of_interest =
-                            (fe.system_to_base_index(cell_dof_idx).first ==
-                             base_indices) &&
-                            (lower_bound <=
-                             fe.system_to_base_index(cell_dof_idx).second) &&
-                            (fe.system_to_base_index(cell_dof_idx).second <=
-                             upper_bound);
-                        }
-                      else if ((dynamic_cast<const FE_Nedelec<dim> *>(&fe) !=
-                                nullptr) ||
-                               (dynamic_cast<const FE_NedelecSZ<dim> *>(&fe) !=
-                                nullptr))
-                        {
-                          Assert((line * (degree + 1) <= face_dof_idx) &&
-                                   (face_dof_idx < (line + 1) * (degree + 1)),
-                                 ExcInternalError());
-                          dof_is_of_interest = true;
-                        }
-                      else
-                        Assert(false, ExcNotImplemented());
-
-                      if (dof_is_of_interest)
-                        {
-                          associated_edge_dof_to_face_dof
-                            [line][associated_edge_dof_index] = face_dof_idx;
-                          ++associated_edge_dof_index;
-                        }
-                    }
-                  // Sanity check:
-                  associated_edge_dofs[line] = associated_edge_dof_index;
-                  Assert(associated_edge_dofs[line] == degree + 1,
-                         ExcInternalError());
-                }
-
-              // Next find the face DoFs associated with the vector components
-              // we're interested in. There are 2*degree*(degree+1) DoFs
-              // associated with the interior of each face (not including
-              // edges!).
-              //
-              // Create a map mapping from the consecutively numbered
-              // associated_dofs to the face DoF (which can be transferred to a
-              // local cell index).
-              //
-              // For FE_Nedelec<3> we just need to have a face numbering greater
-              // than the number of edge DoFs (=lines_per_face*(degree+1).
-              //
-              // For FESystem<3> we need to base the base_indices (base element
-              // and copy within that base element) and ensure we're above the
-              // number of edge DoFs within that base element.
-              std::vector<unsigned int> associated_face_dof_to_face_dof(
-                2 * degree * (degree + 1));
-
-              // Loop over these quad-interior dofs.
-              unsigned int associated_face_dof_index = 0;
-              for (unsigned int quad_dof_idx = 0;
-                   quad_dof_idx < fe.dofs_per_quad;
-                   ++quad_dof_idx)
-                {
-                  const unsigned int face_idx =
-                    GeometryInfo<dim>::vertices_per_face * fe.dofs_per_vertex +
-                    lines_per_face * fe.dofs_per_line + quad_dof_idx;
-                  const unsigned int cell_idx =
-                    fe.face_to_cell_index(face_idx, face);
-                  if (((dynamic_cast<const FESystem<dim> *>(&fe) != nullptr) &&
-                       (fe.system_to_base_index(cell_idx).first ==
-                        base_indices)) ||
-                      (dynamic_cast<const FE_Nedelec<dim> *>(&fe) != nullptr) ||
-                      (dynamic_cast<const FE_NedelecSZ<dim> *>(&fe) != nullptr))
-                    {
-                      AssertIndexRange(associated_face_dof_index,
-                                       associated_face_dof_to_face_dof.size());
-                      associated_face_dof_to_face_dof
-                        [associated_face_dof_index] = face_idx;
-                      ++associated_face_dof_index;
-                    }
-                }
-              // Sanity check:
-              const unsigned int associated_face_dofs =
-                associated_face_dof_index;
-              Assert(associated_face_dofs == 2 * degree * (degree + 1),
-                     ExcMessage("Error: Unexpected number of 3D face DoFs"));
-
-              // Storage for the linear system.
-              // There are 2*degree*(degree+1) DoFs associated with a face in
-              // 3D. Note this doesn't include the DoFs associated with edges on
-              // that face.
-              FullMatrix<number> face_matrix(2 * degree * (degree + 1));
-              FullMatrix<number> face_matrix_inv(2 * degree * (degree + 1));
-              Vector<number>     face_rhs(2 * degree * (degree + 1));
-              Vector<number>     face_solution(2 * degree * (degree + 1));
-
-              // Project the boundary function onto the shape functions for this
-              // face and set up a linear system of equations to get the values
-              // for the DoFs associated with this face. We also must include
-              // the residuals from the shape functions associated with edges.
-              Tensor<1, dim, number> tmp;
-              Tensor<1, dim>         cross_product_i;
-              Tensor<1, dim>         cross_product_j;
-              Tensor<1, dim, number> cross_product_rhs;
-
-              // Loop to construct face linear system.
-              for (unsigned int q_point = 0;
-                   q_point < fe_face_values.n_quadrature_points;
-                   ++q_point)
-                {
-                  // First calculate the residual from the edge functions
-                  // store the result in tmp.
-                  //
-                  // Edge_residual =
-                  //        boundary_value - (
-                  //            \sum_(edges on face)
-                  //                 \sum_(DoFs on edge)
-                  //                 edge_dof_value*edge_shape_function
-                  //                   )
-                  for (unsigned int d = 0; d < dim; ++d)
-                    {
-                      tmp[d] = 0.0;
-                    }
-                  for (unsigned int line = 0; line < lines_per_face; ++line)
-                    {
-                      for (unsigned int associated_edge_dof = 0;
-                           associated_edge_dof < associated_edge_dofs[line];
-                           ++associated_edge_dof)
-                        {
-                          const unsigned int face_idx =
-                            associated_edge_dof_to_face_dof
-                              [line][associated_edge_dof];
-                          const unsigned int cell_idx =
-                            fe.face_to_cell_index(face_idx, face);
-                          tmp -= dof_values[face_idx] *
-                                 fe_face_values[vec].value(cell_idx, q_point);
-                        }
-                    }
-
-                  for (unsigned int d = 0; d < dim; ++d)
-                    {
-                      tmp[d] += values[q_point](first_vector_component + d);
-                    }
-
-                  // Tensor of normal vector on the face at q_point;
-                  const Tensor<1, dim> normal_vector =
-                    fe_face_values.normal_vector(q_point);
-
-                  // Now compute the linear system:
-                  // On a face:
-                  // The matrix entries are:
-                  // \int_{face} (n x face_shape_function_i) \cdot ( n x
-                  // face_shape_function_j) dS
-                  //
-                  // The RHS entries are:
-                  // \int_{face} (n x (Edge_residual) \cdot (n x
-                  // face_shape_function_i) dS
-
-                  for (unsigned int j = 0; j < associated_face_dofs; ++j)
-                    {
-                      const unsigned int j_face_idx =
-                        associated_face_dof_to_face_dof[j];
-                      const unsigned int cell_j =
-                        fe.face_to_cell_index(j_face_idx, face);
-
-                      cross_product_j =
-                        cross_product_3d(normal_vector,
-                                         fe_face_values[vec].value(cell_j,
-                                                                   q_point));
-
-                      for (unsigned int i = 0; i < associated_face_dofs; ++i)
-                        {
-                          const unsigned int i_face_idx =
-                            associated_face_dof_to_face_dof[i];
-                          const unsigned int cell_i =
-                            fe.face_to_cell_index(i_face_idx, face);
-                          cross_product_i = cross_product_3d(
-                            normal_vector,
-                            fe_face_values[vec].value(cell_i, q_point));
-
-                          face_matrix(i, j) += fe_face_values.JxW(q_point) *
-                                               cross_product_i *
-                                               cross_product_j;
-                        }
-                      // compute rhs
-                      cross_product_rhs = cross_product_3d(normal_vector, tmp);
-                      face_rhs(j) += fe_face_values.JxW(q_point) *
-                                     cross_product_rhs * cross_product_j;
-                    }
-                }
-
-              // Solve linear system:
-              if (associated_face_dofs > 0)
-                {
-                  face_matrix_inv.invert(face_matrix);
-                  face_matrix_inv.vmult(face_solution, face_rhs);
-                }
-
-              // Store computed DoFs:
-              for (unsigned int associated_face_dof = 0;
-                   associated_face_dof < associated_face_dofs;
-                   ++associated_face_dof)
-                {
-                  dof_values
-                    [associated_face_dof_to_face_dof[associated_face_dof]] =
-                      face_solution(associated_face_dof);
-                  dofs_processed
-                    [associated_face_dof_to_face_dof[associated_face_dof]] =
-                      true;
-                }
-              break;
-            }
-          default:
-            Assert(false, ExcNotImplemented());
-        }
-    }
-
-    template <int dim, typename DoFHandlerType, typename number>
-    void
-    compute_project_boundary_values_curl_conforming_l2(
-      const DoFHandlerType &                 dof_handler,
-      const unsigned int                     first_vector_component,
-      const Function<dim, number> &          boundary_function,
-      const types::boundary_id               boundary_component,
-      AffineConstraints<number> &            constraints,
-      const hp::MappingCollection<dim, dim> &mapping_collection)
-    {
-      // L2-projection based interpolation formed in one (in 2D) or two (in 3D)
-      // steps.
-      //
-      // In 2D we only need to constrain edge DoFs.
-      //
-      // In 3D we need to constrain both edge and face DoFs. This is done in two
-      // parts.
-      //
-      // For edges, since the face shape functions are zero here ("bubble
-      // functions"), we project the tangential component of the boundary
-      // function and compute the L2-projection. This returns the values for the
-      // DoFs associated with each edge shape function. In 3D, this is computed
-      // by internals::compute_edge_projection_l2, in 2D, it is handled by
-      // compute_face_projection_curl_conforming_l2.
-      //
-      // For faces we compute the residual of the boundary function which is
-      // satisfied by the edge shape functions alone. Which can then be used to
-      // calculate the remaining face DoF values via a projection which leads to
-      // a linear system to solve. This is handled by
-      // compute_face_projection_curl_conforming_l2
-      //
-      // For details see (for example) section 4.2:
-      // Electromagnetic scattering simulation using an H (curl) conforming hp
-      // finite element method in three dimensions, PD Ledger, K Morgan, O
-      // Hassan, Int. J.  Num. Meth. Fluids, Volume 53, Issue 8, pages
-      // 1267–1296, 20 March 2007:
-      // http://onlinelibrary.wiley.com/doi/10.1002/fld.1223/abstract
-
-      // Create hp FEcollection, dof_handler can be either hp or standard type.
-      // From here on we can treat it like a hp-namespace object.
-      const hp::FECollection<dim> &fe_collection(
-        dof_handler.get_fe_collection());
-
-      // Create face quadrature collection
-      hp::QCollection<dim - 1> face_quadrature_collection;
-      for (unsigned int i = 0; i < fe_collection.size(); ++i)
-        {
-          const QGauss<dim - 1> reference_face_quadrature(
-            2 * fe_collection[i].degree + 1);
-          face_quadrature_collection.push_back(reference_face_quadrature);
-        }
-
-      hp::FEFaceValues<dim> fe_face_values(mapping_collection,
-                                           fe_collection,
-                                           face_quadrature_collection,
-                                           update_values |
-                                             update_quadrature_points |
-                                             update_normal_vectors |
-                                             update_JxW_values);
-
-      // Storage for dof values found and whether they have been processed:
-      std::vector<bool>                             dofs_processed;
-      std::vector<number>                           dof_values;
-      std::vector<types::global_dof_index>          face_dof_indices;
-      typename DoFHandlerType::active_cell_iterator cell =
-        dof_handler.begin_active();
-
-      switch (dim)
-        {
-          case 2:
-            {
-              for (; cell != dof_handler.end(); ++cell)
-                {
-                  if (cell->at_boundary() && cell->is_locally_owned())
-                    {
-                      for (const unsigned int face :
-                           GeometryInfo<dim>::face_indices())
-                        {
-                          if (cell->face(face)->boundary_id() ==
-                              boundary_component)
-                            {
-                              // If the FE is an FE_Nothing object there is no
-                              // work to do
-                              if (dynamic_cast<const FE_Nothing<dim> *>(
-                                    &cell->get_fe()) != nullptr)
-                                {
-                                  return;
-                                }
-
-                              // This is only implemented for FE_Nedelec
-                              // elements. If the FE is a FESystem we cannot
-                              // check this.
-                              if (dynamic_cast<const FESystem<dim> *>(
-                                    &cell->get_fe()) == nullptr)
-                                {
-                                  AssertThrow(
-                                    (dynamic_cast<const FE_Nedelec<dim> *>(
-                                       &cell->get_fe()) != nullptr) ||
-                                      (dynamic_cast<const FE_NedelecSZ<dim> *>(
-                                         &cell->get_fe()) != nullptr),
-                                    typename FiniteElement<
-                                      dim>::ExcInterpolationNotImplemented());
-                                }
-
-                              const unsigned int dofs_per_face =
-                                cell->get_fe().dofs_per_face;
-
-                              dofs_processed.resize(dofs_per_face);
-                              dof_values.resize(dofs_per_face);
-
-                              for (unsigned int dof = 0; dof < dofs_per_face;
-                                   ++dof)
-                                {
-                                  dof_values[dof]     = 0.0;
-                                  dofs_processed[dof] = false;
-                                }
-
-                              // Compute the projection of the boundary function
-                              // on the edge. In 2D this is all that's required.
-                              compute_face_projection_curl_conforming_l2(
-                                cell,
-                                face,
-                                fe_face_values,
-                                boundary_function,
-                                first_vector_component,
-                                dof_values,
-                                dofs_processed);
-
-                              // store the local->global map:
-                              face_dof_indices.resize(dofs_per_face);
-                              cell->face(face)->get_dof_indices(
-                                face_dof_indices, cell->active_fe_index());
-
-                              // Add the computed constraints to the
-                              // AffineConstraints object, assuming the degree
-                              // of freedom is not already constrained.
-                              for (unsigned int dof = 0; dof < dofs_per_face;
-                                   ++dof)
-                                {
-                                  if (dofs_processed[dof] &&
-                                      constraints.can_store_line(
-                                        face_dof_indices[dof]) &&
-                                      !(constraints.is_constrained(
-                                        face_dof_indices[dof])))
-                                    {
-                                      constraints.add_line(
-                                        face_dof_indices[dof]);
-                                      if (std::abs(dof_values[dof]) > 1e-13)
-                                        {
-                                          constraints.set_inhomogeneity(
-                                            face_dof_indices[dof],
-                                            dof_values[dof]);
-                                        }
-                                    }
-                                }
-                            }
-                        }
-                    }
-                }
-              break;
-            }
-
-          case 3:
-            {
-              hp::QCollection<dim> edge_quadrature_collection;
-
-              // Create equivalent of FEEdgeValues:
-              for (unsigned int i = 0; i < fe_collection.size(); ++i)
-                {
-                  const QGauss<dim - 2> reference_edge_quadrature(
-                    2 * fe_collection[i].degree + 1);
-                  for (const unsigned int face :
-                       GeometryInfo<dim>::face_indices())
-                    {
-                      for (unsigned int line = 0;
-                           line < GeometryInfo<dim>::lines_per_face;
-                           ++line)
-                        {
-                          edge_quadrature_collection.push_back(
-                            QProjector<dim>::project_to_face(
-                              QProjector<dim - 1>::project_to_face(
-                                reference_edge_quadrature, line),
-                              face));
-                        }
-                    }
-                }
-
-              hp::FEValues<dim> fe_edge_values(mapping_collection,
-                                               fe_collection,
-                                               edge_quadrature_collection,
-                                               update_jacobians |
-                                                 update_JxW_values |
-                                                 update_quadrature_points |
-                                                 update_values);
-
-              for (; cell != dof_handler.end(); ++cell)
-                {
-                  if (cell->at_boundary() && cell->is_locally_owned())
-                    {
-                      for (const unsigned int face :
-                           GeometryInfo<dim>::face_indices())
-                        {
-                          if (cell->face(face)->boundary_id() ==
-                              boundary_component)
-                            {
-                              // If the FE is an FE_Nothing object there is no
-                              // work to do
-                              if (dynamic_cast<const FE_Nothing<dim> *>(
-                                    &cell->get_fe()) != nullptr)
-                                {
-                                  return;
-                                }
-
-                              // This is only implemented for FE_Nedelec
-                              // elements. If the FE is a FESystem we cannot
-                              // check this.
-                              if (dynamic_cast<const FESystem<dim> *>(
-                                    &cell->get_fe()) == nullptr)
-                                {
-                                  AssertThrow(
-                                    (dynamic_cast<const FE_Nedelec<dim> *>(
-                                       &cell->get_fe()) != nullptr) ||
-                                      (dynamic_cast<const FE_NedelecSZ<dim> *>(
-                                         &cell->get_fe()) != nullptr),
-                                    typename FiniteElement<
-                                      dim>::ExcInterpolationNotImplemented());
-                                }
-
-                              const unsigned int superdegree =
-                                cell->get_fe().degree;
-                              const unsigned int degree = superdegree - 1;
-                              const unsigned int dofs_per_face =
-                                cell->get_fe().dofs_per_face;
-
-                              dofs_processed.resize(dofs_per_face);
-                              dof_values.resize(dofs_per_face);
-                              for (unsigned int dof = 0; dof < dofs_per_face;
-                                   ++dof)
-                                {
-                                  dof_values[dof]     = 0.0;
-                                  dofs_processed[dof] = false;
-                                }
-
-                              // First compute the projection on the edges.
-                              for (unsigned int line = 0;
-                                   line < GeometryInfo<3>::lines_per_face;
-                                   ++line)
-                                {
-                                  compute_edge_projection_l2(
-                                    cell,
-                                    face,
-                                    line,
-                                    fe_edge_values,
-                                    boundary_function,
-                                    first_vector_component,
-                                    dof_values,
-                                    dofs_processed);
-                                }
-
-                              // If there are higher order shape functions, then
-                              // we still need to compute the face projection
-                              if (degree > 0)
-                                {
-                                  compute_face_projection_curl_conforming_l2(
-                                    cell,
-                                    face,
-                                    fe_face_values,
-                                    boundary_function,
-                                    first_vector_component,
-                                    dof_values,
-                                    dofs_processed);
-                                }
-
-                              // Store the computed values in the global vector.
-                              face_dof_indices.resize(dofs_per_face);
-                              cell->face(face)->get_dof_indices(
-                                face_dof_indices, cell->active_fe_index());
-
-                              for (unsigned int dof = 0; dof < dofs_per_face;
-                                   ++dof)
-                                {
-                                  if (dofs_processed[dof] &&
-                                      constraints.can_store_line(
-                                        face_dof_indices[dof]) &&
-                                      !(constraints.is_constrained(
-                                        face_dof_indices[dof])))
-                                    {
-                                      constraints.add_line(
-                                        face_dof_indices[dof]);
-
-                                      if (std::abs(dof_values[dof]) > 1e-13)
-                                        {
-                                          constraints.set_inhomogeneity(
-                                            face_dof_indices[dof],
-                                            dof_values[dof]);
-                                        }
-                                    }
-                                }
-                            }
-                        }
-                    }
-                }
-              break;
-            }
-          default:
-            Assert(false, ExcNotImplemented());
-        }
-    }
-
-  } // namespace internals
-
-
-  template <int dim, typename number>
-  void
-  project_boundary_values_curl_conforming_l2(
-    const DoFHandler<dim> &      dof_handler,
-    const unsigned int           first_vector_component,
-    const Function<dim, number> &boundary_function,
-    const types::boundary_id     boundary_component,
-    AffineConstraints<number> &  constraints,
-    const Mapping<dim> &         mapping)
-  {
-    // non-hp version - calls the internal
-    // compute_project_boundary_values_curl_conforming_l2() function
-    // above after recasting the mapping.
-
-    const hp::MappingCollection<dim> mapping_collection(mapping);
-    internals::compute_project_boundary_values_curl_conforming_l2(
-      dof_handler,
-      first_vector_component,
-      boundary_function,
-      boundary_component,
-      constraints,
-      mapping_collection);
-  }
-
-  template <int dim, typename number>
-  void
-  project_boundary_values_curl_conforming_l2(
-    const hp::DoFHandler<dim> &            dof_handler,
-    const unsigned int                     first_vector_component,
-    const Function<dim, number> &          boundary_function,
-    const types::boundary_id               boundary_component,
-    AffineConstraints<number> &            constraints,
-    const hp::MappingCollection<dim, dim> &mapping_collection)
-  {
-    // hp version - calls the internal
-    // compute_project_boundary_values_curl_conforming_l2() function above.
-    internals::compute_project_boundary_values_curl_conforming_l2(
-      dof_handler,
-      first_vector_component,
-      boundary_function,
-      boundary_component,
-      constraints,
-      mapping_collection);
-  }
-
-
-
-  namespace internals
-  {
-    // This function computes the projection of the boundary function on the
-    // boundary in 2d.
-    template <typename cell_iterator>
-    void
-    compute_face_projection_div_conforming(
-      const cell_iterator &                       cell,
-      const unsigned int                          face,
-      const FEFaceValues<2> &                     fe_values,
-      const unsigned int                          first_vector_component,
-      const Function<2> &                         boundary_function,
-      const std::vector<DerivativeForm<1, 2, 2>> &jacobians,
-      AffineConstraints<double> &                 constraints)
-    {
-      // Compute the integral over the product of the normal components of
-      // the boundary function times the normal components of the shape
-      // functions supported on the boundary.
-      const FEValuesExtractors::Vector vec(first_vector_component);
-      const FiniteElement<2> &         fe      = cell->get_fe();
-      const std::vector<Tensor<1, 2>> &normals = fe_values.get_normal_vectors();
-      const unsigned int
-                                  face_coordinate_direction[GeometryInfo<2>::faces_per_cell] = {1,
-                                                                      1,
-                                                                      0,
-                                                                      0};
-      std::vector<Vector<double>> values(fe_values.n_quadrature_points,
-                                         Vector<double>(2));
-      Vector<double>              dof_values(fe.dofs_per_face);
-
-      // Get the values of the boundary function at the quadrature points.
-      {
-        const std::vector<Point<2>> &quadrature_points =
-          fe_values.get_quadrature_points();
-
-        boundary_function.vector_value_list(quadrature_points, values);
-      }
-
-      for (unsigned int q_point = 0; q_point < fe_values.n_quadrature_points;
-           ++q_point)
-        {
-          double tmp = 0.0;
-
-          for (unsigned int d = 0; d < 2; ++d)
-            tmp += normals[q_point][d] * values[q_point](d);
-
-          tmp *=
-            fe_values.JxW(q_point) *
-            std::sqrt(jacobians[q_point][0][face_coordinate_direction[face]] *
-                        jacobians[q_point][0][face_coordinate_direction[face]] +
-                      jacobians[q_point][1][face_coordinate_direction[face]] *
-                        jacobians[q_point][1][face_coordinate_direction[face]]);
-
-          for (unsigned int i = 0; i < fe.dofs_per_face; ++i)
-            dof_values(i) +=
-              tmp * (normals[q_point] *
-                     fe_values[vec].value(
-                       fe.face_to_cell_index(i,
-                                             face,
-                                             cell->face_orientation(face),
-                                             cell->face_flip(face),
-                                             cell->face_rotation(face)),
-                       q_point));
-        }
-
-      std::vector<types::global_dof_index> face_dof_indices(fe.dofs_per_face);
-
-      cell->face(face)->get_dof_indices(face_dof_indices,
-                                        cell->active_fe_index());
-
-      // Copy the computed values in the AffineConstraints only, if the degree
-      // of freedom is not already constrained.
-      for (unsigned int i = 0; i < fe.dofs_per_face; ++i)
-        if (!(constraints.is_constrained(face_dof_indices[i])) &&
-            fe.get_nonzero_components(fe.face_to_cell_index(
-              i,
-              face,
-              cell->face_orientation(face),
-              cell->face_flip(face),
-              cell->face_rotation(face)))[first_vector_component])
-          {
-            constraints.add_line(face_dof_indices[i]);
-
-            if (std::abs(dof_values(i)) > 1e-14)
-              constraints.set_inhomogeneity(face_dof_indices[i], dof_values(i));
-          }
-    }
-
-    // dummy implementation of above function for all other dimensions
-    template <int dim, typename cell_iterator>
-    void
-    compute_face_projection_div_conforming(
-      const cell_iterator &,
-      const unsigned int,
-      const FEFaceValues<dim> &,
-      const unsigned int,
-      const Function<dim> &,
-      const std::vector<DerivativeForm<1, dim, dim>> &,
-      AffineConstraints<double> &)
-    {
-      Assert(false, ExcNotImplemented());
-    }
-
-    // This function computes the projection of the boundary function on the
-    // boundary in 3d.
-    template <typename cell_iterator>
-    void
-    compute_face_projection_div_conforming(
-      const cell_iterator &                       cell,
-      const unsigned int                          face,
-      const FEFaceValues<3> &                     fe_values,
-      const unsigned int                          first_vector_component,
-      const Function<3> &                         boundary_function,
-      const std::vector<DerivativeForm<1, 3, 3>> &jacobians,
-      std::vector<double> &                       dof_values,
-      std::vector<types::global_dof_index> &      projected_dofs)
-    {
-      // Compute the intergral over the product of the normal components of
-      // the boundary function times the normal components of the shape
-      // functions supported on the boundary.
-      const FEValuesExtractors::Vector vec(first_vector_component);
-      const FiniteElement<3> &         fe      = cell->get_fe();
-      const std::vector<Tensor<1, 3>> &normals = fe_values.get_normal_vectors();
-      const unsigned int
-        face_coordinate_directions[GeometryInfo<3>::faces_per_cell][2] = {
-          {1, 2}, {1, 2}, {2, 0}, {2, 0}, {0, 1}, {0, 1}};
-      std::vector<Vector<double>> values(fe_values.n_quadrature_points,
-                                         Vector<double>(3));
-      Vector<double>              dof_values_local(fe.dofs_per_face);
-
-      {
-        const std::vector<Point<3>> &quadrature_points =
-          fe_values.get_quadrature_points();
-
-        boundary_function.vector_value_list(quadrature_points, values);
-      }
-
-      for (unsigned int q_point = 0; q_point < fe_values.n_quadrature_points;
-           ++q_point)
-        {
-          double tmp = 0.0;
-
-          for (unsigned int d = 0; d < 3; ++d)
-            tmp += normals[q_point][d] * values[q_point](d);
-
-          tmp *=
-            fe_values.JxW(q_point) *
-            std::sqrt(
-              (jacobians[q_point][0][face_coordinate_directions[face][0]] *
-                 jacobians[q_point][0][face_coordinate_directions[face][0]] +
-               jacobians[q_point][1][face_coordinate_directions[face][0]] *
-                 jacobians[q_point][1][face_coordinate_directions[face][0]] +
-               jacobians[q_point][2][face_coordinate_directions[face][0]] *
-                 jacobians[q_point][2][face_coordinate_directions[face][0]]) *
-              (jacobians[q_point][0][face_coordinate_directions[face][1]] *
-                 jacobians[q_point][0][face_coordinate_directions[face][1]] +
-               jacobians[q_point][1][face_coordinate_directions[face][1]] *
-                 jacobians[q_point][1][face_coordinate_directions[face][1]] +
-               jacobians[q_point][2][face_coordinate_directions[face][1]] *
-                 jacobians[q_point][2][face_coordinate_directions[face][1]]));
-
-          for (unsigned int i = 0; i < fe.dofs_per_face; ++i)
-            dof_values_local(i) +=
-              tmp * (normals[q_point] *
-                     fe_values[vec].value(
-                       fe.face_to_cell_index(i,
-                                             face,
-                                             cell->face_orientation(face),
-                                             cell->face_flip(face),
-                                             cell->face_rotation(face)),
-                       q_point));
-        }
-
-      std::vector<types::global_dof_index> face_dof_indices(fe.dofs_per_face);
-
-      cell->face(face)->get_dof_indices(face_dof_indices,
-                                        cell->active_fe_index());
-
-      for (unsigned int i = 0; i < fe.dofs_per_face; ++i)
-        if (projected_dofs[face_dof_indices[i]] < fe.degree &&
-            fe.get_nonzero_components(fe.face_to_cell_index(
-              i,
-              face,
-              cell->face_orientation(face),
-              cell->face_flip(face),
-              cell->face_rotation(face)))[first_vector_component])
-          {
-            dof_values[face_dof_indices[i]]     = dof_values_local(i);
-            projected_dofs[face_dof_indices[i]] = fe.degree;
-          }
-    }
-
-    // dummy implementation of above
-    // function for all other
-    // dimensions
-    template <int dim, typename cell_iterator>
-    void
-    compute_face_projection_div_conforming(
-      const cell_iterator &,
-      const unsigned int,
-      const FEFaceValues<dim> &,
-      const unsigned int,
-      const Function<dim> &,
-      const std::vector<DerivativeForm<1, dim, dim>> &,
-      std::vector<double> &,
-      std::vector<types::global_dof_index> &)
-    {
-      Assert(false, ExcNotImplemented());
-    }
-  } // namespace internals
-
-
-  template <int dim>
-  void
-  project_boundary_values_div_conforming(
-    const DoFHandler<dim> &    dof_handler,
-    const unsigned int         first_vector_component,
-    const Function<dim> &      boundary_function,
-    const types::boundary_id   boundary_component,
-    AffineConstraints<double> &constraints,
-    const Mapping<dim> &       mapping)
-  {
-    const unsigned int spacedim = dim;
-    // Interpolate the normal components
-    // of the boundary functions. Since
-    // the Raviart-Thomas elements are
-    // constructed from a Lagrangian
-    // basis, it suffices to compute
-    // the integral over the product
-    // of the normal components of the
-    // boundary function times the
-    // normal components of the shape
-    // functions supported on the
-    // boundary.
-    const FiniteElement<dim> &       fe = dof_handler.get_fe();
-    QGauss<dim - 1>                  face_quadrature(fe.degree + 1);
-    FEFaceValues<dim>                fe_face_values(mapping,
-                                     fe,
-                                     face_quadrature,
-                                     update_JxW_values | update_normal_vectors |
-                                       update_quadrature_points |
-                                       update_values);
-    hp::FECollection<dim>            fe_collection(fe);
-    const hp::MappingCollection<dim> mapping_collection(mapping);
-    hp::QCollection<dim>             quadrature_collection;
-
-    for (unsigned int face : GeometryInfo<dim>::face_indices())
-      quadrature_collection.push_back(
-        QProjector<dim>::project_to_face(face_quadrature, face));
-
-    hp::FEValues<dim> fe_values(mapping_collection,
-                                fe_collection,
-                                quadrature_collection,
-                                update_jacobians);
-
-    switch (dim)
-      {
-        case 2:
-          {
-            for (const auto &cell : dof_handler.active_cell_iterators())
-              if (cell->at_boundary() && cell->is_locally_owned())
-                for (const unsigned int face :
-                     GeometryInfo<dim>::face_indices())
-                  if (cell->face(face)->boundary_id() == boundary_component)
-                    {
-                      // if the FE is a
-                      // FE_Nothing object
-                      // there is no work to
-                      // do
-                      if (dynamic_cast<const FE_Nothing<dim> *>(
-                            &cell->get_fe()) != nullptr)
-                        return;
-
-                      // This is only
-                      // implemented, if the
-                      // FE is a Raviart-Thomas
-                      // element. If the FE is
-                      // a FESystem we cannot
-                      // check this.
-                      if (dynamic_cast<const FESystem<dim> *>(
-                            &cell->get_fe()) == nullptr)
-                        {
-                          AssertThrow(
-                            dynamic_cast<const FE_RaviartThomas<dim> *>(
-                              &cell->get_fe()) != nullptr,
-                            typename FiniteElement<
-                              dim>::ExcInterpolationNotImplemented());
-                        }
-
-                      fe_values.reinit(cell,
-                                       face +
-                                         cell->active_fe_index() *
-                                           GeometryInfo<dim>::faces_per_cell);
-
-                      const std::vector<DerivativeForm<1, dim, spacedim>>
-                        &jacobians =
-                          fe_values.get_present_fe_values().get_jacobians();
-
-                      fe_face_values.reinit(cell, face);
-                      internals::compute_face_projection_div_conforming(
-                        cell,
-                        face,
-                        fe_face_values,
-                        first_vector_component,
-                        boundary_function,
-                        jacobians,
-                        constraints);
-                    }
-
-            break;
-          }
-
-        case 3:
-          {
-            // In three dimensions the edges between two faces are treated
-            // twice. Therefore we store the computed values in a vector
-            // and copy them over in the AffineConstraints after all values
-            // have been computed. If we have two values for one edge, we
-            // choose the one, which was computed with the higher order
-            // element. If both elements are of the same order, we just
-            // keep the first value and do not compute a second one.
-            const unsigned int                   n_dofs = dof_handler.n_dofs();
-            std::vector<double>                  dof_values(n_dofs);
-            std::vector<types::global_dof_index> projected_dofs(n_dofs);
-
-            for (unsigned int dof = 0; dof < n_dofs; ++dof)
-              projected_dofs[dof] = 0;
-
-            for (const auto &cell : dof_handler.active_cell_iterators())
-              if (cell->at_boundary() && cell->is_locally_owned())
-                for (const unsigned int face :
-                     GeometryInfo<dim>::face_indices())
-                  if (cell->face(face)->boundary_id() == boundary_component)
-                    {
-                      // This is only implemented, if the FE is a
-                      // Raviart-Thomas element. If the FE is a FESystem we
-                      // cannot check this.
-                      if (dynamic_cast<const FESystem<dim> *>(
-                            &cell->get_fe()) == nullptr)
-                        {
-                          AssertThrow(
-                            dynamic_cast<const FE_RaviartThomas<dim> *>(
-                              &cell->get_fe()) != nullptr,
-                            typename FiniteElement<
-                              dim>::ExcInterpolationNotImplemented());
-                        }
-
-                      fe_values.reinit(cell,
-                                       face +
-                                         cell->active_fe_index() *
-                                           GeometryInfo<dim>::faces_per_cell);
-
-                      const std::vector<DerivativeForm<1, dim, spacedim>>
-                        &jacobians =
-                          fe_values.get_present_fe_values().get_jacobians();
-
-                      fe_face_values.reinit(cell, face);
-                      internals::compute_face_projection_div_conforming(
-                        cell,
-                        face,
-                        fe_face_values,
-                        first_vector_component,
-                        boundary_function,
-                        jacobians,
-                        dof_values,
-                        projected_dofs);
-                    }
-
-            for (unsigned int dof = 0; dof < n_dofs; ++dof)
-              if ((projected_dofs[dof] != 0) &&
-                  !(constraints.is_constrained(dof)))
-                {
-                  constraints.add_line(dof);
-
-                  if (std::abs(dof_values[dof]) > 1e-14)
-                    constraints.set_inhomogeneity(dof, dof_values[dof]);
-                }
-
-            break;
-          }
-
-        default:
-          Assert(false, ExcNotImplemented());
-      }
-  }
-
-
-  template <int dim>
-  void
-  project_boundary_values_div_conforming(
-    const hp::DoFHandler<dim> &            dof_handler,
-    const unsigned int                     first_vector_component,
-    const Function<dim> &                  boundary_function,
-    const types::boundary_id               boundary_component,
-    AffineConstraints<double> &            constraints,
-    const hp::MappingCollection<dim, dim> &mapping_collection)
-  {
-    const unsigned int           spacedim = dim;
-    const hp::FECollection<dim> &fe_collection =
-      dof_handler.get_fe_collection();
-    hp::QCollection<dim - 1> face_quadrature_collection;
-    hp::QCollection<dim>     quadrature_collection;
-
-    for (unsigned int i = 0; i < fe_collection.size(); ++i)
-      {
-        const QGauss<dim - 1> quadrature(fe_collection[i].degree + 1);
-
-        face_quadrature_collection.push_back(quadrature);
-
-        for (unsigned int face : GeometryInfo<dim>::face_indices())
-          quadrature_collection.push_back(
-            QProjector<dim>::project_to_face(quadrature, face));
-      }
-
-    hp::FEFaceValues<dim> fe_face_values(mapping_collection,
-                                         fe_collection,
-                                         face_quadrature_collection,
-                                         update_JxW_values |
-                                           update_normal_vectors |
-                                           update_quadrature_points |
-                                           update_values);
-    hp::FEValues<dim>     fe_values(mapping_collection,
-                                fe_collection,
-                                quadrature_collection,
-                                update_jacobians);
-
-    switch (dim)
-      {
-        case 2:
-          {
-            for (const auto &cell : dof_handler.active_cell_iterators())
-              if (cell->at_boundary() && cell->is_locally_owned())
-                for (const unsigned int face :
-                     GeometryInfo<dim>::face_indices())
-                  if (cell->face(face)->boundary_id() == boundary_component)
-                    {
-                      // This is only
-                      // implemented, if the
-                      // FE is a Raviart-Thomas
-                      // element. If the FE is
-                      // a FESystem we cannot
-                      // check this.
-                      if (dynamic_cast<const FESystem<dim> *>(
-                            &cell->get_fe()) == nullptr)
-                        {
-                          AssertThrow(
-                            dynamic_cast<const FE_RaviartThomas<dim> *>(
-                              &cell->get_fe()) != nullptr,
-                            typename FiniteElement<
-                              dim>::ExcInterpolationNotImplemented());
-                        }
-
-                      fe_values.reinit(cell,
-                                       face +
-                                         cell->active_fe_index() *
-                                           GeometryInfo<dim>::faces_per_cell);
-
-                      const std::vector<DerivativeForm<1, dim, spacedim>>
-                        &jacobians =
-                          fe_values.get_present_fe_values().get_jacobians();
-
-                      fe_face_values.reinit(cell, face);
-                      internals::compute_face_projection_div_conforming(
-                        cell,
-                        face,
-                        fe_face_values.get_present_fe_values(),
-                        first_vector_component,
-                        boundary_function,
-                        jacobians,
-                        constraints);
-                    }
-
-            break;
-          }
-
-        case 3:
-          {
-            const unsigned int                   n_dofs = dof_handler.n_dofs();
-            std::vector<double>                  dof_values(n_dofs);
-            std::vector<types::global_dof_index> projected_dofs(n_dofs);
-
-            for (unsigned int dof = 0; dof < n_dofs; ++dof)
-              projected_dofs[dof] = 0;
-
-            for (const auto &cell : dof_handler.active_cell_iterators())
-              if (cell->at_boundary() && cell->is_locally_owned())
-                for (const unsigned int face :
-                     GeometryInfo<dim>::face_indices())
-                  if (cell->face(face)->boundary_id() == boundary_component)
-                    {
-                      // This is only
-                      // implemented, if the
-                      // FE is a Raviart-Thomas
-                      // element. If the FE is
-                      // a FESystem we cannot
-                      // check this.
-                      if (dynamic_cast<const FESystem<dim> *>(
-                            &cell->get_fe()) == nullptr)
-                        {
-                          AssertThrow(
-                            dynamic_cast<const FE_RaviartThomas<dim> *>(
-                              &cell->get_fe()) != nullptr,
-                            typename FiniteElement<
-                              dim>::ExcInterpolationNotImplemented());
-                        }
-
-                      fe_values.reinit(cell,
-                                       face +
-                                         cell->active_fe_index() *
-                                           GeometryInfo<dim>::faces_per_cell);
-
-                      const std::vector<DerivativeForm<1, dim, spacedim>>
-                        &jacobians =
-                          fe_values.get_present_fe_values().get_jacobians();
-
-                      fe_face_values.reinit(cell, face);
-                      internals::compute_face_projection_div_conforming(
-                        cell,
-                        face,
-                        fe_face_values.get_present_fe_values(),
-                        first_vector_component,
-                        boundary_function,
-                        jacobians,
-                        dof_values,
-                        projected_dofs);
-                    }
-
-            for (unsigned int dof = 0; dof < n_dofs; ++dof)
-              if ((projected_dofs[dof] != 0) &&
-                  !(constraints.is_constrained(dof)))
-                {
-                  constraints.add_line(dof);
-
-                  if (std::abs(dof_values[dof]) > 1e-14)
-                    constraints.set_inhomogeneity(dof, dof_values[dof]);
-                }
-
-            break;
-          }
-
-        default:
-          Assert(false, ExcNotImplemented());
-      }
-  }
-
-
-
-  template <int dim, int spacedim, template <int, int> class DoFHandlerType>
-  void
-  compute_no_normal_flux_constraints(
-    const DoFHandlerType<dim, spacedim> &dof_handler,
-    const unsigned int                   first_vector_component,
-    const std::set<types::boundary_id> & boundary_ids,
-    AffineConstraints<double> &          constraints,
-    const Mapping<dim, spacedim> &       mapping)
-  {
-    ZeroFunction<dim>                                        zero_function(dim);
-    std::map<types::boundary_id, const Function<spacedim> *> function_map;
-    for (const types::boundary_id boundary_id : boundary_ids)
-      function_map[boundary_id] = &zero_function;
-    compute_nonzero_normal_flux_constraints(dof_handler,
-                                            first_vector_component,
-                                            boundary_ids,
-                                            function_map,
-                                            constraints,
-                                            mapping);
-  }
-
-  template <int dim, int spacedim, template <int, int> class DoFHandlerType>
-  void
-  compute_nonzero_normal_flux_constraints(
-    const DoFHandlerType<dim, spacedim> &dof_handler,
-    const unsigned int                   first_vector_component,
-    const std::set<types::boundary_id> & boundary_ids,
-    const std::map<types::boundary_id, const Function<spacedim> *>
-      &                           function_map,
-    AffineConstraints<double> &   constraints,
-    const Mapping<dim, spacedim> &mapping)
-  {
-    Assert(dim > 1,
-           ExcMessage("This function is not useful in 1d because it amounts "
-                      "to imposing Dirichlet values on the vector-valued "
-                      "quantity."));
-
-    std::vector<types::global_dof_index> face_dofs;
-
-    // create FE and mapping collections for all elements in use by this
-    // DoFHandler
-    const hp::FECollection<dim, spacedim> &fe_collection =
-      dof_handler.get_fe_collection();
-    hp::MappingCollection<dim, spacedim> mapping_collection;
-    for (unsigned int i = 0; i < fe_collection.size(); ++i)
-      mapping_collection.push_back(mapping);
-
-    // now also create a quadrature collection for the faces of a cell. fill
-    // it with a quadrature formula with the support points on faces for each
-    // FE
-    hp::QCollection<dim - 1> face_quadrature_collection;
-    for (unsigned int i = 0; i < fe_collection.size(); ++i)
-      {
-        const std::vector<Point<dim - 1>> &unit_support_points =
-          fe_collection[i].get_unit_face_support_points();
-
-        Assert(unit_support_points.size() == fe_collection[i].dofs_per_face,
-               ExcInternalError());
-
-        face_quadrature_collection.push_back(
-          Quadrature<dim - 1>(unit_support_points));
-      }
-
-    // now create the object with which we will generate the normal vectors
-    hp::FEFaceValues<dim, spacedim> x_fe_face_values(mapping_collection,
-                                                     fe_collection,
-                                                     face_quadrature_collection,
-                                                     update_quadrature_points |
-                                                       update_normal_vectors);
-
-    // have a map that stores normal vectors for each vector-dof tuple we want
-    // to constrain. since we can get at the same vector dof tuple more than
-    // once (for example if it is located at a vertex that we visit from all
-    // adjacent cells), we will want to average later on the normal vectors
-    // computed on different cells as described in the documentation of this
-    // function. however, we can only average if the contributions came from
-    // different cells, whereas we want to constrain twice or more in case the
-    // contributions came from different faces of the same cell
-    // (i.e. constrain not just the *average normal direction* but *all normal
-    // directions* we find). consequently, we also have to store which cell a
-    // normal vector was computed on
-    using DoFToNormalsMap = std::multimap<
-      internal::VectorDoFTuple<dim>,
-      std::pair<Tensor<1, dim>,
-                typename DoFHandlerType<dim, spacedim>::active_cell_iterator>>;
-    std::map<internal::VectorDoFTuple<dim>, Vector<double>>
-      dof_vector_to_b_values;
-
-    DoFToNormalsMap dof_to_normals_map;
-
-    // now loop over all cells and all faces
-    typename DoFHandlerType<dim, spacedim>::active_cell_iterator
-      cell = dof_handler.begin_active(),
-      endc = dof_handler.end();
-    std::set<types::boundary_id>::iterator b_id;
-    for (; cell != endc; ++cell)
-      if (!cell->is_artificial())
-        for (const unsigned int face_no : GeometryInfo<dim>::face_indices())
-          if ((b_id = boundary_ids.find(cell->face(face_no)->boundary_id())) !=
-              boundary_ids.end())
-            {
-              const FiniteElement<dim> &fe = cell->get_fe();
-              typename DoFHandlerType<dim, spacedim>::face_iterator face =
-                cell->face(face_no);
-
-              // get the indices of the dofs on this cell...
-              face_dofs.resize(fe.dofs_per_face);
-              face->get_dof_indices(face_dofs, cell->active_fe_index());
-
-              x_fe_face_values.reinit(cell, face_no);
-              const FEFaceValues<dim> &fe_values =
-                x_fe_face_values.get_present_fe_values();
-
-              // then identify which of them correspond to the selected set of
-              // vector components
-              for (unsigned int i = 0; i < face_dofs.size(); ++i)
-                if (fe.face_system_to_component_index(i).first ==
-                    first_vector_component)
-                  {
-                    // find corresponding other components of vector
-                    internal::VectorDoFTuple<dim> vector_dofs;
-                    vector_dofs.dof_indices[0] = face_dofs[i];
-
-                    Assert(
-                      first_vector_component + dim <= fe.n_components(),
-                      ExcMessage(
-                        "Error: the finite element does not have enough components "
-                        "to define a normal direction."));
-
-                    for (unsigned int k = 0; k < fe.dofs_per_face; ++k)
-                      if ((k != i) &&
-                          (face_quadrature_collection[cell->active_fe_index()]
-                             .point(k) ==
-                           face_quadrature_collection[cell->active_fe_index()]
-                             .point(i)) &&
-                          (fe.face_system_to_component_index(k).first >=
-                           first_vector_component) &&
-                          (fe.face_system_to_component_index(k).first <
-                           first_vector_component + dim))
-                        vector_dofs.dof_indices
-                          [fe.face_system_to_component_index(k).first -
-                           first_vector_component] = face_dofs[k];
-
-                    for (unsigned int d = 0; d < dim; ++d)
-                      Assert(vector_dofs.dof_indices[d] < dof_handler.n_dofs(),
-                             ExcInternalError());
-
-                    // we need the normal vector on this face. we know that it
-                    // is a vector of length 1 but at least with higher order
-                    // mappings it isn't always possible to guarantee that
-                    // each component is exact up to zero tolerance. in
-                    // particular, as shown in the deal.II/no_flux_06 test, if
-                    // we just take the normal vector as given by the
-                    // fe_values object, we can get entries in the normal
-                    // vectors of the unit cube that have entries up to
-                    // several times 1e-14.
-                    //
-                    // the problem with this is that this later yields
-                    // constraints that are circular (e.g., in the testcase,
-                    // we get constraints of the form
-                    //
-                    // x22 =  2.93099e-14*x21 + 2.93099e-14*x23
-                    // x21 = -2.93099e-14*x22 + 2.93099e-14*x21
-                    //
-                    // in both of these constraints, the small numbers should
-                    // be zero and the constraints should simply be
-                    // x22 = x21 = 0
-                    //
-                    // to achieve this, we utilize that we know that the
-                    // normal vector has (or should have) length 1 and that we
-                    // can simply set small elements to zero (without having
-                    // to check that they are small *relative to something
-                    // else*). we do this and then normalize the length of the
-                    // vector back to one, just to be on the safe side
-                    //
-                    // one more point: we would like to use the "real" normal
-                    // vector here, as provided by the boundary description
-                    // and as opposed to what we get from the FEValues object.
-                    // we do this in the immediately next line, but as is
-                    // obvious, the boundary only has a vague idea which side
-                    // of a cell it is on -- indicated by the face number. in
-                    // other words, it may provide the inner or outer normal.
-                    // by and large, there is no harm from this, since the
-                    // tangential vector we compute is still the same. however,
-                    // we do average over normal vectors from adjacent cells
-                    // and if they have recorded normal vectors from the inside
-                    // once and from the outside the other time, then this
-                    // averaging is going to run into trouble. as a consequence
-                    // we ask the mapping after all for its normal vector,
-                    // but we only ask it so that we can possibly correct the
-                    // sign of the normal vector provided by the boundary
-                    // if they should point in different directions. this is the
-                    // case in tests/deal.II/no_flux_11.
-                    Tensor<1, dim> normal_vector =
-                      (cell->face(face_no)->get_manifold().normal_vector(
-                        cell->face(face_no), fe_values.quadrature_point(i)));
-                    if (normal_vector * fe_values.normal_vector(i) < 0)
-                      normal_vector *= -1;
-                    Assert(std::fabs(normal_vector.norm() - 1) < 1e-14,
-                           ExcInternalError());
-                    for (unsigned int d = 0; d < dim; ++d)
-                      if (std::fabs(normal_vector[d]) < 1e-13)
-                        normal_vector[d] = 0;
-                    normal_vector /= normal_vector.norm();
-
-                    const Point<dim> point = fe_values.quadrature_point(i);
-                    Vector<double>   b_values(dim);
-                    function_map.at(*b_id)->vector_value(point, b_values);
-
-                    // now enter the (dofs,(normal_vector,cell)) entry into
-                    // the map
-                    dof_to_normals_map.insert(
-                      std::make_pair(vector_dofs,
-                                     std::make_pair(normal_vector, cell)));
-                    dof_vector_to_b_values.insert(
-                      std::make_pair(vector_dofs, b_values));
-
-#ifdef DEBUG_NO_NORMAL_FLUX
-                    std::cout << "Adding normal vector:" << std::endl
-                              << "   dofs=" << vector_dofs << std::endl
-                              << "   cell=" << cell << " at " << cell->center()
-                              << std::endl
-                              << "   normal=" << normal_vector << std::endl;
-#endif
-                  }
-            }
-
-    // Now do something with the collected information. To this end, loop
-    // through all sets of pairs (dofs,normal_vector) and identify which
-    // entries belong to the same set of dofs and then do as described in the
-    // documentation, i.e. either average the normal vector or don't for this
-    // particular set of dofs
-    typename DoFToNormalsMap::const_iterator p = dof_to_normals_map.begin();
-
-    while (p != dof_to_normals_map.end())
-      {
-        // first find the range of entries in the multimap that corresponds to
-        // the same vector-dof tuple. as usual, we define the range
-        // half-open. the first entry of course is 'p'
-        typename DoFToNormalsMap::const_iterator same_dof_range[2] = {p};
-        for (++p; p != dof_to_normals_map.end(); ++p)
-          if (p->first != same_dof_range[0]->first)
-            {
-              same_dof_range[1] = p;
-              break;
-            }
-        if (p == dof_to_normals_map.end())
-          same_dof_range[1] = dof_to_normals_map.end();
-
-#ifdef DEBUG_NO_NORMAL_FLUX
-        std::cout << "For dof indices <" << p->first
-                  << ">, found the following normals" << std::endl;
-        for (typename DoFToNormalsMap::const_iterator q = same_dof_range[0];
-             q != same_dof_range[1];
-             ++q)
-          std::cout << "   " << q->second.first << " from cell "
-                    << q->second.second << std::endl;
-#endif
-
-
-        // now compute the reverse mapping: for each of the cells that
-        // contributed to the current set of vector dofs, add up the normal
-        // vectors. the values of the map are pairs of normal vectors and
-        // number of cells that have contributed
-        using CellToNormalsMap =
-          std::map<typename DoFHandlerType<dim, spacedim>::active_cell_iterator,
-                   std::pair<Tensor<1, dim>, unsigned int>>;
-
-        CellToNormalsMap cell_to_normals_map;
-        for (typename DoFToNormalsMap::const_iterator q = same_dof_range[0];
-             q != same_dof_range[1];
-             ++q)
-          if (cell_to_normals_map.find(q->second.second) ==
-              cell_to_normals_map.end())
-            cell_to_normals_map[q->second.second] =
-              std::make_pair(q->second.first, 1U);
-          else
-            {
-              const Tensor<1, dim> old_normal =
-                cell_to_normals_map[q->second.second].first;
-              const unsigned int old_count =
-                cell_to_normals_map[q->second.second].second;
-
-              Assert(old_count > 0, ExcInternalError());
-
-              // in the same entry, store again the now averaged normal vector
-              // and the new count
-              cell_to_normals_map[q->second.second] =
-                std::make_pair((old_normal * old_count + q->second.first) /
-                                 (old_count + 1),
-                               old_count + 1);
-            }
-        Assert(cell_to_normals_map.size() >= 1, ExcInternalError());
-
-#ifdef DEBUG_NO_NORMAL_FLUX
-        std::cout << "   cell_to_normals_map:" << std::endl;
-        for (typename CellToNormalsMap::const_iterator x =
-               cell_to_normals_map.begin();
-             x != cell_to_normals_map.end();
-             ++x)
-          std::cout << "      " << x->first << " -> (" << x->second.first << ','
-                    << x->second.second << ')' << std::endl;
-#endif
-
-        // count the maximum number of contributions from each cell
-        unsigned int max_n_contributions_per_cell = 1;
-        for (typename CellToNormalsMap::const_iterator x =
-               cell_to_normals_map.begin();
-             x != cell_to_normals_map.end();
-             ++x)
-          max_n_contributions_per_cell =
-            std::max(max_n_contributions_per_cell, x->second.second);
-
-        // verify that each cell can have only contributed at most dim times,
-        // since that is the maximum number of faces that come together at a
-        // single place
-        Assert(max_n_contributions_per_cell <= dim, ExcInternalError());
-
-        switch (max_n_contributions_per_cell)
-          {
-            // first deal with the case that a number of cells all have
-            // registered that they have a normal vector defined at the
-            // location of a given vector dof, and that each of them have
-            // encountered this vector dof exactly once while looping over all
-            // their faces. as stated in the documentation, this is the case
-            // where we want to simply average over all normal vectors
-            //
-            // the typical case is in 2d where multiple cells meet at one
-            // vertex sitting on the boundary. same in 3d for a vertex that
-            // is associated with only one of the boundary indicators passed
-            // to this function
-            case 1:
-              {
-                // compute the average normal vector from all the ones that have
-                // the same set of dofs. we could add them up and divide them by
-                // the number of additions, or simply normalize them right away
-                // since we want them to have unit length anyway
-                Tensor<1, dim> normal;
-                for (typename CellToNormalsMap::const_iterator x =
-                       cell_to_normals_map.begin();
-                     x != cell_to_normals_map.end();
-                     ++x)
-                  normal += x->second.first;
-                normal /= normal.norm();
-
-                // normalize again
-                for (unsigned int d = 0; d < dim; ++d)
-                  if (std::fabs(normal[d]) < 1e-13)
-                    normal[d] = 0;
-                normal /= normal.norm();
-
-                // then construct constraints from this:
-                const internal::VectorDoFTuple<dim> &dof_indices =
-                  same_dof_range[0]->first;
-                double               normal_value = 0.;
-                const Vector<double> b_values =
-                  dof_vector_to_b_values[dof_indices];
-                for (unsigned int i = 0; i < dim; ++i)
-                  normal_value += b_values[i] * normal[i];
-                internal::add_constraint(dof_indices,
-                                         normal,
-                                         constraints,
-                                         normal_value);
-
-                break;
-              }
-
-            // this is the slightly more complicated case that a single cell has
-            // contributed with exactly DIM normal vectors to the same set of
-            // vector dofs. this is what happens in a corner in 2d and 3d (but
-            // not on an edge in 3d, where we have only 2, i.e. <DIM,
-            // contributions. Here we do not want to average the normal
-            // vectors. Since we have DIM contributions, let's assume (and
-            // verify) that they are in fact all linearly independent; in that
-            // case, all vector components are constrained and we need to set
-            // all of them to the corresponding boundary values
-            case dim:
-              {
-                // assert that indeed only a single cell has contributed
-                Assert(cell_to_normals_map.size() == 1, ExcInternalError());
-
-                // check linear independence by computing the determinant of the
-                // matrix created from all the normal vectors. if they are
-                // linearly independent, then the determinant is nonzero. if
-                // they are orthogonal, then the matrix is in fact equal to 1
-                // (since they are all unit vectors); make sure the determinant
-                // is larger than 1e-3 to avoid cases where cells are degenerate
-                {
-                  Tensor<2, dim> t;
-
-                  typename DoFToNormalsMap::const_iterator x =
-                    same_dof_range[0];
-                  for (unsigned int i = 0; i < dim; ++i, ++x)
-                    for (unsigned int j = 0; j < dim; ++j)
-                      t[i][j] = x->second.first[j];
-
-                  Assert(
-                    std::fabs(determinant(t)) > 1e-3,
-                    ExcMessage(
-                      "Found a set of normal vectors that are nearly collinear."));
-                }
-
-                // so all components of this vector dof are constrained. enter
-                // this into the AffineConstraints object
-                //
-                // ignore dofs already constrained
-                const internal::VectorDoFTuple<dim> &dof_indices =
-                  same_dof_range[0]->first;
-                const Vector<double> b_values =
-                  dof_vector_to_b_values[dof_indices];
-                for (unsigned int i = 0; i < dim; ++i)
-                  if (!constraints.is_constrained(
-                        same_dof_range[0]->first.dof_indices[i]) &&
-                      constraints.can_store_line(
-                        same_dof_range[0]->first.dof_indices[i]))
-                    {
-                      const types::global_dof_index line =
-                        dof_indices.dof_indices[i];
-                      constraints.add_line(line);
-                      if (std::fabs(b_values[i]) >
-                          std::numeric_limits<double>::epsilon())
-                        constraints.set_inhomogeneity(line, b_values[i]);
-                      // no add_entries here
-                    }
-
-                break;
-              }
-
-            // this is the case of an edge contribution in 3d, i.e. the vector
-            // is constrained in two directions but not the third.
-            default:
-              {
-                Assert(dim >= 3, ExcNotImplemented());
-                Assert(max_n_contributions_per_cell == 2, ExcInternalError());
-
-                // as described in the documentation, let us first collect what
-                // each of the cells contributed at the current point. we use a
-                // std::list instead of a std::set (which would be more natural)
-                // because std::set requires that the stored elements are
-                // comparable with operator<
-                using CellContributions = std::map<
-                  typename DoFHandlerType<dim, spacedim>::active_cell_iterator,
-                  std::list<Tensor<1, dim>>>;
-                CellContributions cell_contributions;
-
-                for (typename DoFToNormalsMap::const_iterator q =
-                       same_dof_range[0];
-                     q != same_dof_range[1];
-                     ++q)
-                  cell_contributions[q->second.second].push_back(
-                    q->second.first);
-                Assert(cell_contributions.size() >= 1, ExcInternalError());
-
-                // now for each cell that has contributed determine the number
-                // of normal vectors it has contributed. we currently only
-                // implement if this is dim-1 for all cells (if a single cell
-                // has contributed dim, or if all adjacent cells have
-                // contributed 1 normal vector, this is already handled above).
-                //
-                // we only implement the case that all cells contribute
-                // dim-1 because we assume that we are following an edge
-                // of the domain (think: we are looking at a vertex
-                // located on one of the edges of a refined cube where the
-                // boundary indicators of the two adjacent faces of the
-                // cube are both listed in the set of boundary indicators
-                // passed to this function). in that case, all cells along
-                // that edge of the domain are assumed to have contributed
-                // dim-1 normal vectors. however, there are cases where
-                // this assumption is not justified (see the lengthy
-                // explanation in test no_flux_12.cc) and in those cases
-                // we simply ignore the cell that contributes only
-                // once. this is also discussed at length in the
-                // documentation of this function.
-                //
-                // for each contributing cell compute the tangential vector that
-                // remains unconstrained
-                std::list<Tensor<1, dim>> tangential_vectors;
-                for (typename CellContributions::const_iterator contribution =
-                       cell_contributions.begin();
-                     contribution != cell_contributions.end();
-                     ++contribution)
-                  {
-#ifdef DEBUG_NO_NORMAL_FLUX
-                    std::cout
-                      << "   Treating edge case with dim-1 contributions."
-                      << std::endl
-                      << "   Looking at cell " << contribution->first
-                      << " which has contributed these normal vectors:"
-                      << std::endl;
-                    for (typename std::list<Tensor<1, dim>>::const_iterator t =
-                           contribution->second.begin();
-                         t != contribution->second.end();
-                         ++t)
-                      std::cout << "      " << *t << std::endl;
-#endif
-
-                    // as mentioned above, simply ignore cells that only
-                    // contribute once
-                    if (contribution->second.size() < dim - 1)
-                      continue;
-
-                    Tensor<1, dim> normals[dim - 1];
-                    {
-                      unsigned int index = 0;
-                      for (typename std::list<Tensor<1, dim>>::const_iterator
-                             t = contribution->second.begin();
-                           t != contribution->second.end();
-                           ++t, ++index)
-                        normals[index] = *t;
-                      Assert(index == dim - 1, ExcInternalError());
-                    }
-
-                    // calculate the tangent as the outer product of the normal
-                    // vectors. since these vectors do not need to be orthogonal
-                    // (think, for example, the case of the deal.II/no_flux_07
-                    // test: a sheared cube in 3d, with Q2 elements, where we
-                    // have constraints from the two normal vectors of two faces
-                    // of the sheared cube that are not perpendicular to each
-                    // other), we have to normalize the outer product
-                    Tensor<1, dim> tangent;
-                    switch (dim)
-                      {
-                        case 3:
-                          // take cross product between normals[0] and
-                          // normals[1]. write it in the current form (with
-                          // [dim-2]) to make sure that compilers don't warn
-                          // about out-of-bounds accesses -- the warnings are
-                          // bogus since we get here only for dim==3, but at
-                          // least one isn't quite smart enough to notice this
-                          // and warns when compiling the function in 2d
-                          tangent =
-                            cross_product_3d(normals[0], normals[dim - 2]);
-                          break;
-                        default:
-                          Assert(false, ExcNotImplemented());
-                      }
-
-                    Assert(
-                      std::fabs(tangent.norm()) > 1e-12,
-                      ExcMessage(
-                        "Two normal vectors from adjacent faces are almost "
-                        "parallel."));
-                    tangent /= tangent.norm();
-
-                    tangential_vectors.push_back(tangent);
-                  }
-
-                // go through the list of tangents and make sure that they all
-                // roughly point in the same direction as the first one (i.e.
-                // have an angle less than 90 degrees); if they don't then flip
-                // their sign
-                {
-                  const Tensor<1, dim> first_tangent =
-                    tangential_vectors.front();
-                  typename std::list<Tensor<1, dim>>::iterator t =
-                    tangential_vectors.begin();
-                  ++t;
-                  for (; t != tangential_vectors.end(); ++t)
-                    if (*t * first_tangent < 0)
-                      *t *= -1;
-                }
-
-                // now compute the average tangent and normalize it
-                Tensor<1, dim> average_tangent;
-                for (typename std::list<Tensor<1, dim>>::const_iterator t =
-                       tangential_vectors.begin();
-                     t != tangential_vectors.end();
-                     ++t)
-                  average_tangent += *t;
-                average_tangent /= average_tangent.norm();
-
-                // now all that is left is that we add the constraints that the
-                // vector is parallel to the tangent
-                const internal::VectorDoFTuple<dim> &dof_indices =
-                  same_dof_range[0]->first;
-                const Vector<double> b_values =
-                  dof_vector_to_b_values[dof_indices];
-                internal::add_tangentiality_constraints(dof_indices,
-                                                        average_tangent,
-                                                        constraints,
-                                                        b_values);
-              }
-          }
-      }
-  }
-
-
-
-  namespace internal
-  {
-    template <int dim>
-    struct PointComparator
-    {
-      bool
-      operator()(const std::array<types::global_dof_index, dim> &p1,
-                 const std::array<types::global_dof_index, dim> &p2) const
-      {
-        for (unsigned int d = 0; d < dim; ++d)
-          if (p1[d] < p2[d])
-            return true;
-        return false;
-      }
-    };
-  } // namespace internal
-
-
-
-  template <int dim, int spacedim, template <int, int> class DoFHandlerType>
-  void
-  compute_normal_flux_constraints(
-    const DoFHandlerType<dim, spacedim> &dof_handler,
-    const unsigned int                   first_vector_component,
-    const std::set<types::boundary_id> & boundary_ids,
-    AffineConstraints<double> &          constraints,
-    const Mapping<dim, spacedim> &       mapping)
-  {
-    ZeroFunction<dim>                                        zero_function(dim);
-    std::map<types::boundary_id, const Function<spacedim> *> function_map;
-    for (const types::boundary_id boundary_id : boundary_ids)
-      function_map[boundary_id] = &zero_function;
-    compute_nonzero_tangential_flux_constraints(dof_handler,
-                                                first_vector_component,
-                                                boundary_ids,
-                                                function_map,
-                                                constraints,
-                                                mapping);
-  }
-
-  template <int dim, int spacedim, template <int, int> class DoFHandlerType>
-  void
-  compute_nonzero_tangential_flux_constraints(
-    const DoFHandlerType<dim, spacedim> &dof_handler,
-    const unsigned int                   first_vector_component,
-    const std::set<types::boundary_id> & boundary_ids,
-    const std::map<types::boundary_id, const Function<spacedim> *>
-      &                           function_map,
-    AffineConstraints<double> &   constraints,
-    const Mapping<dim, spacedim> &mapping)
-  {
-    AffineConstraints<double> no_normal_flux_constraints(
-      constraints.get_local_lines());
-    compute_nonzero_normal_flux_constraints(dof_handler,
-                                            first_vector_component,
-                                            boundary_ids,
-                                            function_map,
-                                            no_normal_flux_constraints,
-                                            mapping);
-
-    const hp::FECollection<dim, spacedim> &fe_collection =
-      dof_handler.get_fe_collection();
-    hp::MappingCollection<dim, spacedim> mapping_collection;
-    for (unsigned int i = 0; i < fe_collection.size(); ++i)
-      mapping_collection.push_back(mapping);
-
-    // now also create a quadrature collection for the faces of a cell. fill
-    // it with a quadrature formula with the support points on faces for each
-    // FE
-    hp::QCollection<dim - 1> face_quadrature_collection;
-    for (unsigned int i = 0; i < fe_collection.size(); ++i)
-      {
-        const std::vector<Point<dim - 1>> &unit_support_points =
-          fe_collection[i].get_unit_face_support_points();
-
-        Assert(unit_support_points.size() == fe_collection[i].dofs_per_face,
-               ExcInternalError());
-
-        face_quadrature_collection.push_back(
-          Quadrature<dim - 1>(unit_support_points));
-      }
-
-    // now create the object with which we will generate the normal vectors
-    hp::FEFaceValues<dim, spacedim> x_fe_face_values(mapping_collection,
-                                                     fe_collection,
-                                                     face_quadrature_collection,
-                                                     update_quadrature_points |
-                                                       update_normal_vectors);
-
-    // Extract a list that collects all vector components that belong to the
-    // same node (scalar basis function). When creating that list, we use an
-    // array of dim components that stores the global degree of freedom.
-    std::set<std::array<types::global_dof_index, dim>,
-             internal::PointComparator<dim>>
-                                         vector_dofs;
-    std::vector<types::global_dof_index> face_dofs;
-
-    std::map<std::array<types::global_dof_index, dim>, Vector<double>>
-      dof_vector_to_b_values;
-
-    std::set<types::boundary_id>::iterator                b_id;
-    std::vector<std::array<types::global_dof_index, dim>> cell_vector_dofs;
-    for (const auto &cell : dof_handler.active_cell_iterators())
-      if (!cell->is_artificial())
-        for (const unsigned int face_no : GeometryInfo<dim>::face_indices())
-          if ((b_id = boundary_ids.find(cell->face(face_no)->boundary_id())) !=
-              boundary_ids.end())
-            {
-              const FiniteElement<dim> &fe = cell->get_fe();
-              typename DoFHandlerType<dim, spacedim>::face_iterator face =
-                cell->face(face_no);
-
-              // get the indices of the dofs on this cell...
-              face_dofs.resize(fe.dofs_per_face);
-              face->get_dof_indices(face_dofs, cell->active_fe_index());
-
-              x_fe_face_values.reinit(cell, face_no);
-              const FEFaceValues<dim> &fe_values =
-                x_fe_face_values.get_present_fe_values();
-
-              std::map<types::global_dof_index, double> dof_to_b_value;
-
-              unsigned int n_scalar_indices = 0;
-              cell_vector_dofs.resize(fe.dofs_per_face);
-              for (unsigned int i = 0; i < fe.dofs_per_face; ++i)
-                {
-                  if (fe.face_system_to_component_index(i).first >=
-                        first_vector_component &&
-                      fe.face_system_to_component_index(i).first <
-                        first_vector_component + dim)
-                    {
-                      const unsigned int component =
-                        fe.face_system_to_component_index(i).first -
-                        first_vector_component;
-                      n_scalar_indices =
-                        std::max(n_scalar_indices,
-                                 fe.face_system_to_component_index(i).second +
-                                   1);
-                      cell_vector_dofs[fe.face_system_to_component_index(i)
-                                         .second][component] = face_dofs[i];
-
-                      const Point<dim> point = fe_values.quadrature_point(i);
-                      const double     b_value =
-                        function_map.at(*b_id)->value(point, component);
-                      dof_to_b_value.insert(
-                        std::make_pair(face_dofs[i], b_value));
-                    }
-                }
-
-              // now we identified the vector indices on the cell, so next
-              // insert them into the set (it would be expensive to directly
-              // insert incomplete points into the set)
-              for (unsigned int i = 0; i < n_scalar_indices; ++i)
-                {
-                  vector_dofs.insert(cell_vector_dofs[i]);
-                  Vector<double> b_values(dim);
-                  for (unsigned int j = 0; j < dim; ++j)
-                    b_values[j] = dof_to_b_value[cell_vector_dofs[i][j]];
-                  dof_vector_to_b_values.insert(
-                    std::make_pair(cell_vector_dofs[i], b_values));
-                }
-            }
-
-    // iterate over the list of all vector components we found and see if we
-    // can find constrained ones
-    unsigned int n_total_constraints_found = 0;
-    for (const auto &dofs : vector_dofs)
-      {
-        unsigned int n_constraints = 0;
-        bool         is_constrained[dim];
-        for (unsigned int d = 0; d < dim; ++d)
-          if (no_normal_flux_constraints.is_constrained(dofs[d]))
-            {
-              is_constrained[d] = true;
-              ++n_constraints;
-              ++n_total_constraints_found;
-            }
-          else
-            is_constrained[d] = false;
-        if (n_constraints > 0)
-          {
-            // if more than one no-flux constraint is present, we need to
-            // constrain all vector degrees of freedom (we are in a corner
-            // where several faces meet and to get a continuous FE solution we
-            // need to set all conditions corresponding to the boundary
-            // function.).
-            if (n_constraints > 1)
-              {
-                const Vector<double> b_value = dof_vector_to_b_values[dofs];
-                for (unsigned int d = 0; d < dim; ++d)
-                  {
-                    constraints.add_line(dofs[d]);
-                    constraints.set_inhomogeneity(dofs[d], b_value(d));
-                  }
-                continue;
-              }
-
-            // ok, this is a no-flux constraint, so get the index of the dof
-            // that is currently constrained and make it unconstrained. The
-            // constraint indices will get the normal that contain the other
-            // indices.
-            Tensor<1, dim> normal;
-            unsigned       constrained_index = -1;
-            for (unsigned int d = 0; d < dim; ++d)
-              if (is_constrained[d])
-                {
-                  constrained_index = d;
-                  normal[d]         = 1.;
-                }
-            AssertIndexRange(constrained_index, dim);
-            const std::vector<std::pair<types::global_dof_index, double>>
-              *constrained = no_normal_flux_constraints.get_constraint_entries(
-                dofs[constrained_index]);
-            // find components to which this index is constrained to
-            Assert(constrained != nullptr, ExcInternalError());
-            Assert(constrained->size() < dim, ExcInternalError());
-            for (const auto &entry : *constrained)
-              {
-                int index = -1;
-                for (unsigned int d = 0; d < dim; ++d)
-                  if (entry.first == dofs[d])
-                    index = d;
-                Assert(index != -1, ExcInternalError());
-                normal[index] = entry.second;
-              }
-            Vector<double> boundary_value = dof_vector_to_b_values[dofs];
-            for (unsigned int d = 0; d < dim; ++d)
-              {
-                if (is_constrained[d])
-                  continue;
-                const unsigned int new_index = dofs[d];
-                if (!constraints.is_constrained(new_index))
-                  {
-                    constraints.add_line(new_index);
-                    if (std::abs(normal[d]) > 1e-13)
-                      constraints.add_entry(new_index,
-                                            dofs[constrained_index],
-                                            -normal[d]);
-                    constraints.set_inhomogeneity(new_index, boundary_value[d]);
-                  }
-              }
-          }
-      }
-    AssertDimension(n_total_constraints_found,
-                    no_normal_flux_constraints.n_constraints());
-  }
-
-
-
-  namespace internal
-  {
-    template <int dim, int spacedim, typename Number>
-    struct IDScratchData
-    {
-      IDScratchData(const dealii::hp::MappingCollection<dim, spacedim> &mapping,
-                    const dealii::hp::FECollection<dim, spacedim> &     fe,
-                    const dealii::hp::QCollection<dim> &                q,
-                    const UpdateFlags update_flags);
-
-      IDScratchData(const IDScratchData &data);
-
-      void
-      resize_vectors(const unsigned int n_q_points,
-                     const unsigned int n_components);
-
-      std::vector<Vector<Number>>                           function_values;
-      std::vector<std::vector<Tensor<1, spacedim, Number>>> function_grads;
-      std::vector<double>                                   weight_values;
-      std::vector<Vector<double>>                           weight_vectors;
-
-      std::vector<Vector<Number>>                           psi_values;
-      std::vector<std::vector<Tensor<1, spacedim, Number>>> psi_grads;
-      std::vector<Number>                                   psi_scalar;
-
-      std::vector<Number>                      tmp_values;
-      std::vector<Vector<Number>>              tmp_vector_values;
-      std::vector<Tensor<1, spacedim, Number>> tmp_gradients;
-      std::vector<std::vector<Tensor<1, spacedim, Number>>>
-        tmp_vector_gradients;
-
-      dealii::hp::FEValues<dim, spacedim> x_fe_values;
-    };
-
-
-    template <int dim, int spacedim, typename Number>
-    IDScratchData<dim, spacedim, Number>::IDScratchData(
-      const dealii::hp::MappingCollection<dim, spacedim> &mapping,
-      const dealii::hp::FECollection<dim, spacedim> &     fe,
-      const dealii::hp::QCollection<dim> &                q,
-      const UpdateFlags                                   update_flags)
-      : x_fe_values(mapping, fe, q, update_flags)
-    {}
-
-    template <int dim, int spacedim, typename Number>
-    IDScratchData<dim, spacedim, Number>::IDScratchData(
-      const IDScratchData &data)
-      : x_fe_values(data.x_fe_values.get_mapping_collection(),
-                    data.x_fe_values.get_fe_collection(),
-                    data.x_fe_values.get_quadrature_collection(),
-                    data.x_fe_values.get_update_flags())
-    {}
-
-    template <int dim, int spacedim, typename Number>
-    void
-    IDScratchData<dim, spacedim, Number>::resize_vectors(
-      const unsigned int n_q_points,
-      const unsigned int n_components)
-    {
-      function_values.resize(n_q_points, Vector<Number>(n_components));
-      function_grads.resize(
-        n_q_points, std::vector<Tensor<1, spacedim, Number>>(n_components));
-
-      weight_values.resize(n_q_points);
-      weight_vectors.resize(n_q_points, Vector<double>(n_components));
-
-      psi_values.resize(n_q_points, Vector<Number>(n_components));
-      psi_grads.resize(n_q_points,
-                       std::vector<Tensor<1, spacedim, Number>>(n_components));
-      psi_scalar.resize(n_q_points);
-
-      tmp_values.resize(n_q_points);
-      tmp_vector_values.resize(n_q_points, Vector<Number>(n_components));
-      tmp_gradients.resize(n_q_points);
-      tmp_vector_gradients.resize(
-        n_q_points, std::vector<Tensor<1, spacedim, Number>>(n_components));
-    }
-
-    template <int dim, int spacedim, typename Number>
-    struct DEAL_II_DEPRECATED DeprecatedIDScratchData
-    {
-      DeprecatedIDScratchData(
-        const dealii::hp::MappingCollection<dim, spacedim> &mapping,
-        const dealii::hp::FECollection<dim, spacedim> &     fe,
-        const dealii::hp::QCollection<dim> &                q,
-        const UpdateFlags                                   update_flags);
-
-      DeprecatedIDScratchData(const DeprecatedIDScratchData &data);
-
-      void
-      resize_vectors(const unsigned int n_q_points,
-                     const unsigned int n_components);
-
-      std::vector<Vector<Number>>                           function_values;
-      std::vector<std::vector<Tensor<1, spacedim, Number>>> function_grads;
-      std::vector<double>                                   weight_values;
-      std::vector<Vector<double>>                           weight_vectors;
-
-      std::vector<Vector<Number>>                           psi_values;
-      std::vector<std::vector<Tensor<1, spacedim, Number>>> psi_grads;
-      std::vector<Number>                                   psi_scalar;
-
-      std::vector<double>                           tmp_values;
-      std::vector<Vector<double>>                   tmp_vector_values;
-      std::vector<Tensor<1, spacedim>>              tmp_gradients;
-      std::vector<std::vector<Tensor<1, spacedim>>> tmp_vector_gradients;
-
-      dealii::hp::FEValues<dim, spacedim> x_fe_values;
-    };
-
-
-    template <int dim, int spacedim, typename Number>
-    DeprecatedIDScratchData<dim, spacedim, Number>::DeprecatedIDScratchData(
-      const dealii::hp::MappingCollection<dim, spacedim> &mapping,
-      const dealii::hp::FECollection<dim, spacedim> &     fe,
-      const dealii::hp::QCollection<dim> &                q,
-      const UpdateFlags                                   update_flags)
-      : x_fe_values(mapping, fe, q, update_flags)
-    {}
-
-    template <int dim, int spacedim, typename Number>
-    DeprecatedIDScratchData<dim, spacedim, Number>::DeprecatedIDScratchData(
-      const DeprecatedIDScratchData &data)
-      : x_fe_values(data.x_fe_values.get_mapping_collection(),
-                    data.x_fe_values.get_fe_collection(),
-                    data.x_fe_values.get_quadrature_collection(),
-                    data.x_fe_values.get_update_flags())
-    {}
-
-    template <int dim, int spacedim, typename Number>
-    void
-    DeprecatedIDScratchData<dim, spacedim, Number>::resize_vectors(
-      const unsigned int n_q_points,
-      const unsigned int n_components)
-    {
-      function_values.resize(n_q_points, Vector<Number>(n_components));
-      function_grads.resize(
-        n_q_points, std::vector<Tensor<1, spacedim, Number>>(n_components));
-
-      weight_values.resize(n_q_points);
-      weight_vectors.resize(n_q_points, Vector<double>(n_components));
-
-      psi_values.resize(n_q_points, Vector<Number>(n_components));
-      psi_grads.resize(n_q_points,
-                       std::vector<Tensor<1, spacedim, Number>>(n_components));
-      psi_scalar.resize(n_q_points);
-
-      tmp_values.resize(n_q_points);
-      tmp_vector_values.resize(n_q_points, Vector<double>(n_components));
-      tmp_gradients.resize(n_q_points);
-      tmp_vector_gradients.resize(
-        n_q_points, std::vector<Tensor<1, spacedim>>(n_components));
-    }
-
-    namespace internal
-    {
-      template <typename number>
-      double
-      mean_to_double(const number &mean_value)
-      {
-        return mean_value;
-      }
-
-      template <typename number>
-      double
-      mean_to_double(const std::complex<number> &mean_value)
-      {
-        // we need to return double as a norm, but mean value is a complex
-        // number. Panic and return real-part while warning the user that
-        // they shall never do that.
-        Assert(
-          false,
-          ExcMessage(
-            "Mean value norm is not implemented for complex-valued vectors"));
-        return mean_value.real();
-      }
-    } // namespace internal
-
-
-    // avoid compiling inner function for many vector types when we always
-    // really do the same thing by putting the main work into this helper
-    // function
-    template <int dim, int spacedim, typename Number>
-    double
-    integrate_difference_inner(const Function<spacedim, Number> &exact_solution,
-                               const NormType &                  norm,
-                               const Function<spacedim> *        weight,
-                               const UpdateFlags                 update_flags,
-                               const double                      exponent,
-                               const unsigned int                n_components,
-                               IDScratchData<dim, spacedim, Number> &data)
-    {
-      const bool                             fe_is_system = (n_components != 1);
-      const dealii::FEValues<dim, spacedim> &fe_values =
-        data.x_fe_values.get_present_fe_values();
-      const unsigned int n_q_points = fe_values.n_quadrature_points;
-
-      if (weight != nullptr)
-        {
-          if (weight->n_components > 1)
-            weight->vector_value_list(fe_values.get_quadrature_points(),
-                                      data.weight_vectors);
-          else
-            {
-              weight->value_list(fe_values.get_quadrature_points(),
-                                 data.weight_values);
-              for (unsigned int k = 0; k < n_q_points; ++k)
-                data.weight_vectors[k] = data.weight_values[k];
-            }
-        }
-      else
-        {
-          for (unsigned int k = 0; k < n_q_points; ++k)
-            data.weight_vectors[k] = 1.;
-        }
-
-
-      if (update_flags & update_values)
-        {
-          // first compute the exact solution (vectors) at the quadrature
-          // points. try to do this as efficient as possible by avoiding a
-          // second virtual function call in case the function really has only
-          // one component
-          //
-          // TODO: we have to work a bit here because the Function<dim,double>
-          //   interface of the argument denoting the exact function only
-          //   provides us with double/Tensor<1,dim> values, rather than
-          //   with the correct data type. so evaluate into a temp
-          //   object, then copy around
-          if (fe_is_system)
-            {
-              exact_solution.vector_value_list(
-                fe_values.get_quadrature_points(), data.tmp_vector_values);
-              for (unsigned int i = 0; i < n_q_points; ++i)
-                data.psi_values[i] = data.tmp_vector_values[i];
-            }
-          else
-            {
-              exact_solution.value_list(fe_values.get_quadrature_points(),
-                                        data.tmp_values);
-              for (unsigned int i = 0; i < n_q_points; ++i)
-                data.psi_values[i](0) = data.tmp_values[i];
-            }
-
-          // then subtract finite element fe_function
-          for (unsigned int q = 0; q < n_q_points; ++q)
-            for (unsigned int i = 0; i < data.psi_values[q].size(); ++i)
-              data.psi_values[q][i] -= data.function_values[q][i];
-        }
-
-      // Do the same for gradients, if required
-      if (update_flags & update_gradients)
-        {
-          // try to be a little clever to avoid recursive virtual function
-          // calls when calling gradient_list for functions that are really
-          // scalar functions
-          if (fe_is_system)
-            {
-              exact_solution.vector_gradient_list(
-                fe_values.get_quadrature_points(), data.tmp_vector_gradients);
-              for (unsigned int i = 0; i < n_q_points; ++i)
-                for (unsigned int comp = 0; comp < data.psi_grads[i].size();
-                     ++comp)
-                  data.psi_grads[i][comp] = data.tmp_vector_gradients[i][comp];
-            }
-          else
-            {
-              exact_solution.gradient_list(fe_values.get_quadrature_points(),
-                                           data.tmp_gradients);
-              for (unsigned int i = 0; i < n_q_points; ++i)
-                data.psi_grads[i][0] = data.tmp_gradients[i];
-            }
-
-          // then subtract finite element function_grads. We need to be
-          // careful in the codimension one case, since there we only have
-          // tangential gradients in the finite element function, not the full
-          // gradient. This is taken care of, by subtracting the normal
-          // component of the gradient from the exact function.
-          if (update_flags & update_normal_vectors)
-            for (unsigned int k = 0; k < n_components; ++k)
-              for (unsigned int q = 0; q < n_q_points; ++q)
-                {
-                  // compute (f.n) n
-                  const typename ProductType<Number, double>::type f_dot_n =
-                    data.psi_grads[q][k] * fe_values.normal_vector(q);
-                  const Tensor<1, spacedim, Number> f_dot_n_times_n =
-                    f_dot_n * fe_values.normal_vector(q);
-
-                  data.psi_grads[q][k] -=
-                    (data.function_grads[q][k] + f_dot_n_times_n);
-                }
-          else
-            for (unsigned int k = 0; k < n_components; ++k)
-              for (unsigned int q = 0; q < n_q_points; ++q)
-                for (unsigned int d = 0; d < spacedim; ++d)
-                  data.psi_grads[q][k][d] -= data.function_grads[q][k][d];
-        }
-
-      double diff      = 0;
-      Number diff_mean = 0;
-
-      // First work on function values:
-      switch (norm)
-        {
-          case mean:
-            // Compute values in quadrature points and integrate
-            for (unsigned int q = 0; q < n_q_points; ++q)
-              {
-                Number sum = 0;
-                for (unsigned int k = 0; k < n_components; ++k)
-                  if (data.weight_vectors[q](k) != 0)
-                    sum += data.psi_values[q](k) * data.weight_vectors[q](k);
-                diff_mean += sum * fe_values.JxW(q);
-              }
-            break;
-
-          case Lp_norm:
-          case L1_norm:
-          case W1p_norm:
-            // Compute values in quadrature points and integrate
-            for (unsigned int q = 0; q < n_q_points; ++q)
-              {
-                double sum = 0;
-                for (unsigned int k = 0; k < n_components; ++k)
-                  if (data.weight_vectors[q](k) != 0)
-                    sum += std::pow(static_cast<double>(
-                                      numbers::NumberTraits<Number>::abs_square(
-                                        data.psi_values[q](k))),
-                                    exponent / 2.) *
-                           data.weight_vectors[q](k);
-                diff += sum * fe_values.JxW(q);
-              }
-
-            // Compute the root only if no derivative values are added later
-            if (!(update_flags & update_gradients))
-              diff = std::pow(diff, 1. / exponent);
-            break;
-
-          case L2_norm:
-          case H1_norm:
-            // Compute values in quadrature points and integrate
-            for (unsigned int q = 0; q < n_q_points; ++q)
-              {
-                double sum = 0;
-                for (unsigned int k = 0; k < n_components; ++k)
-                  if (data.weight_vectors[q](k) != 0)
-                    sum += numbers::NumberTraits<Number>::abs_square(
-                             data.psi_values[q](k)) *
-                           data.weight_vectors[q](k);
-                diff += sum * fe_values.JxW(q);
-              }
-            // Compute the root only, if no derivative values are added later
-            if (norm == L2_norm)
-              diff = std::sqrt(diff);
-            break;
-
-          case Linfty_norm:
-          case W1infty_norm:
-            for (unsigned int q = 0; q < n_q_points; ++q)
-              for (unsigned int k = 0; k < n_components; ++k)
-                if (data.weight_vectors[q](k) != 0)
-                  diff = std::max(diff,
-                                  double(std::abs(data.psi_values[q](k) *
-                                                  data.weight_vectors[q](k))));
-            break;
-
-          case H1_seminorm:
-          case Hdiv_seminorm:
-          case W1p_seminorm:
-          case W1infty_seminorm:
-            // function values are not used for these norms
-            break;
-
-          default:
-            Assert(false, ExcNotImplemented());
-            break;
-        }
-
-      // Now compute terms depending on derivatives:
-      switch (norm)
-        {
-          case W1p_seminorm:
-          case W1p_norm:
-            for (unsigned int q = 0; q < n_q_points; ++q)
-              {
-                double sum = 0;
-                for (unsigned int k = 0; k < n_components; ++k)
-                  if (data.weight_vectors[q](k) != 0)
-                    sum += std::pow(data.psi_grads[q][k].norm_square(),
-                                    exponent / 2.) *
-                           data.weight_vectors[q](k);
-                diff += sum * fe_values.JxW(q);
-              }
-            diff = std::pow(diff, 1. / exponent);
-            break;
-
-          case H1_seminorm:
-          case H1_norm:
-            for (unsigned int q = 0; q < n_q_points; ++q)
-              {
-                double sum = 0;
-                for (unsigned int k = 0; k < n_components; ++k)
-                  if (data.weight_vectors[q](k) != 0)
-                    sum += data.psi_grads[q][k].norm_square() *
-                           data.weight_vectors[q](k);
-                diff += sum * fe_values.JxW(q);
-              }
-            diff = std::sqrt(diff);
-            break;
-
-          case Hdiv_seminorm:
-            for (unsigned int q = 0; q < n_q_points; ++q)
-              {
-                unsigned int idx = 0;
-                if (weight != nullptr)
-                  for (; idx < n_components; ++idx)
-                    if (data.weight_vectors[0](idx) > 0)
-                      break;
-
-                Assert(
-                  n_components >= idx + dim,
-                  ExcMessage(
-                    "You can only ask for the Hdiv norm for a finite element "
-                    "with at least 'dim' components. In that case, this function "
-                    "will find the index of the first non-zero weight and take "
-                    "the divergence of the 'dim' components that follow it."));
-
-                Number sum = 0;
-                // take the trace of the derivatives scaled by the weight and
-                // square it
-                for (unsigned int k = idx; k < idx + dim; ++k)
-                  if (data.weight_vectors[q](k) != 0)
-                    sum += data.psi_grads[q][k][k - idx] *
-                           std::sqrt(data.weight_vectors[q](k));
-                diff += numbers::NumberTraits<Number>::abs_square(sum) *
-                        fe_values.JxW(q);
-              }
-            diff = std::sqrt(diff);
-            break;
-
-          case W1infty_seminorm:
-          case W1infty_norm:
-            {
-              double t = 0;
-              for (unsigned int q = 0; q < n_q_points; ++q)
-                for (unsigned int k = 0; k < n_components; ++k)
-                  if (data.weight_vectors[q](k) != 0)
-                    for (unsigned int d = 0; d < dim; ++d)
-                      t = std::max(t,
-                                   double(std::abs(data.psi_grads[q][k][d]) *
-                                          data.weight_vectors[q](k)));
-
-              // then add seminorm to norm if that had previously been computed
-              diff += t;
-            }
-            break;
-          default:
-            break;
-        }
-
-      if (norm == mean)
-        diff = internal::mean_to_double(diff_mean);
-
-      // append result of this cell to the end of the vector
-      AssertIsFinite(diff);
-      return diff;
-    }
-
-    template <int dim, int spacedim, typename Number>
-    DEAL_II_DEPRECATED
-      typename std::enable_if<!std::is_same<Number, double>::value,
-                              double>::type
-      integrate_difference_inner(
-        const Function<spacedim> &                      exact_solution,
-        const NormType &                                norm,
-        const Function<spacedim> *                      weight,
-        const UpdateFlags                               update_flags,
-        const double                                    exponent,
-        const unsigned int                              n_components,
-        DeprecatedIDScratchData<dim, spacedim, Number> &data)
-    {
-      const bool                             fe_is_system = (n_components != 1);
-      const dealii::FEValues<dim, spacedim> &fe_values =
-        data.x_fe_values.get_present_fe_values();
-      const unsigned int n_q_points = fe_values.n_quadrature_points;
-
-      if (weight != nullptr)
-        {
-          if (weight->n_components > 1)
-            weight->vector_value_list(fe_values.get_quadrature_points(),
-                                      data.weight_vectors);
-          else
-            {
-              weight->value_list(fe_values.get_quadrature_points(),
-                                 data.weight_values);
-              for (unsigned int k = 0; k < n_q_points; ++k)
-                data.weight_vectors[k] = data.weight_values[k];
-            }
-        }
-      else
-        {
-          for (unsigned int k = 0; k < n_q_points; ++k)
-            data.weight_vectors[k] = 1.;
-        }
-
-
-      if (update_flags & update_values)
-        {
-          // first compute the exact solution (vectors) at the quadrature
-          // points. try to do this as efficient as possible by avoiding a
-          // second virtual function call in case the function really has only
-          // one component
-          //
-          // TODO: we have to work a bit here because the Function<dim,double>
-          //   interface of the argument denoting the exact function only
-          //   provides us with double/Tensor<1,dim> values, rather than
-          //   with the correct data type. so evaluate into a temp
-          //   object, then copy around
-          if (fe_is_system)
-            {
-              exact_solution.vector_value_list(
-                fe_values.get_quadrature_points(), data.tmp_vector_values);
-              for (unsigned int i = 0; i < n_q_points; ++i)
-                data.psi_values[i] = data.tmp_vector_values[i];
-            }
-          else
-            {
-              exact_solution.value_list(fe_values.get_quadrature_points(),
-                                        data.tmp_values);
-              for (unsigned int i = 0; i < n_q_points; ++i)
-                data.psi_values[i](0) = data.tmp_values[i];
-            }
-
-          // then subtract finite element fe_function
-          for (unsigned int q = 0; q < n_q_points; ++q)
-            for (unsigned int i = 0; i < data.psi_values[q].size(); ++i)
-              data.psi_values[q][i] -= data.function_values[q][i];
-        }
-
-      // Do the same for gradients, if required
-      if (update_flags & update_gradients)
-        {
-          // try to be a little clever to avoid recursive virtual function
-          // calls when calling gradient_list for functions that are really
-          // scalar functions
-          if (fe_is_system)
-            {
-              exact_solution.vector_gradient_list(
-                fe_values.get_quadrature_points(), data.tmp_vector_gradients);
-              for (unsigned int i = 0; i < n_q_points; ++i)
-                for (unsigned int comp = 0; comp < data.psi_grads[i].size();
-                     ++comp)
-                  data.psi_grads[i][comp] = data.tmp_vector_gradients[i][comp];
-            }
-          else
-            {
-              exact_solution.gradient_list(fe_values.get_quadrature_points(),
-                                           data.tmp_gradients);
-              for (unsigned int i = 0; i < n_q_points; ++i)
-                data.psi_grads[i][0] = data.tmp_gradients[i];
-            }
-
-          // then subtract finite element function_grads. We need to be
-          // careful in the codimension one case, since there we only have
-          // tangential gradients in the finite element function, not the full
-          // gradient. This is taken care of, by subtracting the normal
-          // component of the gradient from the exact function.
-          if (update_flags & update_normal_vectors)
-            for (unsigned int k = 0; k < n_components; ++k)
-              for (unsigned int q = 0; q < n_q_points; ++q)
-                {
-                  // compute (f.n) n
-                  const typename ProductType<Number, double>::type f_dot_n =
-                    data.psi_grads[q][k] * fe_values.normal_vector(q);
-                  const Tensor<1, spacedim, Number> f_dot_n_times_n =
-                    f_dot_n * fe_values.normal_vector(q);
-
-                  data.psi_grads[q][k] -=
-                    (data.function_grads[q][k] + f_dot_n_times_n);
-                }
-          else
-            for (unsigned int k = 0; k < n_components; ++k)
-              for (unsigned int q = 0; q < n_q_points; ++q)
-                for (unsigned int d = 0; d < spacedim; ++d)
-                  data.psi_grads[q][k][d] -= data.function_grads[q][k][d];
-        }
-
-      double diff      = 0;
-      Number diff_mean = 0;
-
-      // First work on function values:
-      switch (norm)
-        {
-          case mean:
-            // Compute values in quadrature points and integrate
-            for (unsigned int q = 0; q < n_q_points; ++q)
-              {
-                Number sum = 0;
-                for (unsigned int k = 0; k < n_components; ++k)
-                  if (data.weight_vectors[q](k) != 0)
-                    sum += data.psi_values[q](k) * data.weight_vectors[q](k);
-                diff_mean += sum * fe_values.JxW(q);
-              }
-            break;
-
-          case Lp_norm:
-          case L1_norm:
-          case W1p_norm:
-            // Compute values in quadrature points and integrate
-            for (unsigned int q = 0; q < n_q_points; ++q)
-              {
-                double sum = 0;
-                for (unsigned int k = 0; k < n_components; ++k)
-                  if (data.weight_vectors[q](k) != 0)
-                    sum += std::pow(static_cast<double>(
-                                      numbers::NumberTraits<Number>::abs_square(
-                                        data.psi_values[q](k))),
-                                    exponent / 2.) *
-                           data.weight_vectors[q](k);
-                diff += sum * fe_values.JxW(q);
-              }
-
-            // Compute the root only if no derivative values are added later
-            if (!(update_flags & update_gradients))
-              diff = std::pow(diff, 1. / exponent);
-            break;
-
-          case L2_norm:
-          case H1_norm:
-            // Compute values in quadrature points and integrate
-            for (unsigned int q = 0; q < n_q_points; ++q)
-              {
-                double sum = 0;
-                for (unsigned int k = 0; k < n_components; ++k)
-                  if (data.weight_vectors[q](k) != 0)
-                    sum += numbers::NumberTraits<Number>::abs_square(
-                             data.psi_values[q](k)) *
-                           data.weight_vectors[q](k);
-                diff += sum * fe_values.JxW(q);
-              }
-            // Compute the root only, if no derivative values are added later
-            if (norm == L2_norm)
-              diff = std::sqrt(diff);
-            break;
-
-          case Linfty_norm:
-          case W1infty_norm:
-            for (unsigned int q = 0; q < n_q_points; ++q)
-              for (unsigned int k = 0; k < n_components; ++k)
-                if (data.weight_vectors[q](k) != 0)
-                  diff = std::max(diff,
-                                  double(std::abs(data.psi_values[q](k) *
-                                                  data.weight_vectors[q](k))));
-            break;
-
-          case H1_seminorm:
-          case Hdiv_seminorm:
-          case W1p_seminorm:
-          case W1infty_seminorm:
-            // function values are not used for these norms
-            break;
-
-          default:
-            Assert(false, ExcNotImplemented());
-            break;
-        }
-
-      // Now compute terms depending on derivatives:
-      switch (norm)
-        {
-          case W1p_seminorm:
-          case W1p_norm:
-            for (unsigned int q = 0; q < n_q_points; ++q)
-              {
-                double sum = 0;
-                for (unsigned int k = 0; k < n_components; ++k)
-                  if (data.weight_vectors[q](k) != 0)
-                    sum += std::pow(data.psi_grads[q][k].norm_square(),
-                                    exponent / 2.) *
-                           data.weight_vectors[q](k);
-                diff += sum * fe_values.JxW(q);
-              }
-            diff = std::pow(diff, 1. / exponent);
-            break;
-
-          case H1_seminorm:
-          case H1_norm:
-            for (unsigned int q = 0; q < n_q_points; ++q)
-              {
-                double sum = 0;
-                for (unsigned int k = 0; k < n_components; ++k)
-                  if (data.weight_vectors[q](k) != 0)
-                    sum += data.psi_grads[q][k].norm_square() *
-                           data.weight_vectors[q](k);
-                diff += sum * fe_values.JxW(q);
-              }
-            diff = std::sqrt(diff);
-            break;
-
-          case Hdiv_seminorm:
-            for (unsigned int q = 0; q < n_q_points; ++q)
-              {
-                unsigned int idx = 0;
-                if (weight != nullptr)
-                  for (; idx < n_components; ++idx)
-                    if (data.weight_vectors[0](idx) > 0)
-                      break;
-
-                Assert(
-                  n_components >= idx + dim,
-                  ExcMessage(
-                    "You can only ask for the Hdiv norm for a finite element "
-                    "with at least 'dim' components. In that case, this function "
-                    "will find the index of the first non-zero weight and take "
-                    "the divergence of the 'dim' components that follow it."));
-
-                Number sum = 0;
-                // take the trace of the derivatives scaled by the weight and
-                // square it
-                for (unsigned int k = idx; k < idx + dim; ++k)
-                  if (data.weight_vectors[q](k) != 0)
-                    sum += data.psi_grads[q][k][k - idx] *
-                           std::sqrt(data.weight_vectors[q](k));
-                diff += numbers::NumberTraits<Number>::abs_square(sum) *
-                        fe_values.JxW(q);
-              }
-            diff = std::sqrt(diff);
-            break;
-
-          case W1infty_seminorm:
-          case W1infty_norm:
-            {
-              double t = 0;
-              for (unsigned int q = 0; q < n_q_points; ++q)
-                for (unsigned int k = 0; k < n_components; ++k)
-                  if (data.weight_vectors[q](k) != 0)
-                    for (unsigned int d = 0; d < dim; ++d)
-                      t = std::max(t,
-                                   double(std::abs(data.psi_grads[q][k][d]) *
-                                          data.weight_vectors[q](k)));
-
-              // then add seminorm to norm if that had previously been computed
-              diff += t;
-            }
-            break;
-          default:
-            break;
-        }
-
-      if (norm == mean)
-        diff = internal::mean_to_double(diff_mean);
-
-      // append result of this cell to the end of the vector
-      AssertIsFinite(diff);
-      return diff;
-    }
-
-
-
-    template <int dim,
-              class InVector,
-              class OutVector,
-              typename DoFHandlerType,
-              int spacedim>
-    static void
-    do_integrate_difference(
-      const dealii::hp::MappingCollection<dim, spacedim> &     mapping,
-      const DoFHandlerType &                                   dof,
-      const InVector &                                         fe_function,
-      const Function<spacedim, typename InVector::value_type> &exact_solution,
-      OutVector &                                              difference,
-      const dealii::hp::QCollection<dim> &                     q,
-      const NormType &                                         norm,
-      const Function<spacedim> *                               weight,
-      const double                                             exponent_1)
-    {
-      using Number = typename InVector::value_type;
-      // we mark the "exponent" parameter to this function "const" since it is
-      // strictly incoming, but we need to set it to something different later
-      // on, if necessary, so have a read-write version of it:
-      double exponent = exponent_1;
-
-      const unsigned int n_components = dof.get_fe(0).n_components();
-
-      Assert(exact_solution.n_components == n_components,
-             ExcDimensionMismatch(exact_solution.n_components, n_components));
-
-      if (weight != nullptr)
-        {
-          Assert((weight->n_components == 1) ||
-                   (weight->n_components == n_components),
-                 ExcDimensionMismatch(weight->n_components, n_components));
-        }
-
-      difference.reinit(dof.get_triangulation().n_active_cells());
-
-      switch (norm)
-        {
-          case L2_norm:
-          case H1_seminorm:
-          case H1_norm:
-          case Hdiv_seminorm:
-            exponent = 2.;
-            break;
-
-          case L1_norm:
-            exponent = 1.;
-            break;
-
-          default:
-            break;
-        }
-
-      UpdateFlags update_flags =
-        UpdateFlags(update_quadrature_points | update_JxW_values);
-      switch (norm)
-        {
-          case H1_seminorm:
-          case Hdiv_seminorm:
-          case W1p_seminorm:
-          case W1infty_seminorm:
-            update_flags |= UpdateFlags(update_gradients);
-            if (spacedim == dim + 1)
-              update_flags |= UpdateFlags(update_normal_vectors);
-
-            break;
-
-          case H1_norm:
-          case W1p_norm:
-          case W1infty_norm:
-            update_flags |= UpdateFlags(update_gradients);
-            if (spacedim == dim + 1)
-              update_flags |= UpdateFlags(update_normal_vectors);
-            DEAL_II_FALLTHROUGH;
-
-          default:
-            update_flags |= UpdateFlags(update_values);
-            break;
-        }
-
-      const dealii::hp::FECollection<dim, spacedim> &fe_collection =
-        dof.get_fe_collection();
-      IDScratchData<dim, spacedim, Number> data(mapping,
-                                                fe_collection,
-                                                q,
-                                                update_flags);
-
-      // loop over all cells
-      for (const auto &cell : dof.active_cell_iterators())
-        if (cell->is_locally_owned())
-          {
-            // initialize for this cell
-            data.x_fe_values.reinit(cell);
-
-            const dealii::FEValues<dim, spacedim> &fe_values =
-              data.x_fe_values.get_present_fe_values();
-            const unsigned int n_q_points = fe_values.n_quadrature_points;
-            data.resize_vectors(n_q_points, n_components);
-
-            if (update_flags & update_values)
-              fe_values.get_function_values(fe_function, data.function_values);
-            if (update_flags & update_gradients)
-              fe_values.get_function_gradients(fe_function,
-                                               data.function_grads);
-
-            difference(cell->active_cell_index()) =
-              integrate_difference_inner<dim, spacedim, Number>(exact_solution,
-                                                                norm,
-                                                                weight,
-                                                                update_flags,
-                                                                exponent,
-                                                                n_components,
-                                                                data);
-          }
-        else
-          // the cell is a ghost cell or is artificial. write a zero into the
-          // corresponding value of the returned vector
-          difference(cell->active_cell_index()) = 0;
-    }
-
-    template <int dim,
-              class InVector,
-              class OutVector,
-              typename DoFHandlerType,
-              int spacedim>
-    DEAL_II_DEPRECATED static typename std::enable_if<
-      !std::is_same<typename InVector::value_type, double>::value>::type
-    do_integrate_difference(
-      const dealii::hp::MappingCollection<dim, spacedim> &mapping,
-      const DoFHandlerType &                              dof,
-      const InVector &                                    fe_function,
-      const Function<spacedim> &                          exact_solution,
-      OutVector &                                         difference,
-      const dealii::hp::QCollection<dim> &                q,
-      const NormType &                                    norm,
-      const Function<spacedim> *                          weight,
-      const double                                        exponent_1)
-    {
-      using Number = typename InVector::value_type;
-      // we mark the "exponent" parameter to this function "const" since it is
-      // strictly incoming, but we need to set it to something different later
-      // on, if necessary, so have a read-write version of it:
-      double exponent = exponent_1;
-
-      const unsigned int n_components = dof.get_fe(0).n_components();
-
-      Assert(exact_solution.n_components == n_components,
-             ExcDimensionMismatch(exact_solution.n_components, n_components));
-
-      if (weight != nullptr)
-        {
-          Assert((weight->n_components == 1) ||
-                   (weight->n_components == n_components),
-                 ExcDimensionMismatch(weight->n_components, n_components));
-        }
-
-      difference.reinit(dof.get_triangulation().n_active_cells());
-
-      switch (norm)
-        {
-          case L2_norm:
-          case H1_seminorm:
-          case H1_norm:
-          case Hdiv_seminorm:
-            exponent = 2.;
-            break;
-
-          case L1_norm:
-            exponent = 1.;
-            break;
-
-          default:
-            break;
-        }
-
-      UpdateFlags update_flags =
-        UpdateFlags(update_quadrature_points | update_JxW_values);
-      switch (norm)
-        {
-          case H1_seminorm:
-          case Hdiv_seminorm:
-          case W1p_seminorm:
-          case W1infty_seminorm:
-            update_flags |= UpdateFlags(update_gradients);
-            if (spacedim == dim + 1)
-              update_flags |= UpdateFlags(update_normal_vectors);
-
-            break;
-
-          case H1_norm:
-          case W1p_norm:
-          case W1infty_norm:
-            update_flags |= UpdateFlags(update_gradients);
-            if (spacedim == dim + 1)
-              update_flags |= UpdateFlags(update_normal_vectors);
-            DEAL_II_FALLTHROUGH;
-
-          default:
-            update_flags |= UpdateFlags(update_values);
-            break;
-        }
-
-      const dealii::hp::FECollection<dim, spacedim> &fe_collection =
-        dof.get_fe_collection();
-      DeprecatedIDScratchData<dim, spacedim, Number> data(mapping,
-                                                          fe_collection,
-                                                          q,
-                                                          update_flags);
-
-      // loop over all cells
-      for (const auto &cell : dof.active_cell_iterators())
-        if (cell->is_locally_owned())
-          {
-            // initialize for this cell
-            data.x_fe_values.reinit(cell);
-
-            const dealii::FEValues<dim, spacedim> &fe_values =
-              data.x_fe_values.get_present_fe_values();
-            const unsigned int n_q_points = fe_values.n_quadrature_points;
-            data.resize_vectors(n_q_points, n_components);
-
-            if (update_flags & update_values)
-              fe_values.get_function_values(fe_function, data.function_values);
-            if (update_flags & update_gradients)
-              fe_values.get_function_gradients(fe_function,
-                                               data.function_grads);
-
-            difference(cell->active_cell_index()) =
-              integrate_difference_inner<dim, spacedim, Number>(exact_solution,
-                                                                norm,
-                                                                weight,
-                                                                update_flags,
-                                                                exponent,
-                                                                n_components,
-                                                                data);
-          }
-        else
-          // the cell is a ghost cell or is artificial. write a zero into the
-          // corresponding value of the returned vector
-          difference(cell->active_cell_index()) = 0;
-    }
-
-  } // namespace internal
-
-
-
-  template <int dim, class InVector, class OutVector, int spacedim>
-  void
-  integrate_difference(
-    const Mapping<dim, spacedim> &                           mapping,
-    const DoFHandler<dim, spacedim> &                        dof,
-    const InVector &                                         fe_function,
-    const Function<spacedim, typename InVector::value_type> &exact_solution,
-    OutVector &                                              difference,
-    const Quadrature<dim> &                                  q,
-    const NormType &                                         norm,
-    const Function<spacedim> *                               weight,
-    const double                                             exponent)
-  {
-    internal ::do_integrate_difference(hp::MappingCollection<dim, spacedim>(
-                                         mapping),
-                                       dof,
-                                       fe_function,
-                                       exact_solution,
-                                       difference,
-                                       hp::QCollection<dim>(q),
-                                       norm,
-                                       weight,
-                                       exponent);
-  }
-
-  template <int dim, class InVector, class OutVector, int spacedim>
-  DEAL_II_DEPRECATED typename std::enable_if<
-    !std::is_same<typename InVector::value_type, double>::value>::type
-  integrate_difference(const Mapping<dim, spacedim> &   mapping,
-                       const DoFHandler<dim, spacedim> &dof,
-                       const InVector &                 fe_function,
-                       const Function<spacedim> &       exact_solution,
-                       OutVector &                      difference,
-                       const Quadrature<dim> &          q,
-                       const NormType &                 norm,
-                       const Function<spacedim> *       weight,
-                       const double                     exponent)
-  {
-    internal ::do_integrate_difference(hp::MappingCollection<dim, spacedim>(
-                                         mapping),
-                                       dof,
-                                       fe_function,
-                                       exact_solution,
-                                       difference,
-                                       hp::QCollection<dim>(q),
-                                       norm,
-                                       weight,
-                                       exponent);
-  }
-
-
-  template <int dim, class InVector, class OutVector, int spacedim>
-  void
-  integrate_difference(
-    const DoFHandler<dim, spacedim> &                        dof,
-    const InVector &                                         fe_function,
-    const Function<spacedim, typename InVector::value_type> &exact_solution,
-    OutVector &                                              difference,
-    const Quadrature<dim> &                                  q,
-    const NormType &                                         norm,
-    const Function<spacedim> *                               weight,
-    const double                                             exponent)
-  {
-    internal ::do_integrate_difference(
-      hp::StaticMappingQ1<dim, spacedim>::mapping_collection,
-      dof,
-      fe_function,
-      exact_solution,
-      difference,
-      hp::QCollection<dim>(q),
-      norm,
-      weight,
-      exponent);
-  }
-
-
-  template <int dim, class InVector, class OutVector, int spacedim>
-  DEAL_II_DEPRECATED typename std::enable_if<
-    !std::is_same<typename InVector::value_type, double>::value>::type
-  integrate_difference(const DoFHandler<dim, spacedim> &dof,
-                       const InVector &                 fe_function,
-                       const Function<spacedim> &       exact_solution,
-                       OutVector &                      difference,
-                       const Quadrature<dim> &          q,
-                       const NormType &                 norm,
-                       const Function<spacedim> *       weight,
-                       const double                     exponent)
-  {
-    internal ::do_integrate_difference(
-      hp::StaticMappingQ1<dim, spacedim>::mapping_collection,
-      dof,
-      fe_function,
-      exact_solution,
-      difference,
-      hp::QCollection<dim>(q),
-      norm,
-      weight,
-      exponent);
-  }
-
-
-
-  template <int dim, class InVector, class OutVector, int spacedim>
-  void
-  integrate_difference(
-    const dealii::hp::MappingCollection<dim, spacedim> &     mapping,
-    const dealii::hp::DoFHandler<dim, spacedim> &            dof,
-    const InVector &                                         fe_function,
-    const Function<spacedim, typename InVector::value_type> &exact_solution,
-    OutVector &                                              difference,
-    const dealii::hp::QCollection<dim> &                     q,
-    const NormType &                                         norm,
-    const Function<spacedim> *                               weight,
-    const double                                             exponent)
-  {
-    internal ::do_integrate_difference(hp::MappingCollection<dim, spacedim>(
-                                         mapping),
-                                       dof,
-                                       fe_function,
-                                       exact_solution,
-                                       difference,
-                                       q,
-                                       norm,
-                                       weight,
-                                       exponent);
-  }
-
-  template <int dim, class InVector, class OutVector, int spacedim>
-  DEAL_II_DEPRECATED typename std::enable_if<
-    !std::is_same<typename InVector::value_type, double>::value>::type
-  integrate_difference(
-    const dealii::hp::MappingCollection<dim, spacedim> &mapping,
-    const dealii::hp::DoFHandler<dim, spacedim> &       dof,
-    const InVector &                                    fe_function,
-    const Function<spacedim> &                          exact_solution,
-    OutVector &                                         difference,
-    const dealii::hp::QCollection<dim> &                q,
-    const NormType &                                    norm,
-    const Function<spacedim> *                          weight,
-    const double                                        exponent)
-  {
-    internal ::do_integrate_difference(hp::MappingCollection<dim, spacedim>(
-                                         mapping),
-                                       dof,
-                                       fe_function,
-                                       exact_solution,
-                                       difference,
-                                       q,
-                                       norm,
-                                       weight,
-                                       exponent);
-  }
-
-
-  template <int dim, class InVector, class OutVector, int spacedim>
-  void
-  integrate_difference(
-    const dealii::hp::DoFHandler<dim, spacedim> &            dof,
-    const InVector &                                         fe_function,
-    const Function<spacedim, typename InVector::value_type> &exact_solution,
-    OutVector &                                              difference,
-    const dealii::hp::QCollection<dim> &                     q,
-    const NormType &                                         norm,
-    const Function<spacedim> *                               weight,
-    const double                                             exponent)
-  {
-    internal ::do_integrate_difference(
-      hp::StaticMappingQ1<dim, spacedim>::mapping_collection,
-      dof,
-      fe_function,
-      exact_solution,
-      difference,
-      q,
-      norm,
-      weight,
-      exponent);
-  }
-
-  template <int dim, class InVector, class OutVector, int spacedim>
-  DEAL_II_DEPRECATED typename std::enable_if<
-    !std::is_same<typename InVector::value_type, double>::value>::type
-  integrate_difference(const dealii::hp::DoFHandler<dim, spacedim> &dof,
-                       const InVector &                             fe_function,
-                       const Function<spacedim> &          exact_solution,
-                       OutVector &                         difference,
-                       const dealii::hp::QCollection<dim> &q,
-                       const NormType &                    norm,
-                       const Function<spacedim> *          weight,
-                       const double                        exponent)
-  {
-    internal ::do_integrate_difference(
-      hp::StaticMappingQ1<dim, spacedim>::mapping_collection,
-      dof,
-      fe_function,
-      exact_solution,
-      difference,
-      q,
-      norm,
-      weight,
-      exponent);
-  }
-
-  template <int dim, int spacedim, class InVector>
-  double
-  compute_global_error(const Triangulation<dim, spacedim> &tria,
-                       const InVector &                    cellwise_error,
-                       const NormType &                    norm,
-                       const double                        exponent)
-  {
-    Assert(cellwise_error.size() == tria.n_active_cells(),
-           ExcMessage("input vector cell_error has invalid size!"));
-#ifdef DEBUG
-    {
-      // check that off-processor entries are zero. Otherwise we will compute
-      // wrong results below!
-      typename InVector::size_type                                i = 0;
-      typename Triangulation<dim, spacedim>::active_cell_iterator it =
-        tria.begin_active();
-      for (; i < cellwise_error.size(); ++i, ++it)
-        if (!it->is_locally_owned())
-          Assert(
-            std::fabs(cellwise_error[i]) < 1e-20,
-            ExcMessage(
-              "cellwise_error of cells that are not locally owned need to be zero!"));
-    }
-#endif
-
-    MPI_Comm comm = MPI_COMM_SELF;
-#ifdef DEAL_II_WITH_MPI
-    if (const parallel::TriangulationBase<dim, spacedim> *ptria =
-          dynamic_cast<const parallel::TriangulationBase<dim, spacedim> *>(
-            &tria))
-      comm = ptria->get_communicator();
-#endif
-
-    switch (norm)
-      {
-        case L2_norm:
-        case H1_seminorm:
-        case H1_norm:
-        case Hdiv_seminorm:
-          {
-            const double local = cellwise_error.l2_norm();
-            return std::sqrt(Utilities::MPI::sum(local * local, comm));
-          }
-
-        case L1_norm:
-          {
-            const double local = cellwise_error.l1_norm();
-            return Utilities::MPI::sum(local, comm);
-          }
-
-        case Linfty_norm:
-        case W1infty_seminorm:
-          {
-            const double local = cellwise_error.linfty_norm();
-            return Utilities::MPI::max(local, comm);
-          }
-
-        case W1infty_norm:
-          {
-            AssertThrow(false,
-                        ExcMessage(
-                          "compute_global_error() is impossible for "
-                          "the W1infty_norm. See the documentation for "
-                          "NormType::W1infty_norm for more information."));
-            return std::numeric_limits<double>::infinity();
-          }
-
-        case mean:
-          {
-            // Note: mean is defined as int_\Omega f = sum_K \int_K f, so we
-            // need the sum of the cellwise errors not the Euclidean mean value
-            // that is returned by Vector<>::mean_value().
-            const double local =
-              cellwise_error.mean_value() * cellwise_error.size();
-            return Utilities::MPI::sum(local, comm);
-          }
-
-        case Lp_norm:
-        case W1p_norm:
-        case W1p_seminorm:
-          {
-            double                       local = 0;
-            typename InVector::size_type i;
-            typename Triangulation<dim, spacedim>::active_cell_iterator it =
-              tria.begin_active();
-            for (i = 0; i < cellwise_error.size(); ++i, ++it)
-              if (it->is_locally_owned())
-                local += std::pow(cellwise_error[i], exponent);
-
-            return std::pow(Utilities::MPI::sum(local, comm), 1. / exponent);
-          }
-
-        default:
-          AssertThrow(false, ExcNotImplemented());
-          break;
-      }
-    return 0.0;
-  }
-
-  template <int dim, typename VectorType, int spacedim>
-  void
-  point_difference(
-    const DoFHandler<dim, spacedim> &                          dof,
-    const VectorType &                                         fe_function,
-    const Function<spacedim, typename VectorType::value_type> &exact_function,
-    Vector<typename VectorType::value_type> &                  difference,
-    const Point<spacedim> &                                    point)
-  {
-    point_difference(StaticMappingQ1<dim>::mapping,
-                     dof,
-                     fe_function,
-                     exact_function,
-                     difference,
-                     point);
-  }
-
-
-  template <int dim, typename VectorType, int spacedim>
-  void
-  point_difference(
-    const Mapping<dim, spacedim> &                             mapping,
-    const DoFHandler<dim, spacedim> &                          dof,
-    const VectorType &                                         fe_function,
-    const Function<spacedim, typename VectorType::value_type> &exact_function,
-    Vector<typename VectorType::value_type> &                  difference,
-    const Point<spacedim> &                                    point)
-  {
-    using Number                 = typename VectorType::value_type;
-    const FiniteElement<dim> &fe = dof.get_fe();
-
-    Assert(difference.size() == fe.n_components(),
-           ExcDimensionMismatch(difference.size(), fe.n_components()));
-
-    // first find the cell in which this point
-    // is, initialize a quadrature rule with
-    // it, and then a FEValues object
-    const std::pair<typename DoFHandler<dim, spacedim>::active_cell_iterator,
-                    Point<spacedim>>
-      cell_point =
-        GridTools::find_active_cell_around_point(mapping, dof, point);
-
-    AssertThrow(cell_point.first->is_locally_owned(),
-                ExcPointNotAvailableHere());
-    Assert(GeometryInfo<dim>::distance_to_unit_cell(cell_point.second) < 1e-10,
-           ExcInternalError());
-
-    const Quadrature<dim> quadrature(
-      GeometryInfo<dim>::project_to_unit_cell(cell_point.second));
-    FEValues<dim> fe_values(mapping, fe, quadrature, update_values);
-    fe_values.reinit(cell_point.first);
-
-    // then use this to get at the values of
-    // the given fe_function at this point
-    std::vector<Vector<Number>> u_value(1, Vector<Number>(fe.n_components()));
-    fe_values.get_function_values(fe_function, u_value);
-
-    if (fe.n_components() == 1)
-      difference(0) = exact_function.value(point);
-    else
-      exact_function.vector_value(point, difference);
-
-    for (unsigned int i = 0; i < difference.size(); ++i)
-      difference(i) -= u_value[0](i);
-  }
-
-
-  template <int dim, typename VectorType, int spacedim>
-  void
-  point_value(const DoFHandler<dim, spacedim> &        dof,
-              const VectorType &                       fe_function,
-              const Point<spacedim> &                  point,
-              Vector<typename VectorType::value_type> &value)
-  {
-    point_value(
-      StaticMappingQ1<dim, spacedim>::mapping, dof, fe_function, point, value);
-  }
-
-
-  template <int dim, typename VectorType, int spacedim>
-  void
-  point_value(const hp::DoFHandler<dim, spacedim> &    dof,
-              const VectorType &                       fe_function,
-              const Point<spacedim> &                  point,
-              Vector<typename VectorType::value_type> &value)
-  {
-    point_value(hp::StaticMappingQ1<dim, spacedim>::mapping_collection,
-                dof,
-                fe_function,
-                point,
-                value);
-  }
-
-
-  template <int dim, typename VectorType, int spacedim>
-  typename VectorType::value_type
-  point_value(const DoFHandler<dim, spacedim> &dof,
-              const VectorType &               fe_function,
-              const Point<spacedim> &          point)
-  {
-    return point_value(StaticMappingQ1<dim, spacedim>::mapping,
-                       dof,
-                       fe_function,
-                       point);
-  }
-
-
-  template <int dim, typename VectorType, int spacedim>
-  typename VectorType::value_type
-  point_value(const hp::DoFHandler<dim, spacedim> &dof,
-              const VectorType &                   fe_function,
-              const Point<spacedim> &              point)
-  {
-    return point_value(hp::StaticMappingQ1<dim, spacedim>::mapping_collection,
-                       dof,
-                       fe_function,
-                       point);
-  }
-
-
-  template <int dim, typename VectorType, int spacedim>
-  void
-  point_value(const Mapping<dim, spacedim> &           mapping,
-              const DoFHandler<dim, spacedim> &        dof,
-              const VectorType &                       fe_function,
-              const Point<spacedim> &                  point,
-              Vector<typename VectorType::value_type> &value)
-  {
-    using Number                 = typename VectorType::value_type;
-    const FiniteElement<dim> &fe = dof.get_fe();
-
-    Assert(value.size() == fe.n_components(),
-           ExcDimensionMismatch(value.size(), fe.n_components()));
-
-    // first find the cell in which this point
-    // is, initialize a quadrature rule with
-    // it, and then a FEValues object
-    const std::pair<typename DoFHandler<dim, spacedim>::active_cell_iterator,
-                    Point<spacedim>>
-      cell_point =
-        GridTools::find_active_cell_around_point(mapping, dof, point);
-
-    AssertThrow(cell_point.first->is_locally_owned(),
-                ExcPointNotAvailableHere());
-    Assert(GeometryInfo<dim>::distance_to_unit_cell(cell_point.second) < 1e-10,
-           ExcInternalError());
-
-    const Quadrature<dim> quadrature(
-      GeometryInfo<dim>::project_to_unit_cell(cell_point.second));
-
-    FEValues<dim> fe_values(mapping, fe, quadrature, update_values);
-    fe_values.reinit(cell_point.first);
-
-    // then use this to get at the values of
-    // the given fe_function at this point
-    std::vector<Vector<Number>> u_value(1, Vector<Number>(fe.n_components()));
-    fe_values.get_function_values(fe_function, u_value);
-
-    value = u_value[0];
-  }
-
-
-  template <int dim, typename VectorType, int spacedim>
-  void
-  point_value(const hp::MappingCollection<dim, spacedim> &mapping,
-              const hp::DoFHandler<dim, spacedim> &       dof,
-              const VectorType &                          fe_function,
-              const Point<spacedim> &                     point,
-              Vector<typename VectorType::value_type> &   value)
-  {
-    using Number                              = typename VectorType::value_type;
-    const hp::FECollection<dim, spacedim> &fe = dof.get_fe_collection();
-
-    Assert(value.size() == fe.n_components(),
-           ExcDimensionMismatch(value.size(), fe.n_components()));
-
-    // first find the cell in which this point
-    // is, initialize a quadrature rule with
-    // it, and then a FEValues object
-    const std::pair<
-      typename hp::DoFHandler<dim, spacedim>::active_cell_iterator,
-      Point<spacedim>>
-      cell_point =
-        GridTools::find_active_cell_around_point(mapping, dof, point);
-
-    AssertThrow(cell_point.first->is_locally_owned(),
-                ExcPointNotAvailableHere());
-    Assert(GeometryInfo<dim>::distance_to_unit_cell(cell_point.second) < 1e-10,
-           ExcInternalError());
-
-    const Quadrature<dim> quadrature(
-      GeometryInfo<dim>::project_to_unit_cell(cell_point.second));
-    hp::FEValues<dim, spacedim> hp_fe_values(mapping,
-                                             fe,
-                                             hp::QCollection<dim>(quadrature),
-                                             update_values);
-    hp_fe_values.reinit(cell_point.first);
-    const FEValues<dim, spacedim> &fe_values =
-      hp_fe_values.get_present_fe_values();
-
-    // then use this to get at the values of
-    // the given fe_function at this point
-    std::vector<Vector<Number>> u_value(1, Vector<Number>(fe.n_components()));
-    fe_values.get_function_values(fe_function, u_value);
-
-    value = u_value[0];
-  }
-
-
-  template <int dim, typename VectorType, int spacedim>
-  typename VectorType::value_type
-  point_value(const Mapping<dim, spacedim> &   mapping,
-              const DoFHandler<dim, spacedim> &dof,
-              const VectorType &               fe_function,
-              const Point<spacedim> &          point)
-  {
-    Assert(dof.get_fe(0).n_components() == 1,
-           ExcMessage(
-             "Finite element is not scalar as is necessary for this function"));
-
-    Vector<typename VectorType::value_type> value(1);
-    point_value(mapping, dof, fe_function, point, value);
-
-    return value(0);
-  }
-
-
-  template <int dim, typename VectorType, int spacedim>
-  typename VectorType::value_type
-  point_value(const hp::MappingCollection<dim, spacedim> &mapping,
-              const hp::DoFHandler<dim, spacedim> &       dof,
-              const VectorType &                          fe_function,
-              const Point<spacedim> &                     point)
-  {
-    Assert(dof.get_fe(0).n_components() == 1,
-           ExcMessage(
-             "Finite element is not scalar as is necessary for this function"));
-
-    Vector<typename VectorType::value_type> value(1);
-    point_value(mapping, dof, fe_function, point, value);
-
-    return value(0);
-  }
-
-
-
-  template <int dim, typename VectorType, int spacedim>
-  void
-  point_gradient(
-    const DoFHandler<dim, spacedim> &dof,
-    const VectorType &               fe_function,
-    const Point<spacedim> &          point,
-    std::vector<Tensor<1, spacedim, typename VectorType::value_type>>
-      &gradients)
-  {
-    point_gradient(StaticMappingQ1<dim, spacedim>::mapping,
-                   dof,
-                   fe_function,
-                   point,
-                   gradients);
-  }
-
-
-  template <int dim, typename VectorType, int spacedim>
-  void
-  point_gradient(
-    const hp::DoFHandler<dim, spacedim> &dof,
-    const VectorType &                   fe_function,
-    const Point<spacedim> &              point,
-    std::vector<Tensor<1, spacedim, typename VectorType::value_type>>
-      &gradients)
-  {
-    point_gradient(hp::StaticMappingQ1<dim, spacedim>::mapping_collection,
-                   dof,
-                   fe_function,
-                   point,
-                   gradients);
-  }
-
-
-  template <int dim, typename VectorType, int spacedim>
-  Tensor<1, spacedim, typename VectorType::value_type>
-  point_gradient(const DoFHandler<dim, spacedim> &dof,
-                 const VectorType &               fe_function,
-                 const Point<spacedim> &          point)
-  {
-    return point_gradient(StaticMappingQ1<dim, spacedim>::mapping,
-                          dof,
-                          fe_function,
-                          point);
-  }
-
-
-  template <int dim, typename VectorType, int spacedim>
-  Tensor<1, spacedim, typename VectorType::value_type>
-  point_gradient(const hp::DoFHandler<dim, spacedim> &dof,
-                 const VectorType &                   fe_function,
-                 const Point<spacedim> &              point)
-  {
-    return point_gradient(
-      hp::StaticMappingQ1<dim, spacedim>::mapping_collection,
-      dof,
-      fe_function,
-      point);
-  }
-
-
-  template <int dim, typename VectorType, int spacedim>
-  void
-  point_gradient(
-    const Mapping<dim, spacedim> &   mapping,
-    const DoFHandler<dim, spacedim> &dof,
-    const VectorType &               fe_function,
-    const Point<spacedim> &          point,
-    std::vector<Tensor<1, spacedim, typename VectorType::value_type>> &gradient)
-  {
-    const FiniteElement<dim> &fe = dof.get_fe();
-
-    Assert(gradient.size() == fe.n_components(),
-           ExcDimensionMismatch(gradient.size(), fe.n_components()));
-
-    // first find the cell in which this point
-    // is, initialize a quadrature rule with
-    // it, and then a FEValues object
-    const std::pair<typename DoFHandler<dim, spacedim>::active_cell_iterator,
-                    Point<spacedim>>
-      cell_point =
-        GridTools::find_active_cell_around_point(mapping, dof, point);
-
-    AssertThrow(cell_point.first->is_locally_owned(),
-                ExcPointNotAvailableHere());
-    Assert(GeometryInfo<dim>::distance_to_unit_cell(cell_point.second) < 1e-10,
-           ExcInternalError());
-
-    const Quadrature<dim> quadrature(
-      GeometryInfo<dim>::project_to_unit_cell(cell_point.second));
-
-    FEValues<dim> fe_values(mapping, fe, quadrature, update_gradients);
-    fe_values.reinit(cell_point.first);
-
-    // then use this to get the gradients of
-    // the given fe_function at this point
-    using Number = typename VectorType::value_type;
-    std::vector<std::vector<Tensor<1, dim, Number>>> u_gradient(
-      1, std::vector<Tensor<1, dim, Number>>(fe.n_components()));
-    fe_values.get_function_gradients(fe_function, u_gradient);
-
-    gradient = u_gradient[0];
-  }
-
-
-  template <int dim, typename VectorType, int spacedim>
-  void
-  point_gradient(
-    const hp::MappingCollection<dim, spacedim> &mapping,
-    const hp::DoFHandler<dim, spacedim> &       dof,
-    const VectorType &                          fe_function,
-    const Point<spacedim> &                     point,
-    std::vector<Tensor<1, spacedim, typename VectorType::value_type>> &gradient)
-  {
-    using Number                              = typename VectorType::value_type;
-    const hp::FECollection<dim, spacedim> &fe = dof.get_fe_collection();
-
-    Assert(gradient.size() == fe.n_components(),
-           ExcDimensionMismatch(gradient.size(), fe.n_components()));
-
-    // first find the cell in which this point
-    // is, initialize a quadrature rule with
-    // it, and then a FEValues object
-    const std::pair<
-      typename hp::DoFHandler<dim, spacedim>::active_cell_iterator,
-      Point<spacedim>>
-      cell_point =
-        GridTools::find_active_cell_around_point(mapping, dof, point);
-
-    AssertThrow(cell_point.first->is_locally_owned(),
-                ExcPointNotAvailableHere());
-    Assert(GeometryInfo<dim>::distance_to_unit_cell(cell_point.second) < 1e-10,
-           ExcInternalError());
-
-    const Quadrature<dim> quadrature(
-      GeometryInfo<dim>::project_to_unit_cell(cell_point.second));
-    hp::FEValues<dim, spacedim> hp_fe_values(mapping,
-                                             fe,
-                                             hp::QCollection<dim>(quadrature),
-                                             update_gradients);
-    hp_fe_values.reinit(cell_point.first);
-    const FEValues<dim, spacedim> &fe_values =
-      hp_fe_values.get_present_fe_values();
-
-    std::vector<std::vector<Tensor<1, dim, Number>>> u_gradient(
-      1, std::vector<Tensor<1, dim, Number>>(fe.n_components()));
-    fe_values.get_function_gradients(fe_function, u_gradient);
-
-    gradient = u_gradient[0];
-  }
-
-
-  template <int dim, typename VectorType, int spacedim>
-  Tensor<1, spacedim, typename VectorType::value_type>
-  point_gradient(const Mapping<dim, spacedim> &   mapping,
-                 const DoFHandler<dim, spacedim> &dof,
-                 const VectorType &               fe_function,
-                 const Point<spacedim> &          point)
-  {
-    Assert(dof.get_fe(0).n_components() == 1,
-           ExcMessage(
-             "Finite element is not scalar as is necessary for this function"));
-
-    std::vector<Tensor<1, dim, typename VectorType::value_type>> gradient(1);
-    point_gradient(mapping, dof, fe_function, point, gradient);
-
-    return gradient[0];
-  }
-
-
-
-  template <int dim, typename VectorType, int spacedim>
-  Tensor<1, spacedim, typename VectorType::value_type>
-  point_gradient(const hp::MappingCollection<dim, spacedim> &mapping,
-                 const hp::DoFHandler<dim, spacedim> &       dof,
-                 const VectorType &                          fe_function,
-                 const Point<spacedim> &                     point)
-  {
-    Assert(dof.get_fe(0).n_components() == 1,
-           ExcMessage(
-             "Finite element is not scalar as is necessary for this function"));
-
-    std::vector<Tensor<1, dim, typename VectorType::value_type>> gradient(1);
-    point_gradient(mapping, dof, fe_function, point, gradient);
-
-    return gradient[0];
-  }
-
-  namespace internal
-  {
-    template <typename VectorType>
-    typename std::enable_if<dealii::is_serial_vector<VectorType>::value ==
-                            true>::type
-    subtract_mean_value(VectorType &v, const std::vector<bool> &p_select)
-    {
-      if (p_select.size() == 0)
-        {
-          // In case of an empty boolean mask operate on the whole vector:
-          v.add(-v.mean_value());
-        }
-      else
-        {
-          const unsigned int n = v.size();
-
-          Assert(p_select.size() == n,
-                 ExcDimensionMismatch(p_select.size(), n));
-
-          typename VectorType::value_type s       = 0.;
-          unsigned int                    counter = 0;
-          for (unsigned int i = 0; i < n; ++i)
-            if (p_select[i])
-              {
-                typename VectorType::value_type vi = v(i);
-                s += vi;
-                ++counter;
-              }
-          // Error out if we have not constrained anything. Note that in this
-          // case the vector v is always nonempty.
-          Assert(n == 0 || counter > 0,
-                 ComponentMask::ExcNoComponentSelected());
-
-          s /= counter;
-
-          for (unsigned int i = 0; i < n; ++i)
-            if (p_select[i])
-              v(i) -= s;
-        }
-    }
-
-
-
-    template <typename VectorType>
-    typename std::enable_if<dealii::is_serial_vector<VectorType>::value ==
-                            false>::type
-    subtract_mean_value(VectorType &v, const std::vector<bool> &p_select)
-    {
-      (void)p_select;
-      Assert(p_select.size() == 0, ExcNotImplemented());
-      // In case of an empty boolean mask operate on the whole vector:
-      v.add(-v.mean_value());
-    }
-  } // namespace internal
-
-
-  template <typename VectorType>
-  void
-  subtract_mean_value(VectorType &v, const std::vector<bool> &p_select)
-  {
-    internal::subtract_mean_value(v, p_select);
-  }
-
-  namespace internal
-  {
-    template <typename Number>
-    void
-    set_possibly_complex_number(const double r, const double, Number &n)
-    {
-      n = r;
-    }
-
-
-
-    template <typename Type>
-    void
-    set_possibly_complex_number(const double        r,
-                                const double        i,
-                                std::complex<Type> &n)
-    {
-      n = std::complex<Type>(r, i);
-    }
-  } // namespace internal
-
-
-  template <int dim, typename VectorType, int spacedim>
-  typename VectorType::value_type
-  compute_mean_value(const Mapping<dim, spacedim> &   mapping,
-                     const DoFHandler<dim, spacedim> &dof,
-                     const Quadrature<dim> &          quadrature,
-                     const VectorType &               v,
-                     const unsigned int               component)
-  {
-    using Number = typename VectorType::value_type;
-    Assert(v.size() == dof.n_dofs(),
-           ExcDimensionMismatch(v.size(), dof.n_dofs()));
-    AssertIndexRange(component, dof.get_fe(0).n_components());
-
-    FEValues<dim, spacedim> fe(mapping,
-                               dof.get_fe(),
-                               quadrature,
-                               UpdateFlags(update_JxW_values | update_values));
-
-    std::vector<Vector<Number>> values(
-      quadrature.size(), Vector<Number>(dof.get_fe(0).n_components()));
-
-    Number                                            mean = Number();
-    typename numbers::NumberTraits<Number>::real_type area = 0.;
-    // Compute mean value
-    for (const auto &cell : dof.active_cell_iterators())
-      if (cell->is_locally_owned())
-        {
-          fe.reinit(cell);
-          fe.get_function_values(v, values);
-          for (unsigned int k = 0; k < quadrature.size(); ++k)
-            {
-              mean += fe.JxW(k) * values[k](component);
-              area += fe.JxW(k);
-            }
-        }
-
-#ifdef DEAL_II_WITH_MPI
-    // if this was a distributed DoFHandler, we need to do the reduction
-    // over the entire domain
-    if (const parallel::TriangulationBase<dim, spacedim> *p_triangulation =
-          dynamic_cast<const parallel::TriangulationBase<dim, spacedim> *>(
-            &dof.get_triangulation()))
-      {
-        // The type used to store the elements of the global vector may be a
-        // real or a complex number. Do the global reduction always with real
-        // and imaginary types so that we don't have to distinguish, and to this
-        // end just copy everything into a complex number and, later, back into
-        // the original data type.
-        std::complex<double> mean_double = mean;
-        double my_values[3] = {mean_double.real(), mean_double.imag(), area};
-        double global_values[3];
-
-        const int ierr = MPI_Allreduce(my_values,
-                                       global_values,
-                                       3,
-                                       MPI_DOUBLE,
-                                       MPI_SUM,
-                                       p_triangulation->get_communicator());
-        AssertThrowMPI(ierr);
-
-        internal::set_possibly_complex_number(global_values[0],
-                                              global_values[1],
-                                              mean);
-        area = global_values[2];
-      }
-#endif
-
-    return (mean / area);
-  }
-
-
-  template <int dim, typename VectorType, int spacedim>
-  typename VectorType::value_type
-  compute_mean_value(const DoFHandler<dim, spacedim> &dof,
-                     const Quadrature<dim> &          quadrature,
-                     const VectorType &               v,
-                     const unsigned int               component)
-  {
-    return compute_mean_value(
-      StaticMappingQ1<dim, spacedim>::mapping, dof, quadrature, v, component);
-  }
-
-
-  template <int dim,
-            int spacedim,
-            template <int, int> class DoFHandlerType,
-            typename VectorType>
-  void
-  get_position_vector(const DoFHandlerType<dim, spacedim> &dh,
-                      VectorType &                         vector,
-                      const ComponentMask &                mask)
-  {
-    AssertDimension(vector.size(), dh.n_dofs());
-    const FiniteElement<dim, spacedim> &fe = dh.get_fe();
-
-    // Construct default fe_mask;
-    const ComponentMask fe_mask(
-      mask.size() ? mask :
-                    ComponentMask(fe.get_nonzero_components(0).size(), true));
-
-    AssertDimension(fe_mask.size(), fe.get_nonzero_components(0).size());
-
-    std::vector<unsigned int> fe_to_real(fe_mask.size(),
-                                         numbers::invalid_unsigned_int);
-    unsigned int              size = 0;
-    for (unsigned int i = 0; i < fe_mask.size(); ++i)
-      {
-        if (fe_mask[i])
-          fe_to_real[i] = size++;
-      }
-    Assert(
-      size == spacedim,
-      ExcMessage(
-        "The Component Mask you provided is invalid. It has to select exactly spacedim entries."));
-
-
-    if (fe.has_support_points())
-      {
-        const Quadrature<dim> quad(fe.get_unit_support_points());
-
-        MappingQGeneric<dim, spacedim> map_q(fe.degree);
-        FEValues<dim, spacedim> fe_v(map_q, fe, quad, update_quadrature_points);
-        std::vector<types::global_dof_index> dofs(fe.dofs_per_cell);
-
-        AssertDimension(fe.dofs_per_cell, fe.get_unit_support_points().size());
-        Assert(fe.is_primitive(),
-               ExcMessage("FE is not Primitive! This won't work."));
-
-        for (const auto &cell : dh.active_cell_iterators())
-          if (cell->is_locally_owned())
-            {
-              fe_v.reinit(cell);
-              cell->get_dof_indices(dofs);
-              const std::vector<Point<spacedim>> &points =
-                fe_v.get_quadrature_points();
-              for (unsigned int q = 0; q < points.size(); ++q)
-                {
-                  const unsigned int comp =
-                    fe.system_to_component_index(q).first;
-                  if (fe_mask[comp])
-                    ::dealii::internal::ElementAccess<VectorType>::set(
-                      points[q][fe_to_real[comp]], dofs[q], vector);
-                }
-            }
-      }
-    else
-      {
-        // Construct a FiniteElement with FE_Q^spacedim, and call this
-        // function again.
-        //
-        // Once we have this, interpolate with the given finite element
-        // to get a Mapping which is interpolatory at the support points
-        // of FE_Q(fe.degree())
-        const FESystem<dim, spacedim> *fe_system =
-          dynamic_cast<const FESystem<dim, spacedim> *>(&fe);
-        Assert(fe_system, ExcNotImplemented());
-        unsigned int degree = numbers::invalid_unsigned_int;
-
-        // Get information about the blocks
-        for (unsigned int i = 0; i < fe_mask.size(); ++i)
-          if (fe_mask[i])
-            {
-              const unsigned int base_i =
-                fe_system->component_to_base_index(i).first;
-              Assert(degree == numbers::invalid_unsigned_int ||
-                       degree == fe_system->base_element(base_i).degree,
-                     ExcNotImplemented());
-              Assert(fe_system->base_element(base_i).is_primitive(),
-                     ExcNotImplemented());
-              degree = fe_system->base_element(base_i).degree;
-            }
-
-        // We create an intermediate FE_Q vector space, and then
-        // interpolate from that vector space to this one, by
-        // carefully selecting the right components.
-
-        FESystem<dim, spacedim> feq(FE_Q<dim, spacedim>(degree), spacedim);
-        DoFHandlerType<dim, spacedim> dhq(dh.get_triangulation());
-        dhq.distribute_dofs(feq);
-        Vector<double>      eulerq(dhq.n_dofs());
-        const ComponentMask maskq(spacedim, true);
-        get_position_vector(dhq, eulerq);
-
-        FullMatrix<double> transfer(fe.dofs_per_cell, feq.dofs_per_cell);
-        FullMatrix<double> local_transfer(feq.dofs_per_cell);
-        const std::vector<Point<dim>> &points = feq.get_unit_support_points();
-
-        // Here we construct the interpolation matrix from
-        // FE_Q^spacedim to the FiniteElement used by
-        // euler_dof_handler.
-        //
-        // In order to construct such interpolation matrix, we have to
-        // solve the following system:
-        //
-        // v_j phi_j(q_i) = w_k psi_k(q_i) = w_k delta_ki = w_i
-        //
-        // where psi_k are the basis functions for fe_q, and phi_i are
-        // the basis functions of the target space while q_i are the
-        // support points for the fe_q space. With this choice of
-        // interpolation points, on the matrices is the identity
-        // matrix, and we have to invert only one matrix. The
-        // resulting vector will be interpolatory at the support
-        // points of fe_q, even if the finite element does not have
-        // support points.
-        //
-        // Morally, we should invert the matrix T_ij = phi_i(q_j),
-        // however in general this matrix is not invertible, since
-        // there may be components which do not contribute to the
-        // displacement vector. Since we are not interested in those
-        // components, we construct a square matrix with the same
-        // number of components of the FE_Q system. The FE_Q system
-        // was constructed above in such a way that the polynomial
-        // degree of the FE_Q system and that of the given FE are the
-        // same on the cell, which should guarantee that, for the
-        // displacement components only, the interpolation matrix is
-        // invertible. We construct a mapping between indices first,
-        // and check that this is the case. If not, we bail out, not
-        // knowing what to do in this case.
-
-        std::vector<unsigned int> fe_to_feq(fe.dofs_per_cell,
-                                            numbers::invalid_unsigned_int);
-        unsigned int              index = 0;
-        for (unsigned int i = 0; i < fe.dofs_per_cell; ++i)
-          if (fe_mask[fe.system_to_component_index(i).first])
-            fe_to_feq[i] = index++;
-
-        // If index is not the same as feq.dofs_per_cell, we won't
-        // know how to invert the resulting matrix. Bail out.
-        Assert(index == feq.dofs_per_cell, ExcNotImplemented());
-
-        for (unsigned int j = 0; j < fe.dofs_per_cell; ++j)
-          {
-            const unsigned int comp_j = fe.system_to_component_index(j).first;
-            if (fe_mask[comp_j])
-              for (unsigned int i = 0; i < points.size(); ++i)
-                {
-                  if (fe_to_real[comp_j] ==
-                      feq.system_to_component_index(i).first)
-                    local_transfer(i, fe_to_feq[j]) =
-                      fe.shape_value(j, points[i]);
-                }
-          }
-
-        // Now we construct the rectangular interpolation matrix. This
-        // one is filled only with the information from the components
-        // of the displacement. The rest is set to zero.
-        local_transfer.invert(local_transfer);
-        for (unsigned int i = 0; i < fe.dofs_per_cell; ++i)
-          if (fe_to_feq[i] != numbers::invalid_unsigned_int)
-            for (unsigned int j = 0; j < feq.dofs_per_cell; ++j)
-              transfer(i, j) = local_transfer(fe_to_feq[i], j);
-
-        // The interpolation matrix is then passed to the
-        // VectorTools::interpolate() function to generate the correct
-        // interpolation.
-        interpolate(dhq, dh, transfer, eulerq, vector);
-      }
-  }
-} // namespace VectorTools
-
-DEAL_II_NAMESPACE_CLOSE
+#include <deal.II/numerics/vector_tools_boundary.templates.h>
+#include <deal.II/numerics/vector_tools_constraints.templates.h>
+#include <deal.II/numerics/vector_tools_integrate_difference.templates.h>
+#include <deal.II/numerics/vector_tools_interpolate.templates.h>
+#include <deal.II/numerics/vector_tools_mean_value.templates.h>
+#include <deal.II/numerics/vector_tools_point_gradient.templates.h>
+#include <deal.II/numerics/vector_tools_point_value.templates.h>
+#include <deal.II/numerics/vector_tools_project.templates.h>
+#include <deal.II/numerics/vector_tools_rhs.templates.h>
 
 #endif
diff --git a/include/deal.II/numerics/vector_tools_boundary.templates.h b/include/deal.II/numerics/vector_tools_boundary.templates.h
new file mode 100644 (file)
index 0000000..92352fc
--- /dev/null
@@ -0,0 +1,3762 @@
+// ---------------------------------------------------------------------
+//
+// Copyright (C) 1998 - 2017 by the deal.II authors
+//
+// This file is part of the deal.II library.
+//
+// The deal.II library is free software; you can use it, redistribute
+// it, and/or modify it under the terms of the GNU Lesser General
+// Public License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+// The full text of the license can be found in the file LICENSE.md at
+// the top level directory of deal.II.
+//
+// ---------------------------------------------------------------------
+
+
+#ifndef dealii_vector_tools_boundary_templates_h
+#define dealii_vector_tools_boundary_templates_h
+
+#include <deal.II/base/qprojector.h>
+
+#include <deal.II/dofs/dof_tools.h>
+
+#include <deal.II/fe/fe_nedelec.h>
+#include <deal.II/fe/fe_nedelec_sz.h>
+#include <deal.II/fe/fe_nothing.h>
+#include <deal.II/fe/fe_raviart_thomas.h>
+#include <deal.II/fe/fe_system.h>
+
+#include <deal.II/hp/fe_values.h>
+#include <deal.II/hp/q_collection.h>
+
+#include <deal.II/lac/dynamic_sparsity_pattern.h>
+#include <deal.II/lac/precondition.h>
+#include <deal.II/lac/solver_cg.h>
+#include <deal.II/lac/solver_control.h>
+#include <deal.II/lac/sparse_matrix.h>
+#include <deal.II/lac/sparsity_pattern.h>
+
+#include <deal.II/numerics/matrix_tools.h>
+#include <deal.II/numerics/vector_tools.h>
+
+
+DEAL_II_NAMESPACE_OPEN
+
+namespace VectorTools
+{
+  // ----------- interpolate_boundary_values for std::map --------------------
+
+  namespace internal
+  {
+    template <int dim,
+              int spacedim,
+              typename number,
+              template <int, int> class DoFHandlerType,
+              template <int, int> class M_or_MC>
+    static inline void
+    do_interpolate_boundary_values(
+      const M_or_MC<dim, spacedim> &       mapping,
+      const DoFHandlerType<dim, spacedim> &dof,
+      const std::map<types::boundary_id, const Function<spacedim, number> *>
+        &                                        function_map,
+      std::map<types::global_dof_index, number> &boundary_values,
+      const ComponentMask &                      component_mask)
+    {
+      Assert(
+        component_mask.represents_n_components(dof.get_fe(0).n_components()),
+        ExcMessage("The number of components in the mask has to be either "
+                   "zero or equal to the number of components in the finite "
+                   "element."));
+
+
+      // if for whatever reason we were passed an empty map, return
+      // immediately
+      if (function_map.size() == 0)
+        return;
+
+      Assert(function_map.find(numbers::internal_face_boundary_id) ==
+               function_map.end(),
+             ExcMessage("You cannot specify the special boundary indicator "
+                        "for interior faces in your function map."));
+
+      const unsigned int n_components = DoFTools::n_components(dof);
+      for (typename std::map<types::boundary_id,
+                             const Function<spacedim, number> *>::const_iterator
+             i = function_map.begin();
+           i != function_map.end();
+           ++i)
+        Assert(n_components == i->second->n_components,
+               ExcDimensionMismatch(n_components, i->second->n_components));
+
+
+      // interpolate boundary values in 1d. in higher dimensions, we
+      // use FEValues to figure out what to do on faces, but in 1d
+      // faces are points and it is far easier to simply work on
+      // individual vertices
+      if (dim == 1)
+        {
+          for (const auto &cell : dof.active_cell_iterators())
+            for (const unsigned int direction :
+                 GeometryInfo<dim>::face_indices())
+              if (cell->at_boundary(direction) &&
+                  (function_map.find(cell->face(direction)->boundary_id()) !=
+                   function_map.end()))
+                {
+                  const Function<spacedim, number> &boundary_function =
+                    *function_map.find(cell->face(direction)->boundary_id())
+                       ->second;
+
+                  // get the FE corresponding to this cell
+                  const FiniteElement<dim, spacedim> &fe = cell->get_fe();
+                  Assert(fe.n_components() == boundary_function.n_components,
+                         ExcDimensionMismatch(fe.n_components(),
+                                              boundary_function.n_components));
+
+                  Assert(component_mask.n_selected_components(
+                           fe.n_components()) > 0,
+                         ComponentMask::ExcNoComponentSelected());
+
+                  // now set the value of the vertex degree of
+                  // freedom. setting also creates the entry in the
+                  // map if it did not exist beforehand
+                  //
+                  // save some time by requesting values only once for
+                  // each point, irrespective of the number of
+                  // components of the function
+                  Vector<number> function_values(fe.n_components());
+                  if (fe.n_components() == 1)
+                    function_values(0) =
+                      boundary_function.value(cell->vertex(direction));
+                  else
+                    boundary_function.vector_value(cell->vertex(direction),
+                                                   function_values);
+
+                  for (unsigned int i = 0; i < fe.dofs_per_vertex; ++i)
+                    if (component_mask[fe.face_system_to_component_index(i)
+                                         .first])
+                      boundary_values[cell->vertex_dof_index(
+                        direction, i, cell->active_fe_index())] =
+                        function_values(
+                          fe.face_system_to_component_index(i).first);
+                }
+        }
+      else // dim > 1
+        {
+          const bool fe_is_system = (n_components != 1);
+
+          // field to store the indices
+          std::vector<types::global_dof_index> face_dofs;
+          face_dofs.reserve(DoFTools::max_dofs_per_face(dof));
+
+          // array to store the values of the boundary function at the boundary
+          // points. have two arrays for scalar and vector functions to use the
+          // more efficient one respectively
+          std::vector<number>         dof_values_scalar;
+          std::vector<Vector<number>> dof_values_system;
+          dof_values_scalar.reserve(DoFTools::max_dofs_per_face(dof));
+          dof_values_system.reserve(DoFTools::max_dofs_per_face(dof));
+
+          // before we start with the loop over all cells create an hp::FEValues
+          // object that holds the interpolation points of all finite elements
+          // that may ever be in use
+          const dealii::hp::FECollection<dim, spacedim> &finite_elements =
+            dof.get_fe_collection();
+          dealii::hp::QCollection<dim - 1> q_collection;
+          for (unsigned int f = 0; f < finite_elements.size(); ++f)
+            {
+              const FiniteElement<dim, spacedim> &fe = finite_elements[f];
+
+              // generate a quadrature rule on the face from the unit support
+              // points. this will be used to obtain the quadrature points on
+              // the real cell's face
+              //
+              // to do this, we check whether the FE has support points on the
+              // face at all:
+              if (fe.has_face_support_points())
+                q_collection.push_back(
+                  Quadrature<dim - 1>(fe.get_unit_face_support_points()));
+              else
+                {
+                  // if not, then we should try a more clever way. the idea is
+                  // that a finite element may not offer support points for all
+                  // its shape functions, but maybe only some. if it offers
+                  // support points for the components we are interested in in
+                  // this function, then that's fine. if not, the function we
+                  // call in the finite element will raise an exception. the
+                  // support points for the other shape functions are left
+                  // uninitialized (well, initialized by the default
+                  // constructor), since we don't need them anyway.
+                  //
+                  // As a detour, we must make sure we only query
+                  // face_system_to_component_index if the index corresponds to
+                  // a primitive shape function. since we know that all the
+                  // components we are interested in are primitive (by the above
+                  // check), we can safely put such a check in front
+                  std::vector<Point<dim - 1>> unit_support_points(
+                    fe.dofs_per_face);
+
+                  for (unsigned int i = 0; i < fe.dofs_per_face; ++i)
+                    if (fe.is_primitive(fe.face_to_cell_index(i, 0)))
+                      if (component_mask[fe.face_system_to_component_index(i)
+                                           .first] == true)
+                        unit_support_points[i] = fe.unit_face_support_point(i);
+
+                  q_collection.push_back(
+                    Quadrature<dim - 1>(unit_support_points));
+                }
+            }
+          // now that we have a q_collection object with all the right
+          // quadrature points, create an hp::FEFaceValues object that we can
+          // use to evaluate the boundary values at
+          const auto mapping_collection =
+            dealii::hp::MappingCollection<dim, spacedim>(mapping);
+          dealii::hp::FEFaceValues<dim, spacedim> x_fe_values(
+            mapping_collection,
+            finite_elements,
+            q_collection,
+            update_quadrature_points);
+
+          typename DoFHandlerType<dim, spacedim>::active_cell_iterator
+            cell = dof.begin_active(),
+            endc = dof.end();
+          for (; cell != endc; ++cell)
+            if (!cell->is_artificial())
+              for (const unsigned int face_no :
+                   GeometryInfo<dim>::face_indices())
+                {
+                  const FiniteElement<dim, spacedim> &fe = cell->get_fe();
+
+                  // we can presently deal only with primitive elements for
+                  // boundary values. this does not preclude us using
+                  // non-primitive elements in components that we aren't
+                  // interested in, however. make sure that all shape functions
+                  // that are non-zero for the components we are interested in,
+                  // are in fact primitive
+                  for (unsigned int i = 0; i < cell->get_fe().dofs_per_cell;
+                       ++i)
+                    {
+                      const ComponentMask &nonzero_component_array =
+                        cell->get_fe().get_nonzero_components(i);
+                      for (unsigned int c = 0; c < n_components; ++c)
+                        if ((nonzero_component_array[c] == true) &&
+                            (component_mask[c] == true))
+                          Assert(
+                            cell->get_fe().is_primitive(i),
+                            ExcMessage(
+                              "This function can only deal with requested boundary "
+                              "values that correspond to primitive (scalar) base "
+                              "elements. You may want to look up in the deal.II "
+                              "glossary what the term 'primitive' means."
+                              "\n\n"
+                              "There are alternative boundary value interpolation "
+                              "functions in namespace 'VectorTools' that you can "
+                              "use for non-primitive finite elements."));
+                    }
+
+                  const typename DoFHandlerType<dim, spacedim>::face_iterator
+                                           face = cell->face(face_no);
+                  const types::boundary_id boundary_component =
+                    face->boundary_id();
+
+                  // see if this face is part of the boundaries for which we are
+                  // supposed to do something, and also see if the finite
+                  // element in use here has DoFs on the face at all
+                  if ((function_map.find(boundary_component) !=
+                       function_map.end()) &&
+                      (cell->get_fe().dofs_per_face > 0))
+                    {
+                      // face is of the right component
+                      x_fe_values.reinit(cell, face_no);
+                      const dealii::FEFaceValues<dim, spacedim> &fe_values =
+                        x_fe_values.get_present_fe_values();
+
+                      // get indices, physical location and boundary values of
+                      // dofs on this face
+                      face_dofs.resize(fe.dofs_per_face);
+                      face->get_dof_indices(face_dofs, cell->active_fe_index());
+                      const std::vector<Point<spacedim>> &dof_locations =
+                        fe_values.get_quadrature_points();
+
+                      if (fe_is_system)
+                        {
+                          // resize array. avoid construction of a memory
+                          // allocating temporary if possible
+                          if (dof_values_system.size() < fe.dofs_per_face)
+                            dof_values_system.resize(fe.dofs_per_face,
+                                                     Vector<number>(
+                                                       fe.n_components()));
+                          else
+                            dof_values_system.resize(fe.dofs_per_face);
+
+                          function_map.find(boundary_component)
+                            ->second->vector_value_list(dof_locations,
+                                                        dof_values_system);
+
+                          // enter those dofs into the list that match the
+                          // component signature. avoid the usual complication
+                          // that we can't just use *_system_to_component_index
+                          // for non-primitive FEs
+                          for (unsigned int i = 0; i < face_dofs.size(); ++i)
+                            {
+                              unsigned int component;
+                              if (fe.is_primitive())
+                                component =
+                                  fe.face_system_to_component_index(i).first;
+                              else
+                                {
+                                  // non-primitive case. make sure that this
+                                  // particular shape function _is_ primitive,
+                                  // and get at it's component. use usual trick
+                                  // to transfer face dof index to cell dof
+                                  // index
+                                  const unsigned int cell_i =
+                                    (dim == 1 ?
+                                       i :
+                                       (dim == 2 ?
+                                          (i < 2 * fe.dofs_per_vertex ?
+                                             i :
+                                             i + 2 * fe.dofs_per_vertex) :
+                                          (dim == 3 ?
+                                             (i < 4 * fe.dofs_per_vertex ?
+                                                i :
+                                                (i < 4 * fe.dofs_per_vertex +
+                                                       4 * fe.dofs_per_line ?
+                                                   i + 4 * fe.dofs_per_vertex :
+                                                   i + 4 * fe.dofs_per_vertex +
+                                                     8 * fe.dofs_per_line)) :
+                                             numbers::invalid_unsigned_int)));
+                                  Assert(cell_i < fe.dofs_per_cell,
+                                         ExcInternalError());
+
+                                  // make sure that if this is not a primitive
+                                  // shape function, then all the corresponding
+                                  // components in the mask are not set
+                                  if (!fe.is_primitive(cell_i))
+                                    for (unsigned int c = 0; c < n_components;
+                                         ++c)
+                                      if (fe.get_nonzero_components(cell_i)[c])
+                                        Assert(component_mask[c] == false,
+                                               FETools::ExcFENotPrimitive());
+
+                                  // let's pick the first of possibly more than
+                                  // one non-zero components. if shape function
+                                  // is non-primitive, then we will ignore the
+                                  // result in the following anyway, otherwise
+                                  // there's only one non-zero component which
+                                  // we will use
+                                  component = fe.get_nonzero_components(cell_i)
+                                                .first_selected_component();
+                                }
+
+                              if (component_mask[component] == true)
+                                boundary_values[face_dofs[i]] =
+                                  dof_values_system[i](component);
+                            }
+                        }
+                      else
+                        // fe has only one component, so save some computations
+                        {
+                          // get only the one component that this function has
+                          dof_values_scalar.resize(fe.dofs_per_face);
+                          function_map.find(boundary_component)
+                            ->second->value_list(dof_locations,
+                                                 dof_values_scalar,
+                                                 0);
+
+                          // enter into list
+
+                          for (unsigned int i = 0; i < face_dofs.size(); ++i)
+                            boundary_values[face_dofs[i]] =
+                              dof_values_scalar[i];
+                        }
+                    }
+                }
+        }
+    } // end of interpolate_boundary_values
+  }   // namespace internal
+
+
+
+  template <int dim,
+            int spacedim,
+            template <int, int> class DoFHandlerType,
+            typename number>
+  void
+  interpolate_boundary_values(
+    const Mapping<dim, spacedim> &       mapping,
+    const DoFHandlerType<dim, spacedim> &dof,
+    const std::map<types::boundary_id, const Function<spacedim, number> *>
+      &                                        function_map,
+    std::map<types::global_dof_index, number> &boundary_values,
+    const ComponentMask &                      component_mask_)
+  {
+    internal::do_interpolate_boundary_values(
+      mapping, dof, function_map, boundary_values, component_mask_);
+  }
+
+
+
+  template <int dim,
+            int spacedim,
+            template <int, int> class DoFHandlerType,
+            typename number>
+  void
+  interpolate_boundary_values(
+    const Mapping<dim, spacedim> &             mapping,
+    const DoFHandlerType<dim, spacedim> &      dof,
+    const types::boundary_id                   boundary_component,
+    const Function<spacedim, number> &         boundary_function,
+    std::map<types::global_dof_index, number> &boundary_values,
+    const ComponentMask &                      component_mask)
+  {
+    std::map<types::boundary_id, const Function<spacedim, number> *>
+      function_map;
+    function_map[boundary_component] = &boundary_function;
+    interpolate_boundary_values(
+      mapping, dof, function_map, boundary_values, component_mask);
+  }
+
+
+  template <int dim, int spacedim, typename number>
+  void
+  interpolate_boundary_values(
+    const hp::MappingCollection<dim, spacedim> &mapping,
+    const hp::DoFHandler<dim, spacedim> &       dof,
+    const std::map<types::boundary_id, const Function<spacedim, number> *>
+      &                                        function_map,
+    std::map<types::global_dof_index, number> &boundary_values,
+    const ComponentMask &                      component_mask_)
+  {
+    internal::do_interpolate_boundary_values(
+      mapping, dof, function_map, boundary_values, component_mask_);
+  }
+
+
+
+  template <int dim,
+            int spacedim,
+            template <int, int> class DoFHandlerType,
+            typename number>
+  void
+  interpolate_boundary_values(
+    const DoFHandlerType<dim, spacedim> &      dof,
+    const types::boundary_id                   boundary_component,
+    const Function<spacedim, number> &         boundary_function,
+    std::map<types::global_dof_index, number> &boundary_values,
+    const ComponentMask &                      component_mask)
+  {
+    interpolate_boundary_values(StaticMappingQ1<dim, spacedim>::mapping,
+                                dof,
+                                boundary_component,
+                                boundary_function,
+                                boundary_values,
+                                component_mask);
+  }
+
+
+
+  template <int dim,
+            int spacedim,
+            template <int, int> class DoFHandlerType,
+            typename number>
+  void
+  interpolate_boundary_values(
+    const DoFHandlerType<dim, spacedim> &dof,
+    const std::map<types::boundary_id, const Function<spacedim, number> *>
+      &                                        function_map,
+    std::map<types::global_dof_index, number> &boundary_values,
+    const ComponentMask &                      component_mask)
+  {
+    interpolate_boundary_values(StaticMappingQ1<dim, spacedim>::mapping,
+                                dof,
+                                function_map,
+                                boundary_values,
+                                component_mask);
+  }
+
+
+
+  // ----------- interpolate_boundary_values for AffineConstraints
+  // --------------
+
+
+
+  template <int dim,
+            int spacedim,
+            template <int, int> class DoFHandlerType,
+            typename number>
+  void
+  interpolate_boundary_values(
+    const Mapping<dim, spacedim> &       mapping,
+    const DoFHandlerType<dim, spacedim> &dof,
+    const std::map<types::boundary_id, const Function<spacedim, number> *>
+      &                        function_map,
+    AffineConstraints<number> &constraints,
+    const ComponentMask &      component_mask_)
+  {
+    std::map<types::global_dof_index, number> boundary_values;
+    interpolate_boundary_values(
+      mapping, dof, function_map, boundary_values, component_mask_);
+    typename std::map<types::global_dof_index, number>::const_iterator
+      boundary_value = boundary_values.begin();
+    for (; boundary_value != boundary_values.end(); ++boundary_value)
+      {
+        if (constraints.can_store_line(boundary_value->first) &&
+            !constraints.is_constrained(boundary_value->first))
+          {
+            constraints.add_line(boundary_value->first);
+            constraints.set_inhomogeneity(boundary_value->first,
+                                          boundary_value->second);
+          }
+      }
+  }
+
+
+
+  template <int dim,
+            int spacedim,
+            template <int, int> class DoFHandlerType,
+            typename number>
+  void
+  interpolate_boundary_values(
+    const Mapping<dim, spacedim> &       mapping,
+    const DoFHandlerType<dim, spacedim> &dof,
+    const types::boundary_id             boundary_component,
+    const Function<spacedim, number> &   boundary_function,
+    AffineConstraints<number> &          constraints,
+    const ComponentMask &                component_mask)
+  {
+    std::map<types::boundary_id, const Function<spacedim, number> *>
+      function_map;
+    function_map[boundary_component] = &boundary_function;
+    interpolate_boundary_values(
+      mapping, dof, function_map, constraints, component_mask);
+  }
+
+
+
+  template <int dim,
+            int spacedim,
+            template <int, int> class DoFHandlerType,
+            typename number>
+  void
+  interpolate_boundary_values(
+    const DoFHandlerType<dim, spacedim> &dof,
+    const types::boundary_id             boundary_component,
+    const Function<spacedim, number> &   boundary_function,
+    AffineConstraints<number> &          constraints,
+    const ComponentMask &                component_mask)
+  {
+    interpolate_boundary_values(StaticMappingQ1<dim, spacedim>::mapping,
+                                dof,
+                                boundary_component,
+                                boundary_function,
+                                constraints,
+                                component_mask);
+  }
+
+
+
+  template <int dim,
+            int spacedim,
+            template <int, int> class DoFHandlerType,
+            typename number>
+  void
+  interpolate_boundary_values(
+    const DoFHandlerType<dim, spacedim> &dof,
+    const std::map<types::boundary_id, const Function<spacedim, number> *>
+      &                        function_map,
+    AffineConstraints<number> &constraints,
+    const ComponentMask &      component_mask)
+  {
+    interpolate_boundary_values(StaticMappingQ1<dim, spacedim>::mapping,
+                                dof,
+                                function_map,
+                                constraints,
+                                component_mask);
+  }
+
+
+
+  // -------- implementation for project_boundary_values with std::map --------
+
+
+  namespace internal
+  {
+    // keep the first argument non-reference since we use it
+    // with 1e-8 * number
+    template <typename number1, typename number2>
+    bool
+    real_part_bigger_than(const number1 a, const number2 &b)
+    {
+      return a > b;
+    }
+
+    template <typename number1, typename number2>
+    bool
+    real_part_bigger_than(const number1 a, const std::complex<number2> b)
+    {
+      Assert(std::abs(b.imag()) <= 1e-15 * std::abs(b), ExcInternalError());
+      return a > b.real();
+    }
+
+    template <typename number1, typename number2>
+    bool
+    real_part_bigger_than(const std::complex<number1> a, const number2 b)
+    {
+      Assert(std::abs(a.imag()) <= 1e-15 * std::abs(a), ExcInternalError());
+      return a.real() > b;
+    }
+
+    template <typename number1, typename number2>
+    bool
+    real_part_bigger_than(const std::complex<number1> a,
+                          const std::complex<number2> b)
+    {
+      Assert(std::abs(a.imag()) <= 1e-15 * std::abs(a), ExcInternalError());
+      Assert(std::abs(b.imag()) <= 1e-15 * std::abs(b), ExcInternalError());
+      return a.real() > b.real();
+    }
+
+    // this function is needed to get an idea where
+    // rhs.norm_sqr()  is too small for a given type.
+    template <typename number>
+    number
+    min_number(const number & /*dummy*/)
+    {
+      return std::numeric_limits<number>::min();
+    }
+
+    // Sine rhs.norm_sqr() is non-negative real, in complex case we
+    // take the numeric limits of the underlying type used in std::complex<>.
+    template <typename number>
+    number
+    min_number(const std::complex<number> & /*dummy*/)
+    {
+      return std::numeric_limits<number>::min();
+    }
+
+    template <int dim,
+              int spacedim,
+              template <int, int> class DoFHandlerType,
+              template <int, int> class M_or_MC,
+              template <int> class Q_or_QC,
+              typename number>
+    void
+    do_project_boundary_values(
+      const M_or_MC<dim, spacedim> &       mapping,
+      const DoFHandlerType<dim, spacedim> &dof,
+      const std::map<types::boundary_id, const Function<spacedim, number> *>
+        &                                        boundary_functions,
+      const Q_or_QC<dim - 1> &                   q,
+      std::map<types::global_dof_index, number> &boundary_values,
+      std::vector<unsigned int>                  component_mapping)
+    {
+      // in 1d, projection onto the 0d end points == interpolation
+      if (dim == 1)
+        {
+          Assert(component_mapping.size() == 0, ExcNotImplemented());
+          interpolate_boundary_values(
+            mapping, dof, boundary_functions, boundary_values, ComponentMask());
+          return;
+        }
+
+      // TODO:[?] In project_boundary_values, no condensation of sparsity
+      //    structures, matrices and right hand sides or distribution of
+      //    solution vectors is performed. This is ok for dim<3 because then
+      //    there are no constrained nodes on the boundary, but is not
+      //    acceptable for higher dimensions. Fix this.
+
+      if (component_mapping.size() == 0)
+        {
+          AssertDimension(dof.get_fe(0).n_components(),
+                          boundary_functions.begin()->second->n_components);
+          // I still do not see why i
+          // should create another copy
+          // here
+          component_mapping.resize(dof.get_fe(0).n_components());
+          for (unsigned int i = 0; i < component_mapping.size(); ++i)
+            component_mapping[i] = i;
+        }
+      else
+        AssertDimension(dof.get_fe(0).n_components(), component_mapping.size());
+
+      std::vector<types::global_dof_index> dof_to_boundary_mapping;
+      std::set<types::boundary_id>         selected_boundary_components;
+      for (typename std::map<types::boundary_id,
+                             const Function<spacedim, number> *>::const_iterator
+             i = boundary_functions.begin();
+           i != boundary_functions.end();
+           ++i)
+        selected_boundary_components.insert(i->first);
+
+      DoFTools::map_dof_to_boundary_indices(dof,
+                                            selected_boundary_components,
+                                            dof_to_boundary_mapping);
+
+      // Done if no degrees of freedom on the boundary
+      if (dof.n_boundary_dofs(boundary_functions) == 0)
+        return;
+
+      // set up sparsity structure
+      DynamicSparsityPattern dsp(dof.n_boundary_dofs(boundary_functions),
+                                 dof.n_boundary_dofs(boundary_functions));
+      DoFTools::make_boundary_sparsity_pattern(dof,
+                                               boundary_functions,
+                                               dof_to_boundary_mapping,
+                                               dsp);
+      SparsityPattern sparsity;
+      sparsity.copy_from(dsp);
+
+
+
+      // note: for three or more dimensions, there
+      // may be constrained nodes on the boundary
+      // in this case the boundary mass matrix has
+      // to be condensed and the solution is to
+      // be distributed afterwards, which is not
+      // yet implemented. The reason for this is
+      // that we cannot simply use the condense
+      // family of functions, since the matrices
+      // and vectors do not use the global
+      // numbering but rather the boundary
+      // numbering, i.e. the condense function
+      // needs to use another indirection. There
+      // should be not many technical problems,
+      // but it needs to be implemented
+      if (dim >= 3)
+        {
+#ifdef DEBUG
+          // Assert that there are no hanging nodes at the boundary
+          int level = -1;
+          for (const auto &cell : dof.active_cell_iterators())
+            for (auto f : GeometryInfo<dim>::face_indices())
+              {
+                if (cell->at_boundary(f))
+                  {
+                    if (level == -1)
+                      level = cell->level();
+                    else
+                      {
+                        Assert(
+                          level == cell->level(),
+                          ExcMessage(
+                            "The mesh you use in projecting boundary values "
+                            "has hanging nodes at the boundary. This would require "
+                            "dealing with hanging node constraints when solving "
+                            "the linear system on the boundary, but this is not "
+                            "currently implemented."));
+                      }
+                  }
+              }
+#endif
+        }
+      sparsity.compress();
+
+
+      // make mass matrix and right hand side
+      SparseMatrix<number> mass_matrix(sparsity);
+      Vector<number>       rhs(sparsity.n_rows());
+
+
+      MatrixCreator::create_boundary_mass_matrix(
+        mapping,
+        dof,
+        q,
+        mass_matrix,
+        boundary_functions,
+        rhs,
+        dof_to_boundary_mapping,
+        static_cast<const Function<spacedim, number> *>(nullptr),
+        component_mapping);
+
+      Vector<number> boundary_projection(rhs.size());
+
+      // cannot reduce residual in a useful way if we are close to the square
+      // root of the minimal double value
+      if (rhs.norm_sqr() < 1e28 * min_number(number()))
+        boundary_projection = 0;
+      else
+        {
+          // Allow for a maximum of 5*n steps to reduce the residual by 10^-12.
+          // n steps may not be sufficient, since roundoff errors may accumulate
+          // for badly conditioned matrices
+          ReductionControl control(5 * rhs.size(), 0., 1e-12, false, false);
+          GrowingVectorMemory<Vector<number>> memory;
+          SolverCG<Vector<number>>            cg(control, memory);
+
+          PreconditionSSOR<SparseMatrix<number>> prec;
+          prec.initialize(mass_matrix, 1.2);
+
+          cg.solve(mass_matrix, boundary_projection, rhs, prec);
+        }
+      // fill in boundary values
+      for (unsigned int i = 0; i < dof_to_boundary_mapping.size(); ++i)
+        if (dof_to_boundary_mapping[i] != numbers::invalid_dof_index)
+          {
+            AssertIsFinite(boundary_projection(dof_to_boundary_mapping[i]));
+
+            // this dof is on one of the
+            // interesting boundary parts
+            //
+            // remember: i is the global dof
+            // number, dof_to_boundary_mapping[i]
+            // is the number on the boundary and
+            // thus in the solution vector
+            boundary_values[i] =
+              boundary_projection(dof_to_boundary_mapping[i]);
+          }
+    }
+  } // namespace internal
+
+  template <int dim, int spacedim, typename number>
+  void
+  project_boundary_values(
+    const Mapping<dim, spacedim> &   mapping,
+    const DoFHandler<dim, spacedim> &dof,
+    const std::map<types::boundary_id, const Function<spacedim, number> *>
+      &                                        boundary_functions,
+    const Quadrature<dim - 1> &                q,
+    std::map<types::global_dof_index, number> &boundary_values,
+    std::vector<unsigned int>                  component_mapping)
+  {
+    internal::do_project_boundary_values(
+      mapping, dof, boundary_functions, q, boundary_values, component_mapping);
+  }
+
+
+
+  template <int dim, int spacedim, typename number>
+  void
+  project_boundary_values(
+    const DoFHandler<dim, spacedim> &dof,
+    const std::map<types::boundary_id, const Function<spacedim, number> *>
+      &                                        boundary_functions,
+    const Quadrature<dim - 1> &                q,
+    std::map<types::global_dof_index, number> &boundary_values,
+    std::vector<unsigned int>                  component_mapping)
+  {
+    project_boundary_values(StaticMappingQ1<dim, spacedim>::mapping,
+                            dof,
+                            boundary_functions,
+                            q,
+                            boundary_values,
+                            component_mapping);
+  }
+
+
+
+  template <int dim, int spacedim, typename number>
+  void
+  project_boundary_values(
+    const hp::MappingCollection<dim, spacedim> &mapping,
+    const hp::DoFHandler<dim, spacedim> &       dof,
+    const std::map<types::boundary_id, const Function<spacedim, number> *>
+      &                                        boundary_functions,
+    const hp::QCollection<dim - 1> &           q,
+    std::map<types::global_dof_index, number> &boundary_values,
+    std::vector<unsigned int>                  component_mapping)
+  {
+    internal::do_project_boundary_values(
+      mapping, dof, boundary_functions, q, boundary_values, component_mapping);
+  }
+
+
+
+  template <int dim, int spacedim, typename number>
+  void
+  project_boundary_values(
+    const hp::DoFHandler<dim, spacedim> &dof,
+    const std::map<types::boundary_id, const Function<spacedim, number> *>
+      &                                        boundary_function,
+    const hp::QCollection<dim - 1> &           q,
+    std::map<types::global_dof_index, number> &boundary_values,
+    std::vector<unsigned int>                  component_mapping)
+  {
+    project_boundary_values(
+      hp::StaticMappingQ1<dim, spacedim>::mapping_collection,
+      dof,
+      boundary_function,
+      q,
+      boundary_values,
+      component_mapping);
+  }
+
+
+  // ---- implementation for project_boundary_values with AffineConstraints ----
+
+
+
+  template <int dim, int spacedim, typename number>
+  void
+  project_boundary_values(
+    const Mapping<dim, spacedim> &   mapping,
+    const DoFHandler<dim, spacedim> &dof,
+    const std::map<types::boundary_id, const Function<spacedim, number> *>
+      &                        boundary_functions,
+    const Quadrature<dim - 1> &q,
+    AffineConstraints<number> &constraints,
+    std::vector<unsigned int>  component_mapping)
+  {
+    std::map<types::global_dof_index, number> boundary_values;
+    project_boundary_values(
+      mapping, dof, boundary_functions, q, boundary_values, component_mapping);
+    typename std::map<types::global_dof_index, number>::const_iterator
+      boundary_value = boundary_values.begin();
+    for (; boundary_value != boundary_values.end(); ++boundary_value)
+      {
+        if (!constraints.is_constrained(boundary_value->first))
+          {
+            constraints.add_line(boundary_value->first);
+            constraints.set_inhomogeneity(boundary_value->first,
+                                          boundary_value->second);
+          }
+      }
+  }
+
+
+
+  template <int dim, int spacedim, typename number>
+  void
+  project_boundary_values(
+    const DoFHandler<dim, spacedim> &dof,
+    const std::map<types::boundary_id, const Function<spacedim, number> *>
+      &                        boundary_functions,
+    const Quadrature<dim - 1> &q,
+    AffineConstraints<number> &constraints,
+    std::vector<unsigned int>  component_mapping)
+  {
+    project_boundary_values(StaticMappingQ1<dim, spacedim>::mapping,
+                            dof,
+                            boundary_functions,
+                            q,
+                            constraints,
+                            component_mapping);
+  }
+
+
+  namespace internals
+  {
+    // This function computes the
+    // projection of the boundary
+    // function on edges for 3D.
+    template <typename cell_iterator>
+    void
+    compute_edge_projection(const cell_iterator &cell,
+                            const unsigned int   face,
+                            const unsigned int   line,
+                            hp::FEValues<3> &    hp_fe_values,
+                            const Function<3> &  boundary_function,
+                            const unsigned int   first_vector_component,
+                            std::vector<double> &dof_values,
+                            std::vector<bool> &  dofs_processed)
+    {
+      const double tol =
+        0.5 * cell->face(face)->line(line)->diameter() / cell->get_fe().degree;
+      const unsigned int dim      = 3;
+      const unsigned int spacedim = 3;
+
+      hp_fe_values.reinit(
+        cell,
+        (cell->active_fe_index() * GeometryInfo<dim>::faces_per_cell + face) *
+            GeometryInfo<dim>::lines_per_face +
+          line);
+
+      // Initialize the required
+      // objects.
+      const FEValues<dim> &fe_values = hp_fe_values.get_present_fe_values();
+      const FiniteElement<dim> &                           fe = cell->get_fe();
+      const std::vector<DerivativeForm<1, dim, spacedim>> &jacobians =
+        fe_values.get_jacobians();
+      const std::vector<Point<dim>> &quadrature_points =
+        fe_values.get_quadrature_points();
+
+      std::vector<Tensor<1, dim>> tangentials(fe_values.n_quadrature_points);
+      std::vector<Vector<double>> values(fe_values.n_quadrature_points,
+                                         Vector<double>(fe.n_components()));
+
+      // Get boundary function values
+      // at quadrature points.
+      boundary_function.vector_value_list(quadrature_points, values);
+
+      const std::vector<Point<dim>> &reference_quadrature_points =
+        fe_values.get_quadrature().get_points();
+      std::pair<unsigned int, unsigned int> base_indices(0, 0);
+
+      if (dynamic_cast<const FESystem<dim> *>(&cell->get_fe()) != nullptr)
+        {
+          unsigned int fe_index     = 0;
+          unsigned int fe_index_old = 0;
+          unsigned int i            = 0;
+
+          for (; i < fe.n_base_elements(); ++i)
+            {
+              fe_index_old = fe_index;
+              fe_index +=
+                fe.element_multiplicity(i) * fe.base_element(i).n_components();
+
+              if (fe_index > first_vector_component)
+                break;
+            }
+
+          base_indices.first  = i;
+          base_indices.second = (first_vector_component - fe_index_old) /
+                                fe.base_element(i).n_components();
+        }
+
+      // coordinate directions of
+      // the edges of the face.
+      const unsigned int
+        edge_coordinate_direction[GeometryInfo<dim>::faces_per_cell]
+                                 [GeometryInfo<dim>::lines_per_face] = {
+                                   {2, 2, 1, 1},
+                                   {2, 2, 1, 1},
+                                   {0, 0, 2, 2},
+                                   {0, 0, 2, 2},
+                                   {1, 1, 0, 0},
+                                   {1, 1, 0, 0}};
+      const FEValuesExtractors::Vector vec(first_vector_component);
+
+      // The interpolation for the
+      // lowest order edge shape
+      // functions is just the mean
+      // value of the tangential
+      // components of the boundary
+      // function on the edge.
+      for (unsigned int q_point = 0; q_point < fe_values.n_quadrature_points;
+           ++q_point)
+        {
+          // Therefore compute the
+          // tangential of the edge at
+          // the quadrature point.
+          Point<dim> shifted_reference_point_1 =
+            reference_quadrature_points[q_point];
+          Point<dim> shifted_reference_point_2 =
+            reference_quadrature_points[q_point];
+
+          shifted_reference_point_1(edge_coordinate_direction[face][line]) +=
+            tol;
+          shifted_reference_point_2(edge_coordinate_direction[face][line]) -=
+            tol;
+          tangentials[q_point] =
+            (0.5 *
+             (fe_values.get_mapping().transform_unit_to_real_cell(
+                cell, shifted_reference_point_1) -
+              fe_values.get_mapping().transform_unit_to_real_cell(
+                cell, shifted_reference_point_2)) /
+             tol);
+          tangentials[q_point] /= tangentials[q_point].norm();
+
+          // Compute the degrees of
+          // freedom.
+          for (unsigned int i = 0; i < fe.dofs_per_face; ++i)
+            if (((dynamic_cast<const FESystem<dim> *>(&fe) != nullptr) &&
+                 (fe.system_to_base_index(fe.face_to_cell_index(i, face))
+                    .first == base_indices) &&
+                 (fe.base_element(base_indices.first)
+                    .face_to_cell_index(line * fe.degree, face) <=
+                  fe.system_to_base_index(fe.face_to_cell_index(i, face))
+                    .second) &&
+                 (fe.system_to_base_index(fe.face_to_cell_index(i, face))
+                    .second <=
+                  fe.base_element(base_indices.first)
+                    .face_to_cell_index((line + 1) * fe.degree - 1, face))) ||
+                ((dynamic_cast<const FE_Nedelec<dim> *>(&fe) != nullptr) &&
+                 (line * fe.degree <= i) && (i < (line + 1) * fe.degree)))
+              {
+                const double tangential_solution_component =
+                  (values[q_point](first_vector_component) *
+                     tangentials[q_point][0] +
+                   values[q_point](first_vector_component + 1) *
+                     tangentials[q_point][1] +
+                   values[q_point](first_vector_component + 2) *
+                     tangentials[q_point][2]);
+                dof_values[i] +=
+                  (fe_values.JxW(q_point) * tangential_solution_component *
+                   (fe_values[vec].value(fe.face_to_cell_index(i, face),
+                                         q_point) *
+                    tangentials[q_point]) /
+                   std::sqrt(
+                     jacobians[q_point][0]
+                              [edge_coordinate_direction[face][line]] *
+                       jacobians[q_point][0]
+                                [edge_coordinate_direction[face][line]] +
+                     jacobians[q_point][1]
+                              [edge_coordinate_direction[face][line]] *
+                       jacobians[q_point][1]
+                                [edge_coordinate_direction[face][line]] +
+                     jacobians[q_point][2]
+                              [edge_coordinate_direction[face][line]] *
+                       jacobians[q_point][2]
+                                [edge_coordinate_direction[face][line]]));
+
+                if (q_point == 0)
+                  dofs_processed[i] = true;
+              }
+        }
+    }
+
+    // dummy implementation of above
+    // function for all other
+    // dimensions
+    template <int dim, typename cell_iterator>
+    void
+    compute_edge_projection(const cell_iterator &,
+                            const unsigned int,
+                            const unsigned int,
+                            hp::FEValues<dim> &,
+                            const Function<dim> &,
+                            const unsigned int,
+                            std::vector<double> &,
+                            std::vector<bool> &)
+    {
+      Assert(false, ExcInternalError());
+    }
+
+    // This function computes the
+    // projection of the boundary
+    // function on the interior of
+    // faces.
+    template <int dim, typename cell_iterator, typename number>
+    void
+    compute_face_projection_curl_conforming(
+      const cell_iterator &        cell,
+      const unsigned int           face,
+      hp::FEValues<dim> &          hp_fe_values,
+      const Function<dim, number> &boundary_function,
+      const unsigned int           first_vector_component,
+      std::vector<double> &        dof_values,
+      std::vector<bool> &          dofs_processed)
+    {
+      const unsigned int spacedim = dim;
+      hp_fe_values.reinit(cell,
+                          cell->active_fe_index() *
+                              GeometryInfo<dim>::faces_per_cell +
+                            face);
+      // Initialize the required
+      // objects.
+      const FEValues<dim> &fe_values = hp_fe_values.get_present_fe_values();
+      const FiniteElement<dim> &                           fe = cell->get_fe();
+      const std::vector<DerivativeForm<1, dim, spacedim>> &jacobians =
+        fe_values.get_jacobians();
+      const std::vector<Point<dim>> &quadrature_points =
+        fe_values.get_quadrature_points();
+      const unsigned int                    degree = fe.degree - 1;
+      std::pair<unsigned int, unsigned int> base_indices(0, 0);
+
+      if (dynamic_cast<const FESystem<dim> *>(&cell->get_fe()) != nullptr)
+        {
+          unsigned int fe_index     = 0;
+          unsigned int fe_index_old = 0;
+          unsigned int i            = 0;
+
+          for (; i < fe.n_base_elements(); ++i)
+            {
+              fe_index_old = fe_index;
+              fe_index +=
+                fe.element_multiplicity(i) * fe.base_element(i).n_components();
+
+              if (fe_index > first_vector_component)
+                break;
+            }
+
+          base_indices.first  = i;
+          base_indices.second = (first_vector_component - fe_index_old) /
+                                fe.base_element(i).n_components();
+        }
+
+      std::vector<Vector<double>> values(fe_values.n_quadrature_points,
+                                         Vector<double>(fe.n_components()));
+
+      // Get boundary function
+      // values at quadrature
+      // points.
+      boundary_function.vector_value_list(quadrature_points, values);
+
+      switch (dim)
+        {
+          case 2:
+            {
+              const double tol =
+                0.5 * cell->face(face)->diameter() / cell->get_fe().degree;
+              std::vector<Tensor<1, dim>> tangentials(
+                fe_values.n_quadrature_points);
+
+              const std::vector<Point<dim>> &reference_quadrature_points =
+                fe_values.get_quadrature().get_points();
+
+              // coordinate directions
+              // of the face.
+              const unsigned int
+                face_coordinate_direction[GeometryInfo<dim>::faces_per_cell] = {
+                  1, 1, 0, 0};
+              const FEValuesExtractors::Vector vec(first_vector_component);
+
+              // The interpolation for
+              // the lowest order face
+              // shape functions is just
+              // the mean value of the
+              // tangential  components
+              // of the boundary function
+              // on the edge.
+              for (unsigned int q_point = 0;
+                   q_point < fe_values.n_quadrature_points;
+                   ++q_point)
+                {
+                  // Therefore compute the
+                  // tangential of the
+                  // face at the quadrature
+                  // point.
+                  Point<dim> shifted_reference_point_1 =
+                    reference_quadrature_points[q_point];
+                  Point<dim> shifted_reference_point_2 =
+                    reference_quadrature_points[q_point];
+
+                  shifted_reference_point_1(face_coordinate_direction[face]) +=
+                    tol;
+                  shifted_reference_point_2(face_coordinate_direction[face]) -=
+                    tol;
+                  tangentials[q_point] =
+                    (fe_values.get_mapping().transform_unit_to_real_cell(
+                       cell, shifted_reference_point_1) -
+                     fe_values.get_mapping().transform_unit_to_real_cell(
+                       cell, shifted_reference_point_2)) /
+                    tol;
+                  tangentials[q_point] /= tangentials[q_point].norm();
+
+                  // Compute the degrees
+                  // of freedom.
+                  for (unsigned int i = 0; i < fe.dofs_per_face; ++i)
+                    if (((dynamic_cast<const FESystem<dim> *>(&fe) !=
+                          nullptr) &&
+                         (fe.system_to_base_index(
+                              fe.face_to_cell_index(i, face))
+                            .first == base_indices)) ||
+                        (dynamic_cast<const FE_Nedelec<dim> *>(&fe) != nullptr))
+                      {
+                        dof_values[i] +=
+                          fe_values.JxW(q_point) *
+                          (values[q_point](first_vector_component) *
+                             tangentials[q_point][0] +
+                           values[q_point](first_vector_component + 1) *
+                             tangentials[q_point][1]) *
+                          (fe_values[vec].value(fe.face_to_cell_index(i, face),
+                                                q_point) *
+                           tangentials[q_point]);
+
+                        if (q_point == 0)
+                          dofs_processed[i] = true;
+                      }
+                }
+
+              break;
+            }
+
+          case 3:
+            {
+              const FEValuesExtractors::Vector vec(first_vector_component);
+              FullMatrix<double>               assembling_matrix(
+                degree * fe.degree, dim * fe_values.n_quadrature_points);
+              Vector<double>     assembling_vector(assembling_matrix.n());
+              Vector<double>     cell_rhs(assembling_matrix.m());
+              FullMatrix<double> cell_matrix(assembling_matrix.m(),
+                                             assembling_matrix.m());
+              FullMatrix<double> cell_matrix_inv(assembling_matrix.m(),
+                                                 assembling_matrix.m());
+              Vector<double>     solution(cell_matrix.m());
+
+              // Get coordinate directions
+              // of the face.
+              const unsigned int global_face_coordinate_directions
+                [GeometryInfo<3>::faces_per_cell][2] = {
+                  {1, 2}, {1, 2}, {2, 0}, {2, 0}, {0, 1}, {0, 1}};
+
+              // The projection is divided into two steps.  In the first step we
+              // project the boundary function on the horizontal shape
+              // functions. Then the boundary function is projected on the
+              // vertical shape functions.  We begin with the horizontal shape
+              // functions and set up a linear system of equations to get the
+              // values for degrees of freedom associated with the interior of
+              // the face.
+              for (unsigned int q_point = 0;
+                   q_point < fe_values.n_quadrature_points;
+                   ++q_point)
+                {
+                  // The right hand
+                  // side of the
+                  // corresponding problem
+                  // is the residual
+                  // of the boundary
+                  // function and
+                  // the already
+                  // interpolated part
+                  // on the edges.
+                  Tensor<1, dim> tmp;
+
+                  for (unsigned int d = 0; d < dim; ++d)
+                    tmp[d] = values[q_point](first_vector_component + d);
+
+                  for (unsigned int i = 0; i < fe.dofs_per_face; ++i)
+                    if (((dynamic_cast<const FESystem<dim> *>(&fe) !=
+                          nullptr) &&
+                         (fe.system_to_base_index(
+                              fe.face_to_cell_index(i, face))
+                            .first == base_indices) &&
+                         (fe.base_element(base_indices.first)
+                            .face_to_cell_index(2 * fe.degree, face) <=
+                          fe.system_to_base_index(
+                              fe.face_to_cell_index(i, face))
+                            .second) &&
+                         (fe.system_to_base_index(
+                              fe.face_to_cell_index(i, face))
+                            .second <=
+                          fe.base_element(base_indices.first)
+                            .face_to_cell_index(4 * fe.degree - 1, face))) ||
+                        ((dynamic_cast<const FE_Nedelec<dim> *>(&fe) !=
+                          nullptr) &&
+                         (2 * fe.degree <= i) && (i < 4 * fe.degree)))
+                      tmp -=
+                        dof_values[i] *
+                        fe_values[vec].value(fe.face_to_cell_index(i, face),
+                                             q_point);
+
+                  const double JxW = std::sqrt(
+                    fe_values.JxW(q_point) /
+                    ((jacobians[q_point][0]
+                               [global_face_coordinate_directions[face][0]] *
+                        jacobians[q_point][0]
+                                 [global_face_coordinate_directions[face][0]] +
+                      jacobians[q_point][1]
+                               [global_face_coordinate_directions[face][0]] *
+                        jacobians[q_point][1]
+                                 [global_face_coordinate_directions[face][0]] +
+                      jacobians[q_point][2]
+                               [global_face_coordinate_directions[face][0]] *
+                        jacobians[q_point][2]
+                                 [global_face_coordinate_directions[face][0]]) *
+                     (jacobians[q_point][0]
+                               [global_face_coordinate_directions[face][1]] *
+                        jacobians[q_point][0]
+                                 [global_face_coordinate_directions[face][1]] +
+                      jacobians[q_point][1]
+                               [global_face_coordinate_directions[face][1]] *
+                        jacobians[q_point][1]
+                                 [global_face_coordinate_directions[face][1]] +
+                      jacobians[q_point][2]
+                               [global_face_coordinate_directions[face][1]] *
+                        jacobians[q_point][2]
+                                 [global_face_coordinate_directions[face]
+                                                                   [1]])));
+
+                  // In the weak form
+                  // the right hand
+                  // side function
+                  // is multiplicated
+                  // by the horizontal
+                  // shape functions
+                  // defined in the
+                  // interior of
+                  // the face.
+                  for (unsigned int d = 0; d < dim; ++d)
+                    assembling_vector(dim * q_point + d) = JxW * tmp[d];
+
+                  unsigned int index = 0;
+
+                  for (unsigned int i = 0; i < fe.dofs_per_face; ++i)
+                    if (((dynamic_cast<const FESystem<dim> *>(&fe) !=
+                          nullptr) &&
+                         (fe.system_to_base_index(
+                              fe.face_to_cell_index(i, face))
+                            .first == base_indices) &&
+                         (fe.base_element(base_indices.first)
+                            .face_to_cell_index(
+                              GeometryInfo<dim>::lines_per_face * fe.degree,
+                              face) <=
+                          fe.system_to_base_index(
+                              fe.face_to_cell_index(i, face))
+                            .second) &&
+                         (fe.system_to_base_index(
+                              fe.face_to_cell_index(i, face))
+                            .second <
+                          fe.base_element(base_indices.first)
+                            .face_to_cell_index(
+                              (degree + GeometryInfo<dim>::lines_per_face) *
+                                fe.degree,
+                              face))) ||
+                        ((dynamic_cast<const FE_Nedelec<dim> *>(&fe) !=
+                          nullptr) &&
+                         (GeometryInfo<dim>::lines_per_face * fe.degree <= i) &&
+                         (i < (degree + GeometryInfo<dim>::lines_per_face) *
+                                fe.degree)))
+                      {
+                        const Tensor<1, dim> shape_value =
+                          (JxW *
+                           fe_values[vec].value(fe.face_to_cell_index(i, face),
+                                                q_point));
+
+                        for (unsigned int d = 0; d < dim; ++d)
+                          assembling_matrix(index, dim * q_point + d) =
+                            shape_value[d];
+
+                        ++index;
+                      }
+                }
+
+              // Create the system matrix by multiplying the assembling matrix
+              // with its transposed and the right hand side vector by
+              // multiplying the assembling matrix with the assembling vector.
+              // Invert the system matrix.
+              assembling_matrix.mTmult(cell_matrix, assembling_matrix);
+              cell_matrix_inv.invert(cell_matrix);
+              assembling_matrix.vmult(cell_rhs, assembling_vector);
+              cell_matrix_inv.vmult(solution, cell_rhs);
+
+              // Store the computed
+              // values.
+              {
+                unsigned int index = 0;
+
+                for (unsigned int i = 0; i < fe.dofs_per_face; ++i)
+                  if (((dynamic_cast<const FESystem<dim> *>(&fe) != nullptr) &&
+                       (fe.system_to_base_index(fe.face_to_cell_index(i, face))
+                          .first == base_indices) &&
+                       (fe.base_element(base_indices.first)
+                          .face_to_cell_index(
+                            GeometryInfo<dim>::lines_per_face * fe.degree,
+                            face) <=
+                        fe.system_to_base_index(fe.face_to_cell_index(i, face))
+                          .second) &&
+                       (fe.system_to_base_index(fe.face_to_cell_index(i, face))
+                          .second <
+                        fe.base_element(base_indices.first)
+                          .face_to_cell_index(
+                            (degree + GeometryInfo<dim>::lines_per_face) *
+                              fe.degree,
+                            face))) ||
+                      ((dynamic_cast<const FE_Nedelec<dim> *>(&fe) !=
+                        nullptr) &&
+                       (GeometryInfo<dim>::lines_per_face * fe.degree <= i) &&
+                       (i < (degree + GeometryInfo<dim>::lines_per_face) *
+                              fe.degree)))
+                    {
+                      dof_values[i]     = solution(index);
+                      dofs_processed[i] = true;
+                      ++index;
+                    }
+              }
+
+              // Now we do the same as above with the vertical shape functions
+              // instead of the horizontal ones.
+              for (unsigned int q_point = 0;
+                   q_point < fe_values.n_quadrature_points;
+                   ++q_point)
+                {
+                  Tensor<1, dim> tmp;
+
+                  for (unsigned int d = 0; d < dim; ++d)
+                    tmp[d] = values[q_point](first_vector_component + d);
+
+                  for (unsigned int i = 0; i < fe.dofs_per_face; ++i)
+                    if (((dynamic_cast<const FESystem<dim> *>(&fe) !=
+                          nullptr) &&
+                         (fe.system_to_base_index(
+                              fe.face_to_cell_index(i, face))
+                            .first == base_indices) &&
+                         (fe.system_to_base_index(
+                              fe.face_to_cell_index(i, face))
+                            .second <=
+                          fe.base_element(base_indices.first)
+                            .face_to_cell_index(2 * fe.degree - 1, face)) &&
+                         (fe.system_to_base_index(
+                              fe.face_to_cell_index(i, face))
+                            .second >= fe.base_element(base_indices.first)
+                                         .face_to_cell_index(0, face))) ||
+                        ((dynamic_cast<const FE_Nedelec<dim> *>(&fe) !=
+                          nullptr) &&
+                         (i < 2 * fe.degree)))
+                      tmp -=
+                        dof_values[i] *
+                        fe_values[vec].value(fe.face_to_cell_index(i, face),
+                                             q_point);
+
+                  const double JxW = std::sqrt(
+                    fe_values.JxW(q_point) /
+                    ((jacobians[q_point][0]
+                               [global_face_coordinate_directions[face][0]] *
+                        jacobians[q_point][0]
+                                 [global_face_coordinate_directions[face][0]] +
+                      jacobians[q_point][1]
+                               [global_face_coordinate_directions[face][0]] *
+                        jacobians[q_point][1]
+                                 [global_face_coordinate_directions[face][0]] +
+                      jacobians[q_point][2]
+                               [global_face_coordinate_directions[face][0]] *
+                        jacobians[q_point][2]
+                                 [global_face_coordinate_directions[face][0]]) *
+                     (jacobians[q_point][0]
+                               [global_face_coordinate_directions[face][1]] *
+                        jacobians[q_point][0]
+                                 [global_face_coordinate_directions[face][1]] +
+                      jacobians[q_point][1]
+                               [global_face_coordinate_directions[face][1]] *
+                        jacobians[q_point][1]
+                                 [global_face_coordinate_directions[face][1]] +
+                      jacobians[q_point][2]
+                               [global_face_coordinate_directions[face][1]] *
+                        jacobians[q_point][2]
+                                 [global_face_coordinate_directions[face]
+                                                                   [1]])));
+
+                  for (unsigned int d = 0; d < dim; ++d)
+                    assembling_vector(dim * q_point + d) = JxW * tmp[d];
+
+                  unsigned int index = 0;
+
+                  for (unsigned int i = 0; i < fe.dofs_per_face; ++i)
+                    if (((dynamic_cast<const FESystem<dim> *>(&fe) !=
+                          nullptr) &&
+                         (fe.system_to_base_index(
+                              fe.face_to_cell_index(i, face))
+                            .first == base_indices) &&
+                         (fe.base_element(base_indices.first)
+                            .face_to_cell_index(
+                              (degree + GeometryInfo<dim>::lines_per_face) *
+                                fe.degree,
+                              face) <=
+                          fe.system_to_base_index(
+                              fe.face_to_cell_index(i, face))
+                            .second)) ||
+                        ((dynamic_cast<const FE_Nedelec<dim> *>(&fe) !=
+                          nullptr) &&
+                         ((degree + GeometryInfo<dim>::lines_per_face) *
+                            fe.degree <=
+                          i)))
+                      {
+                        const Tensor<1, dim> shape_value =
+                          JxW *
+                          fe_values[vec].value(fe.face_to_cell_index(i, face),
+                                               q_point);
+
+                        for (unsigned int d = 0; d < dim; ++d)
+                          assembling_matrix(index, dim * q_point + d) =
+                            shape_value[d];
+
+                        ++index;
+                      }
+                }
+
+              assembling_matrix.mTmult(cell_matrix, assembling_matrix);
+              cell_matrix_inv.invert(cell_matrix);
+              assembling_matrix.vmult(cell_rhs, assembling_vector);
+              cell_matrix_inv.vmult(solution, cell_rhs);
+
+              unsigned int index = 0;
+
+              for (unsigned int i = 0; i < fe.dofs_per_face; ++i)
+                if (((dynamic_cast<const FESystem<dim> *>(&fe) != nullptr) &&
+                     (fe.system_to_base_index(fe.face_to_cell_index(i, face))
+                        .first == base_indices) &&
+                     (fe.base_element(base_indices.first)
+                        .face_to_cell_index(
+                          (degree + GeometryInfo<dim>::lines_per_face) *
+                            fe.degree,
+                          face) <=
+                      fe.system_to_base_index(fe.face_to_cell_index(i, face))
+                        .second)) ||
+                    ((dynamic_cast<const FE_Nedelec<dim> *>(&fe) != nullptr) &&
+                     ((degree + GeometryInfo<dim>::lines_per_face) *
+                        fe.degree <=
+                      i)))
+                  {
+                    dof_values[i]     = solution(index);
+                    dofs_processed[i] = true;
+                    ++index;
+                  }
+
+              break;
+            }
+
+          default:
+            Assert(false, ExcNotImplemented());
+        }
+    }
+  } // namespace internals
+
+
+
+  template <int dim>
+  void
+  project_boundary_values_curl_conforming(
+    const DoFHandler<dim> &    dof_handler,
+    const unsigned int         first_vector_component,
+    const Function<dim> &      boundary_function,
+    const types::boundary_id   boundary_component,
+    AffineConstraints<double> &constraints,
+    const Mapping<dim> &       mapping)
+  {
+    // Projection-based interpolation is performed in two (in 2D) respectively
+    // three (in 3D) steps. First the tangential component of the function is
+    // interpolated on each edge.  This gives the values for the degrees of
+    // freedom corresponding to the edge shape functions. Now we are done for
+    // 2D, but in 3D we possibly have also degrees of freedom, which are
+    // located in the interior of the faces. Therefore we compute the residual
+    // of the function describing the boundary values and the interpolated
+    // part, which we have computed in the last step. On the faces there are
+    // two kinds of shape functions, the horizontal and the vertical
+    // ones. Thus we have to solve two linear systems of equations of size
+    // <tt>degree * (degree + 1)<tt> to obtain the values for the
+    // corresponding degrees of freedom.
+    const unsigned int    superdegree = dof_handler.get_fe().degree;
+    const QGauss<dim - 1> reference_face_quadrature(2 * superdegree);
+    const unsigned int    dofs_per_face = dof_handler.get_fe().dofs_per_face;
+    const hp::FECollection<dim> &fe_collection(dof_handler.get_fe_collection());
+    const hp::MappingCollection<dim> mapping_collection(mapping);
+    hp::QCollection<dim>             face_quadrature_collection;
+
+    for (unsigned int face : GeometryInfo<dim>::face_indices())
+      face_quadrature_collection.push_back(
+        QProjector<dim>::project_to_face(reference_face_quadrature, face));
+
+    hp::FEValues<dim> fe_face_values(mapping_collection,
+                                     fe_collection,
+                                     face_quadrature_collection,
+                                     update_jacobians | update_JxW_values |
+                                       update_quadrature_points |
+                                       update_values);
+
+    std::vector<bool>                    dofs_processed(dofs_per_face);
+    std::vector<double>                  dof_values(dofs_per_face);
+    std::vector<types::global_dof_index> face_dof_indices(dofs_per_face);
+    typename DoFHandler<dim>::active_cell_iterator cell =
+      dof_handler.begin_active();
+
+    switch (dim)
+      {
+        case 2:
+          {
+            for (; cell != dof_handler.end(); ++cell)
+              if (cell->at_boundary() && cell->is_locally_owned())
+                for (const unsigned int face :
+                     GeometryInfo<dim>::face_indices())
+                  if (cell->face(face)->boundary_id() == boundary_component)
+                    {
+                      // if the FE is a
+                      // FE_Nothing object
+                      // there is no work to
+                      // do
+                      if (dynamic_cast<const FE_Nothing<dim> *>(
+                            &cell->get_fe()) != nullptr)
+                        return;
+
+                      // This is only
+                      // implemented, if the
+                      // FE is a Nedelec
+                      // element. If the FE
+                      // is a FESystem, we
+                      // cannot check this.
+                      if (dynamic_cast<const FESystem<dim> *>(
+                            &cell->get_fe()) == nullptr)
+                        {
+                          AssertThrow(
+                            dynamic_cast<const FE_Nedelec<dim> *>(
+                              &cell->get_fe()) != nullptr,
+                            (typename FiniteElement<
+                              dim>::ExcInterpolationNotImplemented()));
+                        }
+
+                      for (unsigned int dof = 0; dof < dofs_per_face; ++dof)
+                        {
+                          dof_values[dof]     = 0.0;
+                          dofs_processed[dof] = false;
+                        }
+
+                      // Compute the
+                      // projection of the
+                      // boundary function on
+                      // the edge.
+                      internals::compute_face_projection_curl_conforming(
+                        cell,
+                        face,
+                        fe_face_values,
+                        boundary_function,
+                        first_vector_component,
+                        dof_values,
+                        dofs_processed);
+                      cell->face(face)->get_dof_indices(
+                        face_dof_indices, cell->active_fe_index());
+
+                      // Add the computed constraints to the constraints
+                      // object, if the degree of freedom is not already
+                      // constrained.
+                      for (unsigned int dof = 0; dof < dofs_per_face; ++dof)
+                        if (dofs_processed[dof] &&
+                            constraints.can_store_line(face_dof_indices[dof]) &&
+                            !(constraints.is_constrained(
+                              face_dof_indices[dof])))
+                          {
+                            constraints.add_line(face_dof_indices[dof]);
+
+                            if (std::abs(dof_values[dof]) > 1e-13)
+                              constraints.set_inhomogeneity(
+                                face_dof_indices[dof], dof_values[dof]);
+                          }
+                    }
+
+            break;
+          }
+
+        case 3:
+          {
+            const QGauss<dim - 2> reference_edge_quadrature(2 * superdegree);
+            const unsigned int    degree = superdegree - 1;
+            hp::QCollection<dim>  edge_quadrature_collection;
+
+            for (const unsigned int face : GeometryInfo<dim>::face_indices())
+              for (unsigned int line = 0;
+                   line < GeometryInfo<dim>::lines_per_face;
+                   ++line)
+                edge_quadrature_collection.push_back(
+                  QProjector<dim>::project_to_face(
+                    QProjector<dim - 1>::project_to_face(
+                      reference_edge_quadrature, line),
+                    face));
+
+            hp::FEValues<dim> fe_edge_values(mapping_collection,
+                                             fe_collection,
+                                             edge_quadrature_collection,
+                                             update_jacobians |
+                                               update_JxW_values |
+                                               update_quadrature_points |
+                                               update_values);
+
+            for (; cell != dof_handler.end(); ++cell)
+              if (cell->at_boundary() && cell->is_locally_owned())
+                for (const unsigned int face :
+                     GeometryInfo<dim>::face_indices())
+                  if (cell->face(face)->boundary_id() == boundary_component)
+                    {
+                      // if the FE is a
+                      // FE_Nothing object
+                      // there is no work to
+                      // do
+                      if (dynamic_cast<const FE_Nothing<dim> *>(
+                            &cell->get_fe()) != nullptr)
+                        return;
+
+                      // This is only
+                      // implemented, if the
+                      // FE is a Nedelec
+                      // element. If the FE is
+                      // a FESystem we cannot
+                      // check this.
+                      if (dynamic_cast<const FESystem<dim> *>(
+                            &cell->get_fe()) == nullptr)
+                        {
+                          AssertThrow(dynamic_cast<const FE_Nedelec<dim> *>(
+                                        &cell->get_fe()) != nullptr,
+                                      typename FiniteElement<
+                                        dim>::ExcInterpolationNotImplemented());
+                        }
+
+                      for (unsigned int dof = 0; dof < dofs_per_face; ++dof)
+                        {
+                          dof_values[dof]     = 0.0;
+                          dofs_processed[dof] = false;
+                        }
+
+                      // First we compute the
+                      // projection on the
+                      // edges.
+                      for (unsigned int line = 0;
+                           line < GeometryInfo<3>::lines_per_face;
+                           ++line)
+                        internals::compute_edge_projection(
+                          cell,
+                          face,
+                          line,
+                          fe_edge_values,
+                          boundary_function,
+                          first_vector_component,
+                          dof_values,
+                          dofs_processed);
+
+                      // If there are higher
+                      // order shape
+                      // functions, there is
+                      // still some work
+                      // left.
+                      if (degree > 0)
+                        internals::compute_face_projection_curl_conforming(
+                          cell,
+                          face,
+                          fe_face_values,
+                          boundary_function,
+                          first_vector_component,
+                          dof_values,
+                          dofs_processed);
+
+                      // Store the computed
+                      // values in the global
+                      // vector.
+                      cell->face(face)->get_dof_indices(
+                        face_dof_indices, cell->active_fe_index());
+
+                      for (unsigned int dof = 0; dof < dofs_per_face; ++dof)
+                        if (dofs_processed[dof] &&
+                            constraints.can_store_line(face_dof_indices[dof]) &&
+                            !(constraints.is_constrained(
+                              face_dof_indices[dof])))
+                          {
+                            constraints.add_line(face_dof_indices[dof]);
+
+                            if (std::abs(dof_values[dof]) > 1e-13)
+                              constraints.set_inhomogeneity(
+                                face_dof_indices[dof], dof_values[dof]);
+                          }
+                    }
+
+            break;
+          }
+
+        default:
+          Assert(false, ExcNotImplemented());
+      }
+  }
+
+
+
+  template <int dim>
+  void
+
+  project_boundary_values_curl_conforming(
+    const hp::DoFHandler<dim> &       dof_handler,
+    const unsigned int                first_vector_component,
+    const Function<dim> &             boundary_function,
+    const types::boundary_id          boundary_component,
+    AffineConstraints<double> &       constraints,
+    const hp::MappingCollection<dim> &mapping_collection)
+  {
+    const hp::FECollection<dim> &fe_collection(dof_handler.get_fe_collection());
+    hp::QCollection<dim>         face_quadrature_collection;
+
+    for (unsigned int i = 0; i < fe_collection.size(); ++i)
+      {
+        const QGauss<dim - 1> reference_face_quadrature(
+          2 * fe_collection[i].degree);
+
+        for (unsigned int face : GeometryInfo<dim>::face_indices())
+          face_quadrature_collection.push_back(
+            QProjector<dim>::project_to_face(reference_face_quadrature, face));
+      }
+
+    hp::FEValues<dim>                    fe_face_values(mapping_collection,
+                                     fe_collection,
+                                     face_quadrature_collection,
+                                     update_jacobians | update_JxW_values |
+                                       update_quadrature_points |
+                                       update_values);
+    std::vector<bool>                    dofs_processed;
+    std::vector<double>                  dof_values;
+    std::vector<types::global_dof_index> face_dof_indices;
+    typename hp::DoFHandler<dim>::active_cell_iterator cell =
+      dof_handler.begin_active();
+
+    switch (dim)
+      {
+        case 2:
+          {
+            for (; cell != dof_handler.end(); ++cell)
+              if (cell->at_boundary() && cell->is_locally_owned())
+                for (const unsigned int face :
+                     GeometryInfo<dim>::face_indices())
+                  if (cell->face(face)->boundary_id() == boundary_component)
+                    {
+                      // if the FE is a FE_Nothing object there is no work to do
+                      if (dynamic_cast<const FE_Nothing<dim> *>(
+                            &cell->get_fe()) != nullptr)
+                        return;
+
+                      // This is only implemented, if the FE is a Nedelec
+                      // element. If the FE is a FESystem we cannot check this.
+                      if (dynamic_cast<const FESystem<dim> *>(
+                            &cell->get_fe()) == nullptr)
+                        {
+                          AssertThrow(dynamic_cast<const FE_Nedelec<dim> *>(
+                                        &cell->get_fe()) != nullptr,
+                                      typename FiniteElement<
+                                        dim>::ExcInterpolationNotImplemented());
+                        }
+
+                      const unsigned int dofs_per_face =
+                        cell->get_fe().dofs_per_face;
+
+                      dofs_processed.resize(dofs_per_face);
+                      dof_values.resize(dofs_per_face);
+
+                      for (unsigned int dof = 0; dof < dofs_per_face; ++dof)
+                        {
+                          dof_values[dof]     = 0.0;
+                          dofs_processed[dof] = false;
+                        }
+
+                      internals::compute_face_projection_curl_conforming(
+                        cell,
+                        face,
+                        fe_face_values,
+                        boundary_function,
+                        first_vector_component,
+                        dof_values,
+                        dofs_processed);
+                      face_dof_indices.resize(dofs_per_face);
+                      cell->face(face)->get_dof_indices(
+                        face_dof_indices, cell->active_fe_index());
+
+                      for (unsigned int dof = 0; dof < dofs_per_face; ++dof)
+                        if (dofs_processed[dof] &&
+                            constraints.can_store_line(face_dof_indices[dof]) &&
+                            !(constraints.is_constrained(
+                              face_dof_indices[dof])))
+                          {
+                            constraints.add_line(face_dof_indices[dof]);
+
+                            if (std::abs(dof_values[dof]) > 1e-13)
+                              constraints.set_inhomogeneity(
+                                face_dof_indices[dof], dof_values[dof]);
+                          }
+                    }
+
+            break;
+          }
+
+        case 3:
+          {
+            hp::QCollection<dim> edge_quadrature_collection;
+
+            for (unsigned int i = 0; i < fe_collection.size(); ++i)
+              {
+                const QGauss<dim - 2> reference_edge_quadrature(
+                  2 * fe_collection[i].degree);
+
+                for (const unsigned int face :
+                     GeometryInfo<dim>::face_indices())
+                  for (unsigned int line = 0;
+                       line < GeometryInfo<dim>::lines_per_face;
+                       ++line)
+                    edge_quadrature_collection.push_back(
+                      QProjector<dim>::project_to_face(
+                        QProjector<dim - 1>::project_to_face(
+                          reference_edge_quadrature, line),
+                        face));
+              }
+
+            hp::FEValues<dim> fe_edge_values(mapping_collection,
+                                             fe_collection,
+                                             edge_quadrature_collection,
+                                             update_jacobians |
+                                               update_JxW_values |
+                                               update_quadrature_points |
+                                               update_values);
+
+            for (; cell != dof_handler.end(); ++cell)
+              if (cell->at_boundary() && cell->is_locally_owned())
+                for (const unsigned int face :
+                     GeometryInfo<dim>::face_indices())
+                  if (cell->face(face)->boundary_id() == boundary_component)
+                    {
+                      // if the FE is a FE_Nothing object there is no work to do
+                      if (dynamic_cast<const FE_Nothing<dim> *>(
+                            &cell->get_fe()) != nullptr)
+                        return;
+
+                      // This is only implemented, if the FE is a Nedelec
+                      // element. If the FE is a FESystem we cannot check this.
+                      if (dynamic_cast<const FESystem<dim> *>(
+                            &cell->get_fe()) == nullptr)
+                        {
+                          AssertThrow(dynamic_cast<const FE_Nedelec<dim> *>(
+                                        &cell->get_fe()) != nullptr,
+                                      typename FiniteElement<
+                                        dim>::ExcInterpolationNotImplemented());
+                        }
+
+                      const unsigned int superdegree = cell->get_fe().degree;
+                      const unsigned int degree      = superdegree - 1;
+                      const unsigned int dofs_per_face =
+                        cell->get_fe().dofs_per_face;
+
+                      dofs_processed.resize(dofs_per_face);
+                      dof_values.resize(dofs_per_face);
+
+                      for (unsigned int dof = 0; dof < dofs_per_face; ++dof)
+                        {
+                          dof_values[dof]     = 0.0;
+                          dofs_processed[dof] = false;
+                        }
+
+                      for (unsigned int line = 0;
+                           line < GeometryInfo<dim>::lines_per_face;
+                           ++line)
+                        internals::compute_edge_projection(
+                          cell,
+                          face,
+                          line,
+                          fe_edge_values,
+                          boundary_function,
+                          first_vector_component,
+                          dof_values,
+                          dofs_processed);
+
+                      // If there are higher order shape functions, there is
+                      // still some work left.
+                      if (degree > 0)
+                        internals::compute_face_projection_curl_conforming(
+                          cell,
+                          face,
+                          fe_face_values,
+                          boundary_function,
+                          first_vector_component,
+                          dof_values,
+                          dofs_processed);
+
+
+                      face_dof_indices.resize(dofs_per_face);
+                      cell->face(face)->get_dof_indices(
+                        face_dof_indices, cell->active_fe_index());
+
+                      for (unsigned int dof = 0; dof < dofs_per_face; ++dof)
+                        if (dofs_processed[dof] &&
+                            constraints.can_store_line(face_dof_indices[dof]) &&
+                            !(constraints.is_constrained(
+                              face_dof_indices[dof])))
+                          {
+                            constraints.add_line(face_dof_indices[dof]);
+
+                            if (std::abs(dof_values[dof]) > 1e-13)
+                              constraints.set_inhomogeneity(
+                                face_dof_indices[dof], dof_values[dof]);
+                          }
+                    }
+
+            break;
+          }
+
+        default:
+          Assert(false, ExcNotImplemented());
+      }
+  }
+
+
+  namespace internals
+  {
+    template <int dim, typename cell_iterator, typename number>
+    typename std::enable_if<dim == 3>::type
+    compute_edge_projection_l2(const cell_iterator &        cell,
+                               const unsigned int           face,
+                               const unsigned int           line,
+                               hp::FEValues<dim> &          hp_fe_values,
+                               const Function<dim, number> &boundary_function,
+                               const unsigned int   first_vector_component,
+                               std::vector<number> &dof_values,
+                               std::vector<bool> &  dofs_processed)
+    {
+      // This function computes the L2-projection of the given
+      // boundary function on 3D edges and returns the constraints
+      // associated with the edge functions for the given cell.
+      //
+      // In the context of this function, by associated DoFs we mean:
+      // the DoFs corresponding to the group of components making up the vector
+      // with first component first_vector_component (length dim).
+      const FiniteElement<dim> &fe = cell->get_fe();
+
+      // reinit for this cell, face and line.
+      hp_fe_values.reinit(
+        cell,
+        (cell->active_fe_index() * GeometryInfo<dim>::faces_per_cell + face) *
+            GeometryInfo<dim>::lines_per_face +
+          line);
+
+      // Initialize the required objects.
+      const FEValues<dim> &fe_values = hp_fe_values.get_present_fe_values();
+
+      const std::vector<Point<dim>> &quadrature_points =
+        fe_values.get_quadrature_points();
+      std::vector<Vector<number>> values(fe_values.n_quadrature_points,
+                                         Vector<number>(fe.n_components()));
+
+      // Get boundary function values
+      // at quadrature points.
+      boundary_function.vector_value_list(quadrature_points, values);
+
+      // Find the group of vector components we want to project onto
+      // (dim of them, starting at first_vector_component) within the
+      // overall finite element (which may be an FESystem).
+      std::pair<unsigned int, unsigned int> base_indices(0, 0);
+      if (dynamic_cast<const FESystem<dim> *>(&cell->get_fe()) != nullptr)
+        {
+          unsigned int fe_index     = 0;
+          unsigned int fe_index_old = 0;
+          unsigned int i            = 0;
+
+          // Find base element:
+          // base_indices.first
+          //
+          // Then select which copy of that base element
+          // [ each copy is of length
+          // fe.base_element(base_indices.first).n_components() ] corresponds to
+          // first_vector_component: base_index.second
+          for (; i < fe.n_base_elements(); ++i)
+            {
+              fe_index_old = fe_index;
+              fe_index +=
+                fe.element_multiplicity(i) * fe.base_element(i).n_components();
+
+              if (fe_index > first_vector_component)
+                break;
+            }
+
+          base_indices.first  = i;
+          base_indices.second = (first_vector_component - fe_index_old) /
+                                fe.base_element(i).n_components();
+        }
+      else
+        // The only other element we know how to deal with (so far) is
+        // FE_Nedelec, which has one base element and one copy of it
+        // (with 3 components). In that case, the values of
+        // 'base_indices' as initialized above are correct.
+        Assert((dynamic_cast<const FE_Nedelec<dim> *>(&cell->get_fe()) !=
+                nullptr) ||
+                 (dynamic_cast<const FE_NedelecSZ<dim> *>(&cell->get_fe()) !=
+                  nullptr),
+               ExcNotImplemented());
+
+
+      // Store the 'degree' of the Nedelec element as fe.degree-1. For
+      // Nedelec elements, FE_Nedelec<dim>(0) returns fe.degree = 1
+      // because fe.degree stores the *polynomial* degree, not the
+      // degree of the element (which is typically defined based on
+      // the largest polynomial space that is *complete* within the
+      // finite element).
+      const unsigned int degree =
+        fe.base_element(base_indices.first).degree - 1;
+
+      // Find DoFs we want to constrain: There are
+      // fe.base_element(base_indices.first).dofs_per_line DoFs
+      // associated with the given line on the given face on the given
+      // cell.
+      //
+      // We need to know which of these DoFs (there are degree+1 of interest)
+      // are associated with the components given by first_vector_component.
+      // Then we can make a map from the associated line DoFs to the face DoFs.
+      //
+      // For a single FE_Nedelec<3> element this is simple:
+      //    We know the ordering of local DoFs goes
+      //    lines -> faces -> cells
+      //
+      // For a set of FESystem<3> elements we need to pick out the matching base
+      // element and the index within this ordering.
+      //
+      // We call the map associated_edge_dof_to_face_dof
+      std::vector<unsigned int> associated_edge_dof_to_face_dof(
+        degree + 1, numbers::invalid_unsigned_int);
+
+      // Lowest DoF in the base element allowed for this edge:
+      const unsigned int lower_bound =
+        fe.base_element(base_indices.first)
+          .face_to_cell_index(line * (degree + 1), face);
+      // Highest DoF in the base element allowed for this edge:
+      const unsigned int upper_bound =
+        fe.base_element(base_indices.first)
+          .face_to_cell_index((line + 1) * (degree + 1) - 1, face);
+
+      unsigned int associated_edge_dof_index = 0;
+      for (unsigned int line_dof_idx = 0; line_dof_idx < fe.dofs_per_line;
+           ++line_dof_idx)
+        {
+          // For each DoF associated with the (interior of) the line, we need
+          // to figure out which base element it belongs to and then if
+          // that's the correct base element. This is complicated by the
+          // fact that the FiniteElement class has functions that translate
+          // from face to cell, but not from edge to cell index systems. So
+          // we have to do that step by step.
+          //
+          // DoFs on a face in 3d are numbered in order by vertices then lines
+          // then faces.
+          // i.e. line 0 has degree+1 dofs numbered 0,..,degree
+          //      line 1 has degree+1 dofs numbered (degree+1),..,2*(degree+1)
+          //      and so on.
+
+          const unsigned int face_dof_idx =
+            GeometryInfo<dim>::vertices_per_face * fe.dofs_per_vertex +
+            line * fe.dofs_per_line + line_dof_idx;
+
+          // Note, assuming that the edge orientations are "standard"
+          //       i.e. cell->line_orientation(line) = true.
+          Assert(cell->line_orientation(line),
+                 ExcMessage("Edge orientation does not meet expectation."));
+          // Next, translate from face to cell. Note, this might be assuming
+          // that the edge orientations are "standard" (not sure any more at
+          // this time), i.e.
+          //       cell->line_orientation(line) = true.
+          const unsigned int cell_dof_idx =
+            fe.face_to_cell_index(face_dof_idx, face);
+
+          // Check that this cell_idx belongs to the correct base_element,
+          // component and line. We do this for each of the supported elements
+          // separately
+          bool dof_is_of_interest = false;
+          if (dynamic_cast<const FESystem<dim> *>(&fe) != nullptr)
+            {
+              dof_is_of_interest =
+                (fe.system_to_base_index(cell_dof_idx).first == base_indices) &&
+                (lower_bound <= fe.system_to_base_index(cell_dof_idx).second) &&
+                (fe.system_to_base_index(cell_dof_idx).second <= upper_bound);
+            }
+          else if ((dynamic_cast<const FE_Nedelec<dim> *>(&fe) != nullptr) ||
+                   (dynamic_cast<const FE_NedelecSZ<dim> *>(&fe) != nullptr))
+            {
+              Assert((line * (degree + 1) <= face_dof_idx) &&
+                       (face_dof_idx < (line + 1) * (degree + 1)),
+                     ExcInternalError());
+              dof_is_of_interest = true;
+            }
+          else
+            Assert(false, ExcNotImplemented());
+
+          if (dof_is_of_interest)
+            {
+              associated_edge_dof_to_face_dof[associated_edge_dof_index] =
+                face_dof_idx;
+              ++associated_edge_dof_index;
+            }
+        }
+      // Sanity check:
+      const unsigned int n_associated_edge_dofs = associated_edge_dof_index;
+      Assert(n_associated_edge_dofs == degree + 1,
+             ExcMessage("Error: Unexpected number of 3D edge DoFs"));
+
+      // Matrix and RHS vectors to store linear system:
+      // We have (degree+1) basis functions for an edge
+      FullMatrix<number> edge_matrix(degree + 1, degree + 1);
+      FullMatrix<number> edge_matrix_inv(degree + 1, degree + 1);
+      Vector<number>     edge_rhs(degree + 1);
+      Vector<number>     edge_solution(degree + 1);
+
+      const FEValuesExtractors::Vector vec(first_vector_component);
+
+      // coordinate directions of
+      // the edges of the face.
+      const unsigned int
+        edge_coordinate_direction[GeometryInfo<dim>::faces_per_cell]
+                                 [GeometryInfo<dim>::lines_per_face] = {
+                                   {2, 2, 1, 1},
+                                   {2, 2, 1, 1},
+                                   {0, 0, 2, 2},
+                                   {0, 0, 2, 2},
+                                   {1, 1, 0, 0},
+                                   {1, 1, 0, 0}};
+
+      const double tol =
+        0.5 * cell->face(face)->line(line)->diameter() / fe.degree;
+      const std::vector<Point<dim>> &reference_quadrature_points =
+        fe_values.get_quadrature().get_points();
+
+      // Project the boundary function onto the shape functions for this edge
+      // and set up a linear system of equations to get the values for the DoFs
+      // associated with this edge.
+      for (unsigned int q_point = 0; q_point < fe_values.n_quadrature_points;
+           ++q_point)
+        {
+          // Compute the tangential
+          // of the edge at
+          // the quadrature point.
+          Point<dim> shifted_reference_point_1 =
+            reference_quadrature_points[q_point];
+          Point<dim> shifted_reference_point_2 =
+            reference_quadrature_points[q_point];
+
+          shifted_reference_point_1(edge_coordinate_direction[face][line]) +=
+            tol;
+          shifted_reference_point_2(edge_coordinate_direction[face][line]) -=
+            tol;
+          Tensor<1, dim> tangential =
+            (0.5 *
+             (fe_values.get_mapping().transform_unit_to_real_cell(
+                cell, shifted_reference_point_1) -
+              fe_values.get_mapping().transform_unit_to_real_cell(
+                cell, shifted_reference_point_2)) /
+             tol);
+          tangential /= tangential.norm();
+
+          // Compute the entries of the linear system
+          // Note the system is symmetric so we could only compute the
+          // lower/upper triangle.
+          //
+          // The matrix entries are
+          // \int_{edge}
+          // (tangential*edge_shape_function_i)*(tangential*edge_shape_function_j)
+          // dS
+          //
+          // The RHS entries are:
+          // \int_{edge}
+          // (tangential*boundary_value)*(tangential*edge_shape_function_i) dS.
+          for (unsigned int j = 0; j < n_associated_edge_dofs; ++j)
+            {
+              const unsigned int j_face_idx =
+                associated_edge_dof_to_face_dof[j];
+              const unsigned int j_cell_idx =
+                fe.face_to_cell_index(j_face_idx, face);
+              for (unsigned int i = 0; i < n_associated_edge_dofs; ++i)
+                {
+                  const unsigned int i_face_idx =
+                    associated_edge_dof_to_face_dof[i];
+                  const unsigned int i_cell_idx =
+                    fe.face_to_cell_index(i_face_idx, face);
+
+                  edge_matrix(i, j) +=
+                    fe_values.JxW(q_point) *
+                    (fe_values[vec].value(i_cell_idx, q_point) * tangential) *
+                    (fe_values[vec].value(j_cell_idx, q_point) * tangential);
+                }
+              // Compute the RHS entries:
+              edge_rhs(j) +=
+                fe_values.JxW(q_point) *
+                (values[q_point](first_vector_component) * tangential[0] +
+                 values[q_point](first_vector_component + 1) * tangential[1] +
+                 values[q_point](first_vector_component + 2) * tangential[2]) *
+                (fe_values[vec].value(j_cell_idx, q_point) * tangential);
+            }
+        }
+
+      // Invert linear system
+      edge_matrix_inv.invert(edge_matrix);
+      edge_matrix_inv.vmult(edge_solution, edge_rhs);
+
+      // Store computed DoFs
+      for (unsigned int i = 0; i < n_associated_edge_dofs; ++i)
+        {
+          dof_values[associated_edge_dof_to_face_dof[i]]     = edge_solution(i);
+          dofs_processed[associated_edge_dof_to_face_dof[i]] = true;
+        }
+    }
+
+
+    template <int dim, typename cell_iterator, typename number>
+    typename std::enable_if<dim != 3>::type
+    compute_edge_projection_l2(const cell_iterator &,
+                               const unsigned int,
+                               const unsigned int,
+                               hp::FEValues<dim> &,
+                               const Function<dim, number> &,
+                               const unsigned int,
+                               std::vector<number> &,
+                               std::vector<bool> &)
+    {
+      // dummy implementation of above function
+      // for all other dimensions
+      Assert(false, ExcInternalError());
+    }
+
+
+    template <int dim, typename cell_iterator, typename number>
+    void
+    compute_face_projection_curl_conforming_l2(
+      const cell_iterator &        cell,
+      const unsigned int           face,
+      hp::FEFaceValues<dim> &      hp_fe_face_values,
+      const Function<dim, number> &boundary_function,
+      const unsigned int           first_vector_component,
+      std::vector<number> &        dof_values,
+      std::vector<bool> &          dofs_processed)
+    {
+      // This function computes the L2-projection of the boundary
+      // function on the interior of faces only. In 3D, this should only be
+      // called after first calling compute_edge_projection_l2, as it relies on
+      // edge constraints which are found.
+
+      // In the context of this function, by associated DoFs we mean:
+      // the DoFs corresponding to the group of components making up the vector
+      // with first component first_vector_component (with total components
+      // dim).
+
+      // Copy to the standard FEFaceValues object:
+      hp_fe_face_values.reinit(cell, face);
+      const FEFaceValues<dim> &fe_face_values =
+        hp_fe_face_values.get_present_fe_values();
+
+      // Initialize the required objects.
+      const FiniteElement<dim> &     fe = cell->get_fe();
+      const std::vector<Point<dim>> &quadrature_points =
+        fe_face_values.get_quadrature_points();
+
+      std::vector<Vector<number>> values(fe_face_values.n_quadrature_points,
+                                         Vector<number>(fe.n_components()));
+
+      // Get boundary function values at quadrature points.
+      boundary_function.vector_value_list(quadrature_points, values);
+
+      // Find where the group of vector components (dim of them,
+      // starting at first_vector_component) are within an FESystem.
+      //
+      // If not using FESystem then must be using FE_Nedelec,
+      // which has one base element and one copy of it (with 3 components).
+      std::pair<unsigned int, unsigned int> base_indices(0, 0);
+      if (dynamic_cast<const FESystem<dim> *>(&cell->get_fe()) != nullptr)
+        {
+          unsigned int fe_index     = 0;
+          unsigned int fe_index_old = 0;
+          unsigned int i            = 0;
+
+          // Find base element:
+          // base_indices.first
+          //
+          // Then select which copy of that base element
+          // [ each copy is of length
+          // fe.base_element(base_indices.first).n_components() ] corresponds to
+          // first_vector_component: base_index.second
+          for (; i < fe.n_base_elements(); ++i)
+            {
+              fe_index_old = fe_index;
+              fe_index +=
+                fe.element_multiplicity(i) * fe.base_element(i).n_components();
+
+              if (fe_index > first_vector_component)
+                break;
+            }
+          base_indices.first  = i;
+          base_indices.second = (first_vector_component - fe_index_old) /
+                                fe.base_element(i).n_components();
+        }
+      else
+        {
+          // Assert that the FE is in fact an FE_Nedelec, so that the default
+          // base_indices == (0,0) is correct.
+          Assert((dynamic_cast<const FE_Nedelec<dim> *>(&cell->get_fe()) !=
+                  nullptr) ||
+                   (dynamic_cast<const FE_NedelecSZ<dim> *>(&cell->get_fe()) !=
+                    nullptr),
+                 ExcNotImplemented());
+        }
+      const unsigned int degree =
+        fe.base_element(base_indices.first).degree - 1;
+
+      switch (dim)
+        {
+          case 2:
+            // NOTE: This is very similar to compute_edge_projection as used in
+            // 3D,
+            //       and contains a lot of overlap with that function.
+            {
+              // Find the DoFs we want to constrain. There are degree+1 in
+              // total. Create a map from these to the face index Note:
+              //    - for a single FE_Nedelec<2> element this is
+              //      simply 0 to fe.dofs_per_face
+              //    - for FESystem<2> this just requires matching the
+              //      base element, fe.system_to_base_index.first.first
+              //      and the copy of the base element we're interested
+              //      in, fe.system_to_base_index.first.second
+              std::vector<unsigned int> associated_edge_dof_to_face_dof(degree +
+                                                                        1);
+
+              unsigned int associated_edge_dof_index = 0;
+              for (unsigned int face_idx = 0; face_idx < fe.dofs_per_face;
+                   ++face_idx)
+                {
+                  const unsigned int cell_idx =
+                    fe.face_to_cell_index(face_idx, face);
+                  if (((dynamic_cast<const FESystem<dim> *>(&fe) != nullptr) &&
+                       (fe.system_to_base_index(cell_idx).first ==
+                        base_indices)) ||
+                      (dynamic_cast<const FE_Nedelec<dim> *>(&fe) != nullptr) ||
+                      (dynamic_cast<const FE_NedelecSZ<dim> *>(&fe) != nullptr))
+                    {
+                      associated_edge_dof_to_face_dof
+                        [associated_edge_dof_index] = face_idx;
+                      ++associated_edge_dof_index;
+                    }
+                }
+              // Sanity check:
+              const unsigned int associated_edge_dofs =
+                associated_edge_dof_index;
+              Assert(associated_edge_dofs == degree + 1,
+                     ExcMessage("Error: Unexpected number of 2D edge DoFs"));
+
+              // Matrix and RHS vectors to store:
+              // We have (degree+1) edge basis functions
+              FullMatrix<number> edge_matrix(degree + 1, degree + 1);
+              FullMatrix<number> edge_matrix_inv(degree + 1, degree + 1);
+              Vector<number>     edge_rhs(degree + 1);
+              Vector<number>     edge_solution(degree + 1);
+
+              const FEValuesExtractors::Vector vec(first_vector_component);
+
+              // Project the boundary function onto the shape functions for this
+              // edge and set up a linear system of equations to get the values
+              // for the DoFs associated with this edge.
+              for (unsigned int q_point = 0;
+                   q_point < fe_face_values.n_quadrature_points;
+                   ++q_point)
+                {
+                  // Compute the entries of the linear system
+                  // Note the system is symmetric so we could only compute the
+                  // lower/upper triangle.
+                  //
+                  // The matrix entries are
+                  // \int_{edge} (tangential * edge_shape_function_i) *
+                  // (tangential * edge_shape_function_j) dS
+                  //
+                  // The RHS entries are:
+                  // \int_{edge} (tangential* boundary_value) * (tangential *
+                  // edge_shape_function_i) dS.
+                  //
+                  // In 2D, tangential*vector is equivalent to
+                  // cross_product_3d(normal, vector), so we use this instead.
+                  // This avoids possible issues with the computation of the
+                  // tangent.
+
+                  // Store the normal at this quad point:
+                  Tensor<1, dim> normal_at_q_point =
+                    fe_face_values.normal_vector(q_point);
+                  for (unsigned int j = 0; j < associated_edge_dofs; ++j)
+                    {
+                      const unsigned int j_face_idx =
+                        associated_edge_dof_to_face_dof[j];
+                      const unsigned int j_cell_idx =
+                        fe.face_to_cell_index(j_face_idx, face);
+
+                      Tensor<1, dim> phi_j =
+                        fe_face_values[vec].value(j_cell_idx, q_point);
+                      for (unsigned int i = 0; i < associated_edge_dofs; ++i)
+                        {
+                          const unsigned int i_face_idx =
+                            associated_edge_dof_to_face_dof[i];
+                          const unsigned int i_cell_idx =
+                            fe.face_to_cell_index(i_face_idx, face);
+
+                          Tensor<1, dim> phi_i =
+                            fe_face_values[vec].value(i_cell_idx, q_point);
+
+                          // Using n cross phi
+                          edge_matrix(i, j) +=
+                            fe_face_values.JxW(q_point) *
+                            ((phi_i[1] * normal_at_q_point[0] -
+                              phi_i[0] * normal_at_q_point[1]) *
+                             (phi_j[1] * normal_at_q_point[0] -
+                              phi_j[0] * normal_at_q_point[1]));
+                        }
+                      // Using n cross phi
+                      edge_rhs(j) +=
+                        fe_face_values.JxW(q_point) *
+                        ((values[q_point](first_vector_component + 1) *
+                            normal_at_q_point[0] -
+                          values[q_point](first_vector_component) *
+                            normal_at_q_point[1]) *
+                         (phi_j[1] * normal_at_q_point[0] -
+                          phi_j[0] * normal_at_q_point[1]));
+                    }
+                }
+
+              // Invert linear system
+              edge_matrix_inv.invert(edge_matrix);
+              edge_matrix_inv.vmult(edge_solution, edge_rhs);
+
+              // Store computed DoFs
+              for (unsigned int associated_edge_dof_index = 0;
+                   associated_edge_dof_index < associated_edge_dofs;
+                   ++associated_edge_dof_index)
+                {
+                  dof_values[associated_edge_dof_to_face_dof
+                               [associated_edge_dof_index]] =
+                    edge_solution(associated_edge_dof_index);
+                  dofs_processed[associated_edge_dof_to_face_dof
+                                   [associated_edge_dof_index]] = true;
+                }
+              break;
+            }
+
+          case 3:
+            {
+              const FEValuesExtractors::Vector vec(first_vector_component);
+
+              // First group DoFs associated with edges which we already know.
+              // Sort these into groups of dofs (0 -> degree+1 of them) by each
+              // edge. This will help when computing the residual for the face
+              // projections.
+              //
+              // This matches with the search done in compute_edge_projection.
+              const unsigned int lines_per_face =
+                GeometryInfo<dim>::lines_per_face;
+              std::vector<std::vector<unsigned int>>
+                                        associated_edge_dof_to_face_dof(lines_per_face,
+                                                                        std::vector<unsigned int>(degree +
+                                                                        1));
+              std::vector<unsigned int> associated_edge_dofs(lines_per_face);
+
+              for (unsigned int line = 0; line < lines_per_face; ++line)
+                {
+                  // Lowest DoF in the base element allowed for this edge:
+                  const unsigned int lower_bound =
+                    fe.base_element(base_indices.first)
+                      .face_to_cell_index(line * (degree + 1), face);
+                  // Highest DoF in the base element allowed for this edge:
+                  const unsigned int upper_bound =
+                    fe.base_element(base_indices.first)
+                      .face_to_cell_index((line + 1) * (degree + 1) - 1, face);
+                  unsigned int associated_edge_dof_index = 0;
+
+                  for (unsigned int line_dof_idx = 0;
+                       line_dof_idx < fe.dofs_per_line;
+                       ++line_dof_idx)
+                    {
+                      // For each DoF associated with the (interior of) the
+                      // line, we need to figure out which base element it
+                      // belongs to and then if that's the correct base element.
+                      // This is complicated by the fact that the FiniteElement
+                      // class has functions that translate from face to cell,
+                      // but not from edge to cell index systems. So we have to
+                      // do that step by step.
+                      //
+                      // DoFs on a face in 3d are numbered in order by vertices
+                      // then lines then faces. i.e. line 0 has degree+1 dofs
+                      // numbered 0,..,degree
+                      //      line 1 has degree+1 dofs numbered
+                      //      (degree+1),..,2*(degree+1) and so on.
+                      const unsigned int face_dof_idx =
+                        GeometryInfo<dim>::vertices_per_face *
+                          fe.dofs_per_vertex +
+                        line * fe.dofs_per_line + line_dof_idx;
+
+                      // Next, translate from face to cell. Note, this might be
+                      // assuming that the edge orientations are "standard" (not
+                      // sure any more at this time), i.e.
+                      //       cell->line_orientation(line) = true.
+                      const unsigned int cell_dof_idx =
+                        fe.face_to_cell_index(face_dof_idx, face);
+
+                      // Check that this cell_idx belongs to the correct
+                      // base_element, component and line. We do this for each
+                      // of the supported elements separately
+                      bool dof_is_of_interest = false;
+                      if (dynamic_cast<const FESystem<dim> *>(&fe) != nullptr)
+                        {
+                          dof_is_of_interest =
+                            (fe.system_to_base_index(cell_dof_idx).first ==
+                             base_indices) &&
+                            (lower_bound <=
+                             fe.system_to_base_index(cell_dof_idx).second) &&
+                            (fe.system_to_base_index(cell_dof_idx).second <=
+                             upper_bound);
+                        }
+                      else if ((dynamic_cast<const FE_Nedelec<dim> *>(&fe) !=
+                                nullptr) ||
+                               (dynamic_cast<const FE_NedelecSZ<dim> *>(&fe) !=
+                                nullptr))
+                        {
+                          Assert((line * (degree + 1) <= face_dof_idx) &&
+                                   (face_dof_idx < (line + 1) * (degree + 1)),
+                                 ExcInternalError());
+                          dof_is_of_interest = true;
+                        }
+                      else
+                        Assert(false, ExcNotImplemented());
+
+                      if (dof_is_of_interest)
+                        {
+                          associated_edge_dof_to_face_dof
+                            [line][associated_edge_dof_index] = face_dof_idx;
+                          ++associated_edge_dof_index;
+                        }
+                    }
+                  // Sanity check:
+                  associated_edge_dofs[line] = associated_edge_dof_index;
+                  Assert(associated_edge_dofs[line] == degree + 1,
+                         ExcInternalError());
+                }
+
+              // Next find the face DoFs associated with the vector components
+              // we're interested in. There are 2*degree*(degree+1) DoFs
+              // associated with the interior of each face (not including
+              // edges!).
+              //
+              // Create a map mapping from the consecutively numbered
+              // associated_dofs to the face DoF (which can be transferred to a
+              // local cell index).
+              //
+              // For FE_Nedelec<3> we just need to have a face numbering greater
+              // than the number of edge DoFs (=lines_per_face*(degree+1).
+              //
+              // For FESystem<3> we need to base the base_indices (base element
+              // and copy within that base element) and ensure we're above the
+              // number of edge DoFs within that base element.
+              std::vector<unsigned int> associated_face_dof_to_face_dof(
+                2 * degree * (degree + 1));
+
+              // Loop over these quad-interior dofs.
+              unsigned int associated_face_dof_index = 0;
+              for (unsigned int quad_dof_idx = 0;
+                   quad_dof_idx < fe.dofs_per_quad;
+                   ++quad_dof_idx)
+                {
+                  const unsigned int face_idx =
+                    GeometryInfo<dim>::vertices_per_face * fe.dofs_per_vertex +
+                    lines_per_face * fe.dofs_per_line + quad_dof_idx;
+                  const unsigned int cell_idx =
+                    fe.face_to_cell_index(face_idx, face);
+                  if (((dynamic_cast<const FESystem<dim> *>(&fe) != nullptr) &&
+                       (fe.system_to_base_index(cell_idx).first ==
+                        base_indices)) ||
+                      (dynamic_cast<const FE_Nedelec<dim> *>(&fe) != nullptr) ||
+                      (dynamic_cast<const FE_NedelecSZ<dim> *>(&fe) != nullptr))
+                    {
+                      AssertIndexRange(associated_face_dof_index,
+                                       associated_face_dof_to_face_dof.size());
+                      associated_face_dof_to_face_dof
+                        [associated_face_dof_index] = face_idx;
+                      ++associated_face_dof_index;
+                    }
+                }
+              // Sanity check:
+              const unsigned int associated_face_dofs =
+                associated_face_dof_index;
+              Assert(associated_face_dofs == 2 * degree * (degree + 1),
+                     ExcMessage("Error: Unexpected number of 3D face DoFs"));
+
+              // Storage for the linear system.
+              // There are 2*degree*(degree+1) DoFs associated with a face in
+              // 3D. Note this doesn't include the DoFs associated with edges on
+              // that face.
+              FullMatrix<number> face_matrix(2 * degree * (degree + 1));
+              FullMatrix<number> face_matrix_inv(2 * degree * (degree + 1));
+              Vector<number>     face_rhs(2 * degree * (degree + 1));
+              Vector<number>     face_solution(2 * degree * (degree + 1));
+
+              // Project the boundary function onto the shape functions for this
+              // face and set up a linear system of equations to get the values
+              // for the DoFs associated with this face. We also must include
+              // the residuals from the shape functions associated with edges.
+              Tensor<1, dim, number> tmp;
+              Tensor<1, dim>         cross_product_i;
+              Tensor<1, dim>         cross_product_j;
+              Tensor<1, dim, number> cross_product_rhs;
+
+              // Loop to construct face linear system.
+              for (unsigned int q_point = 0;
+                   q_point < fe_face_values.n_quadrature_points;
+                   ++q_point)
+                {
+                  // First calculate the residual from the edge functions
+                  // store the result in tmp.
+                  //
+                  // Edge_residual =
+                  //        boundary_value - (
+                  //            \sum_(edges on face)
+                  //                 \sum_(DoFs on edge)
+                  //                 edge_dof_value*edge_shape_function
+                  //                   )
+                  for (unsigned int d = 0; d < dim; ++d)
+                    {
+                      tmp[d] = 0.0;
+                    }
+                  for (unsigned int line = 0; line < lines_per_face; ++line)
+                    {
+                      for (unsigned int associated_edge_dof = 0;
+                           associated_edge_dof < associated_edge_dofs[line];
+                           ++associated_edge_dof)
+                        {
+                          const unsigned int face_idx =
+                            associated_edge_dof_to_face_dof
+                              [line][associated_edge_dof];
+                          const unsigned int cell_idx =
+                            fe.face_to_cell_index(face_idx, face);
+                          tmp -= dof_values[face_idx] *
+                                 fe_face_values[vec].value(cell_idx, q_point);
+                        }
+                    }
+
+                  for (unsigned int d = 0; d < dim; ++d)
+                    {
+                      tmp[d] += values[q_point](first_vector_component + d);
+                    }
+
+                  // Tensor of normal vector on the face at q_point;
+                  const Tensor<1, dim> normal_vector =
+                    fe_face_values.normal_vector(q_point);
+
+                  // Now compute the linear system:
+                  // On a face:
+                  // The matrix entries are:
+                  // \int_{face} (n x face_shape_function_i) \cdot ( n x
+                  // face_shape_function_j) dS
+                  //
+                  // The RHS entries are:
+                  // \int_{face} (n x (Edge_residual) \cdot (n x
+                  // face_shape_function_i) dS
+
+                  for (unsigned int j = 0; j < associated_face_dofs; ++j)
+                    {
+                      const unsigned int j_face_idx =
+                        associated_face_dof_to_face_dof[j];
+                      const unsigned int cell_j =
+                        fe.face_to_cell_index(j_face_idx, face);
+
+                      cross_product_j =
+                        cross_product_3d(normal_vector,
+                                         fe_face_values[vec].value(cell_j,
+                                                                   q_point));
+
+                      for (unsigned int i = 0; i < associated_face_dofs; ++i)
+                        {
+                          const unsigned int i_face_idx =
+                            associated_face_dof_to_face_dof[i];
+                          const unsigned int cell_i =
+                            fe.face_to_cell_index(i_face_idx, face);
+                          cross_product_i = cross_product_3d(
+                            normal_vector,
+                            fe_face_values[vec].value(cell_i, q_point));
+
+                          face_matrix(i, j) += fe_face_values.JxW(q_point) *
+                                               cross_product_i *
+                                               cross_product_j;
+                        }
+                      // compute rhs
+                      cross_product_rhs = cross_product_3d(normal_vector, tmp);
+                      face_rhs(j) += fe_face_values.JxW(q_point) *
+                                     cross_product_rhs * cross_product_j;
+                    }
+                }
+
+              // Solve linear system:
+              if (associated_face_dofs > 0)
+                {
+                  face_matrix_inv.invert(face_matrix);
+                  face_matrix_inv.vmult(face_solution, face_rhs);
+                }
+
+              // Store computed DoFs:
+              for (unsigned int associated_face_dof = 0;
+                   associated_face_dof < associated_face_dofs;
+                   ++associated_face_dof)
+                {
+                  dof_values
+                    [associated_face_dof_to_face_dof[associated_face_dof]] =
+                      face_solution(associated_face_dof);
+                  dofs_processed
+                    [associated_face_dof_to_face_dof[associated_face_dof]] =
+                      true;
+                }
+              break;
+            }
+          default:
+            Assert(false, ExcNotImplemented());
+        }
+    }
+
+
+    template <int dim, typename DoFHandlerType, typename number>
+    void
+    compute_project_boundary_values_curl_conforming_l2(
+      const DoFHandlerType &                 dof_handler,
+      const unsigned int                     first_vector_component,
+      const Function<dim, number> &          boundary_function,
+      const types::boundary_id               boundary_component,
+      AffineConstraints<number> &            constraints,
+      const hp::MappingCollection<dim, dim> &mapping_collection)
+    {
+      // L2-projection based interpolation formed in one (in 2D) or two (in 3D)
+      // steps.
+      //
+      // In 2D we only need to constrain edge DoFs.
+      //
+      // In 3D we need to constrain both edge and face DoFs. This is done in two
+      // parts.
+      //
+      // For edges, since the face shape functions are zero here ("bubble
+      // functions"), we project the tangential component of the boundary
+      // function and compute the L2-projection. This returns the values for the
+      // DoFs associated with each edge shape function. In 3D, this is computed
+      // by internals::compute_edge_projection_l2, in 2D, it is handled by
+      // compute_face_projection_curl_conforming_l2.
+      //
+      // For faces we compute the residual of the boundary function which is
+      // satisfied by the edge shape functions alone. Which can then be used to
+      // calculate the remaining face DoF values via a projection which leads to
+      // a linear system to solve. This is handled by
+      // compute_face_projection_curl_conforming_l2
+      //
+      // For details see (for example) section 4.2:
+      // Electromagnetic scattering simulation using an H (curl) conforming hp
+      // finite element method in three dimensions, PD Ledger, K Morgan, O
+      // Hassan, Int. J.  Num. Meth. Fluids, Volume 53, Issue 8, pages
+      // 1267–1296, 20 March 2007:
+      // http://onlinelibrary.wiley.com/doi/10.1002/fld.1223/abstract
+
+      // Create hp FEcollection, dof_handler can be either hp or standard type.
+      // From here on we can treat it like a hp-namespace object.
+      const hp::FECollection<dim> &fe_collection(
+        dof_handler.get_fe_collection());
+
+      // Create face quadrature collection
+      hp::QCollection<dim - 1> face_quadrature_collection;
+      for (unsigned int i = 0; i < fe_collection.size(); ++i)
+        {
+          const QGauss<dim - 1> reference_face_quadrature(
+            2 * fe_collection[i].degree + 1);
+          face_quadrature_collection.push_back(reference_face_quadrature);
+        }
+
+      hp::FEFaceValues<dim> fe_face_values(mapping_collection,
+                                           fe_collection,
+                                           face_quadrature_collection,
+                                           update_values |
+                                             update_quadrature_points |
+                                             update_normal_vectors |
+                                             update_JxW_values);
+
+      // Storage for dof values found and whether they have been processed:
+      std::vector<bool>                             dofs_processed;
+      std::vector<number>                           dof_values;
+      std::vector<types::global_dof_index>          face_dof_indices;
+      typename DoFHandlerType::active_cell_iterator cell =
+        dof_handler.begin_active();
+
+      switch (dim)
+        {
+          case 2:
+            {
+              for (; cell != dof_handler.end(); ++cell)
+                {
+                  if (cell->at_boundary() && cell->is_locally_owned())
+                    {
+                      for (const unsigned int face :
+                           GeometryInfo<dim>::face_indices())
+                        {
+                          if (cell->face(face)->boundary_id() ==
+                              boundary_component)
+                            {
+                              // If the FE is an FE_Nothing object there is no
+                              // work to do
+                              if (dynamic_cast<const FE_Nothing<dim> *>(
+                                    &cell->get_fe()) != nullptr)
+                                {
+                                  return;
+                                }
+
+                              // This is only implemented for FE_Nedelec
+                              // elements. If the FE is a FESystem we cannot
+                              // check this.
+                              if (dynamic_cast<const FESystem<dim> *>(
+                                    &cell->get_fe()) == nullptr)
+                                {
+                                  AssertThrow(
+                                    (dynamic_cast<const FE_Nedelec<dim> *>(
+                                       &cell->get_fe()) != nullptr) ||
+                                      (dynamic_cast<const FE_NedelecSZ<dim> *>(
+                                         &cell->get_fe()) != nullptr),
+                                    typename FiniteElement<
+                                      dim>::ExcInterpolationNotImplemented());
+                                }
+
+                              const unsigned int dofs_per_face =
+                                cell->get_fe().dofs_per_face;
+
+                              dofs_processed.resize(dofs_per_face);
+                              dof_values.resize(dofs_per_face);
+
+                              for (unsigned int dof = 0; dof < dofs_per_face;
+                                   ++dof)
+                                {
+                                  dof_values[dof]     = 0.0;
+                                  dofs_processed[dof] = false;
+                                }
+
+                              // Compute the projection of the boundary function
+                              // on the edge. In 2D this is all that's required.
+                              compute_face_projection_curl_conforming_l2(
+                                cell,
+                                face,
+                                fe_face_values,
+                                boundary_function,
+                                first_vector_component,
+                                dof_values,
+                                dofs_processed);
+
+                              // store the local->global map:
+                              face_dof_indices.resize(dofs_per_face);
+                              cell->face(face)->get_dof_indices(
+                                face_dof_indices, cell->active_fe_index());
+
+                              // Add the computed constraints to the
+                              // AffineConstraints object, assuming the degree
+                              // of freedom is not already constrained.
+                              for (unsigned int dof = 0; dof < dofs_per_face;
+                                   ++dof)
+                                {
+                                  if (dofs_processed[dof] &&
+                                      constraints.can_store_line(
+                                        face_dof_indices[dof]) &&
+                                      !(constraints.is_constrained(
+                                        face_dof_indices[dof])))
+                                    {
+                                      constraints.add_line(
+                                        face_dof_indices[dof]);
+                                      if (std::abs(dof_values[dof]) > 1e-13)
+                                        {
+                                          constraints.set_inhomogeneity(
+                                            face_dof_indices[dof],
+                                            dof_values[dof]);
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+              break;
+            }
+
+          case 3:
+            {
+              hp::QCollection<dim> edge_quadrature_collection;
+
+              // Create equivalent of FEEdgeValues:
+              for (unsigned int i = 0; i < fe_collection.size(); ++i)
+                {
+                  const QGauss<dim - 2> reference_edge_quadrature(
+                    2 * fe_collection[i].degree + 1);
+                  for (const unsigned int face :
+                       GeometryInfo<dim>::face_indices())
+                    {
+                      for (unsigned int line = 0;
+                           line < GeometryInfo<dim>::lines_per_face;
+                           ++line)
+                        {
+                          edge_quadrature_collection.push_back(
+                            QProjector<dim>::project_to_face(
+                              QProjector<dim - 1>::project_to_face(
+                                reference_edge_quadrature, line),
+                              face));
+                        }
+                    }
+                }
+
+              hp::FEValues<dim> fe_edge_values(mapping_collection,
+                                               fe_collection,
+                                               edge_quadrature_collection,
+                                               update_jacobians |
+                                                 update_JxW_values |
+                                                 update_quadrature_points |
+                                                 update_values);
+
+              for (; cell != dof_handler.end(); ++cell)
+                {
+                  if (cell->at_boundary() && cell->is_locally_owned())
+                    {
+                      for (const unsigned int face :
+                           GeometryInfo<dim>::face_indices())
+                        {
+                          if (cell->face(face)->boundary_id() ==
+                              boundary_component)
+                            {
+                              // If the FE is an FE_Nothing object there is no
+                              // work to do
+                              if (dynamic_cast<const FE_Nothing<dim> *>(
+                                    &cell->get_fe()) != nullptr)
+                                {
+                                  return;
+                                }
+
+                              // This is only implemented for FE_Nedelec
+                              // elements. If the FE is a FESystem we cannot
+                              // check this.
+                              if (dynamic_cast<const FESystem<dim> *>(
+                                    &cell->get_fe()) == nullptr)
+                                {
+                                  AssertThrow(
+                                    (dynamic_cast<const FE_Nedelec<dim> *>(
+                                       &cell->get_fe()) != nullptr) ||
+                                      (dynamic_cast<const FE_NedelecSZ<dim> *>(
+                                         &cell->get_fe()) != nullptr),
+                                    typename FiniteElement<
+                                      dim>::ExcInterpolationNotImplemented());
+                                }
+
+                              const unsigned int superdegree =
+                                cell->get_fe().degree;
+                              const unsigned int degree = superdegree - 1;
+                              const unsigned int dofs_per_face =
+                                cell->get_fe().dofs_per_face;
+
+                              dofs_processed.resize(dofs_per_face);
+                              dof_values.resize(dofs_per_face);
+                              for (unsigned int dof = 0; dof < dofs_per_face;
+                                   ++dof)
+                                {
+                                  dof_values[dof]     = 0.0;
+                                  dofs_processed[dof] = false;
+                                }
+
+                              // First compute the projection on the edges.
+                              for (unsigned int line = 0;
+                                   line < GeometryInfo<3>::lines_per_face;
+                                   ++line)
+                                {
+                                  compute_edge_projection_l2(
+                                    cell,
+                                    face,
+                                    line,
+                                    fe_edge_values,
+                                    boundary_function,
+                                    first_vector_component,
+                                    dof_values,
+                                    dofs_processed);
+                                }
+
+                              // If there are higher order shape functions, then
+                              // we still need to compute the face projection
+                              if (degree > 0)
+                                {
+                                  compute_face_projection_curl_conforming_l2(
+                                    cell,
+                                    face,
+                                    fe_face_values,
+                                    boundary_function,
+                                    first_vector_component,
+                                    dof_values,
+                                    dofs_processed);
+                                }
+
+                              // Store the computed values in the global vector.
+                              face_dof_indices.resize(dofs_per_face);
+                              cell->face(face)->get_dof_indices(
+                                face_dof_indices, cell->active_fe_index());
+
+                              for (unsigned int dof = 0; dof < dofs_per_face;
+                                   ++dof)
+                                {
+                                  if (dofs_processed[dof] &&
+                                      constraints.can_store_line(
+                                        face_dof_indices[dof]) &&
+                                      !(constraints.is_constrained(
+                                        face_dof_indices[dof])))
+                                    {
+                                      constraints.add_line(
+                                        face_dof_indices[dof]);
+
+                                      if (std::abs(dof_values[dof]) > 1e-13)
+                                        {
+                                          constraints.set_inhomogeneity(
+                                            face_dof_indices[dof],
+                                            dof_values[dof]);
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+              break;
+            }
+          default:
+            Assert(false, ExcNotImplemented());
+        }
+    }
+
+  } // namespace internals
+
+
+  template <int dim, typename number>
+  void
+  project_boundary_values_curl_conforming_l2(
+    const DoFHandler<dim> &      dof_handler,
+    const unsigned int           first_vector_component,
+    const Function<dim, number> &boundary_function,
+    const types::boundary_id     boundary_component,
+    AffineConstraints<number> &  constraints,
+    const Mapping<dim> &         mapping)
+  {
+    // non-hp version - calls the internal
+    // compute_project_boundary_values_curl_conforming_l2() function
+    // above after recasting the mapping.
+
+    const hp::MappingCollection<dim> mapping_collection(mapping);
+    internals::compute_project_boundary_values_curl_conforming_l2(
+      dof_handler,
+      first_vector_component,
+      boundary_function,
+      boundary_component,
+      constraints,
+      mapping_collection);
+  }
+
+  template <int dim, typename number>
+  void
+  project_boundary_values_curl_conforming_l2(
+    const hp::DoFHandler<dim> &            dof_handler,
+    const unsigned int                     first_vector_component,
+    const Function<dim, number> &          boundary_function,
+    const types::boundary_id               boundary_component,
+    AffineConstraints<number> &            constraints,
+    const hp::MappingCollection<dim, dim> &mapping_collection)
+  {
+    // hp version - calls the internal
+    // compute_project_boundary_values_curl_conforming_l2() function above.
+    internals::compute_project_boundary_values_curl_conforming_l2(
+      dof_handler,
+      first_vector_component,
+      boundary_function,
+      boundary_component,
+      constraints,
+      mapping_collection);
+  }
+
+
+
+  namespace internals
+  {
+    // This function computes the projection of the boundary function on the
+    // boundary in 2d.
+    template <typename cell_iterator>
+    void
+    compute_face_projection_div_conforming(
+      const cell_iterator &                       cell,
+      const unsigned int                          face,
+      const FEFaceValues<2> &                     fe_values,
+      const unsigned int                          first_vector_component,
+      const Function<2> &                         boundary_function,
+      const std::vector<DerivativeForm<1, 2, 2>> &jacobians,
+      AffineConstraints<double> &                 constraints)
+    {
+      // Compute the integral over the product of the normal components of
+      // the boundary function times the normal components of the shape
+      // functions supported on the boundary.
+      const FEValuesExtractors::Vector vec(first_vector_component);
+      const FiniteElement<2> &         fe      = cell->get_fe();
+      const std::vector<Tensor<1, 2>> &normals = fe_values.get_normal_vectors();
+      const unsigned int
+                                  face_coordinate_direction[GeometryInfo<2>::faces_per_cell] = {1,
+                                                                      1,
+                                                                      0,
+                                                                      0};
+      std::vector<Vector<double>> values(fe_values.n_quadrature_points,
+                                         Vector<double>(2));
+      Vector<double>              dof_values(fe.dofs_per_face);
+
+      // Get the values of the boundary function at the quadrature points.
+      {
+        const std::vector<Point<2>> &quadrature_points =
+          fe_values.get_quadrature_points();
+
+        boundary_function.vector_value_list(quadrature_points, values);
+      }
+
+      for (unsigned int q_point = 0; q_point < fe_values.n_quadrature_points;
+           ++q_point)
+        {
+          double tmp = 0.0;
+
+          for (unsigned int d = 0; d < 2; ++d)
+            tmp += normals[q_point][d] * values[q_point](d);
+
+          tmp *=
+            fe_values.JxW(q_point) *
+            std::sqrt(jacobians[q_point][0][face_coordinate_direction[face]] *
+                        jacobians[q_point][0][face_coordinate_direction[face]] +
+                      jacobians[q_point][1][face_coordinate_direction[face]] *
+                        jacobians[q_point][1][face_coordinate_direction[face]]);
+
+          for (unsigned int i = 0; i < fe.dofs_per_face; ++i)
+            dof_values(i) +=
+              tmp * (normals[q_point] *
+                     fe_values[vec].value(
+                       fe.face_to_cell_index(i,
+                                             face,
+                                             cell->face_orientation(face),
+                                             cell->face_flip(face),
+                                             cell->face_rotation(face)),
+                       q_point));
+        }
+
+      std::vector<types::global_dof_index> face_dof_indices(fe.dofs_per_face);
+
+      cell->face(face)->get_dof_indices(face_dof_indices,
+                                        cell->active_fe_index());
+
+      // Copy the computed values in the AffineConstraints only, if the degree
+      // of freedom is not already constrained.
+      for (unsigned int i = 0; i < fe.dofs_per_face; ++i)
+        if (!(constraints.is_constrained(face_dof_indices[i])) &&
+            fe.get_nonzero_components(fe.face_to_cell_index(
+              i,
+              face,
+              cell->face_orientation(face),
+              cell->face_flip(face),
+              cell->face_rotation(face)))[first_vector_component])
+          {
+            constraints.add_line(face_dof_indices[i]);
+
+            if (std::abs(dof_values(i)) > 1e-14)
+              constraints.set_inhomogeneity(face_dof_indices[i], dof_values(i));
+          }
+    }
+
+    // dummy implementation of above function for all other dimensions
+    template <int dim, typename cell_iterator>
+    void
+    compute_face_projection_div_conforming(
+      const cell_iterator &,
+      const unsigned int,
+      const FEFaceValues<dim> &,
+      const unsigned int,
+      const Function<dim> &,
+      const std::vector<DerivativeForm<1, dim, dim>> &,
+      AffineConstraints<double> &)
+    {
+      Assert(false, ExcNotImplemented());
+    }
+
+    // This function computes the projection of the boundary function on the
+    // boundary in 3d.
+    template <typename cell_iterator>
+    void
+    compute_face_projection_div_conforming(
+      const cell_iterator &                       cell,
+      const unsigned int                          face,
+      const FEFaceValues<3> &                     fe_values,
+      const unsigned int                          first_vector_component,
+      const Function<3> &                         boundary_function,
+      const std::vector<DerivativeForm<1, 3, 3>> &jacobians,
+      std::vector<double> &                       dof_values,
+      std::vector<types::global_dof_index> &      projected_dofs)
+    {
+      // Compute the intergral over the product of the normal components of
+      // the boundary function times the normal components of the shape
+      // functions supported on the boundary.
+      const FEValuesExtractors::Vector vec(first_vector_component);
+      const FiniteElement<3> &         fe      = cell->get_fe();
+      const std::vector<Tensor<1, 3>> &normals = fe_values.get_normal_vectors();
+      const unsigned int
+        face_coordinate_directions[GeometryInfo<3>::faces_per_cell][2] = {
+          {1, 2}, {1, 2}, {2, 0}, {2, 0}, {0, 1}, {0, 1}};
+      std::vector<Vector<double>> values(fe_values.n_quadrature_points,
+                                         Vector<double>(3));
+      Vector<double>              dof_values_local(fe.dofs_per_face);
+
+      {
+        const std::vector<Point<3>> &quadrature_points =
+          fe_values.get_quadrature_points();
+
+        boundary_function.vector_value_list(quadrature_points, values);
+      }
+
+      for (unsigned int q_point = 0; q_point < fe_values.n_quadrature_points;
+           ++q_point)
+        {
+          double tmp = 0.0;
+
+          for (unsigned int d = 0; d < 3; ++d)
+            tmp += normals[q_point][d] * values[q_point](d);
+
+          tmp *=
+            fe_values.JxW(q_point) *
+            std::sqrt(
+              (jacobians[q_point][0][face_coordinate_directions[face][0]] *
+                 jacobians[q_point][0][face_coordinate_directions[face][0]] +
+               jacobians[q_point][1][face_coordinate_directions[face][0]] *
+                 jacobians[q_point][1][face_coordinate_directions[face][0]] +
+               jacobians[q_point][2][face_coordinate_directions[face][0]] *
+                 jacobians[q_point][2][face_coordinate_directions[face][0]]) *
+              (jacobians[q_point][0][face_coordinate_directions[face][1]] *
+                 jacobians[q_point][0][face_coordinate_directions[face][1]] +
+               jacobians[q_point][1][face_coordinate_directions[face][1]] *
+                 jacobians[q_point][1][face_coordinate_directions[face][1]] +
+               jacobians[q_point][2][face_coordinate_directions[face][1]] *
+                 jacobians[q_point][2][face_coordinate_directions[face][1]]));
+
+          for (unsigned int i = 0; i < fe.dofs_per_face; ++i)
+            dof_values_local(i) +=
+              tmp * (normals[q_point] *
+                     fe_values[vec].value(
+                       fe.face_to_cell_index(i,
+                                             face,
+                                             cell->face_orientation(face),
+                                             cell->face_flip(face),
+                                             cell->face_rotation(face)),
+                       q_point));
+        }
+
+      std::vector<types::global_dof_index> face_dof_indices(fe.dofs_per_face);
+
+      cell->face(face)->get_dof_indices(face_dof_indices,
+                                        cell->active_fe_index());
+
+      for (unsigned int i = 0; i < fe.dofs_per_face; ++i)
+        if (projected_dofs[face_dof_indices[i]] < fe.degree &&
+            fe.get_nonzero_components(fe.face_to_cell_index(
+              i,
+              face,
+              cell->face_orientation(face),
+              cell->face_flip(face),
+              cell->face_rotation(face)))[first_vector_component])
+          {
+            dof_values[face_dof_indices[i]]     = dof_values_local(i);
+            projected_dofs[face_dof_indices[i]] = fe.degree;
+          }
+    }
+
+    // dummy implementation of above
+    // function for all other
+    // dimensions
+    template <int dim, typename cell_iterator>
+    void
+    compute_face_projection_div_conforming(
+      const cell_iterator &,
+      const unsigned int,
+      const FEFaceValues<dim> &,
+      const unsigned int,
+      const Function<dim> &,
+      const std::vector<DerivativeForm<1, dim, dim>> &,
+      std::vector<double> &,
+      std::vector<types::global_dof_index> &)
+    {
+      Assert(false, ExcNotImplemented());
+    }
+  } // namespace internals
+
+
+  template <int dim>
+  void
+  project_boundary_values_div_conforming(
+    const DoFHandler<dim> &    dof_handler,
+    const unsigned int         first_vector_component,
+    const Function<dim> &      boundary_function,
+    const types::boundary_id   boundary_component,
+    AffineConstraints<double> &constraints,
+    const Mapping<dim> &       mapping)
+  {
+    const unsigned int spacedim = dim;
+    // Interpolate the normal components
+    // of the boundary functions. Since
+    // the Raviart-Thomas elements are
+    // constructed from a Lagrangian
+    // basis, it suffices to compute
+    // the integral over the product
+    // of the normal components of the
+    // boundary function times the
+    // normal components of the shape
+    // functions supported on the
+    // boundary.
+    const FiniteElement<dim> &       fe = dof_handler.get_fe();
+    QGauss<dim - 1>                  face_quadrature(fe.degree + 1);
+    FEFaceValues<dim>                fe_face_values(mapping,
+                                     fe,
+                                     face_quadrature,
+                                     update_JxW_values | update_normal_vectors |
+                                       update_quadrature_points |
+                                       update_values);
+    hp::FECollection<dim>            fe_collection(fe);
+    const hp::MappingCollection<dim> mapping_collection(mapping);
+    hp::QCollection<dim>             quadrature_collection;
+
+    for (unsigned int face : GeometryInfo<dim>::face_indices())
+      quadrature_collection.push_back(
+        QProjector<dim>::project_to_face(face_quadrature, face));
+
+    hp::FEValues<dim> fe_values(mapping_collection,
+                                fe_collection,
+                                quadrature_collection,
+                                update_jacobians);
+
+    switch (dim)
+      {
+        case 2:
+          {
+            for (const auto &cell : dof_handler.active_cell_iterators())
+              if (cell->at_boundary() && cell->is_locally_owned())
+                for (const unsigned int face :
+                     GeometryInfo<dim>::face_indices())
+                  if (cell->face(face)->boundary_id() == boundary_component)
+                    {
+                      // if the FE is a
+                      // FE_Nothing object
+                      // there is no work to
+                      // do
+                      if (dynamic_cast<const FE_Nothing<dim> *>(
+                            &cell->get_fe()) != nullptr)
+                        return;
+
+                      // This is only
+                      // implemented, if the
+                      // FE is a Raviart-Thomas
+                      // element. If the FE is
+                      // a FESystem we cannot
+                      // check this.
+                      if (dynamic_cast<const FESystem<dim> *>(
+                            &cell->get_fe()) == nullptr)
+                        {
+                          AssertThrow(
+                            dynamic_cast<const FE_RaviartThomas<dim> *>(
+                              &cell->get_fe()) != nullptr,
+                            typename FiniteElement<
+                              dim>::ExcInterpolationNotImplemented());
+                        }
+
+                      fe_values.reinit(cell,
+                                       face +
+                                         cell->active_fe_index() *
+                                           GeometryInfo<dim>::faces_per_cell);
+
+                      const std::vector<DerivativeForm<1, dim, spacedim>>
+                        &jacobians =
+                          fe_values.get_present_fe_values().get_jacobians();
+
+                      fe_face_values.reinit(cell, face);
+                      internals::compute_face_projection_div_conforming(
+                        cell,
+                        face,
+                        fe_face_values,
+                        first_vector_component,
+                        boundary_function,
+                        jacobians,
+                        constraints);
+                    }
+
+            break;
+          }
+
+        case 3:
+          {
+            // In three dimensions the edges between two faces are treated
+            // twice. Therefore we store the computed values in a vector
+            // and copy them over in the AffineConstraints after all values
+            // have been computed. If we have two values for one edge, we
+            // choose the one, which was computed with the higher order
+            // element. If both elements are of the same order, we just
+            // keep the first value and do not compute a second one.
+            const unsigned int                   n_dofs = dof_handler.n_dofs();
+            std::vector<double>                  dof_values(n_dofs);
+            std::vector<types::global_dof_index> projected_dofs(n_dofs);
+
+            for (unsigned int dof = 0; dof < n_dofs; ++dof)
+              projected_dofs[dof] = 0;
+
+            for (const auto &cell : dof_handler.active_cell_iterators())
+              if (cell->at_boundary() && cell->is_locally_owned())
+                for (const unsigned int face :
+                     GeometryInfo<dim>::face_indices())
+                  if (cell->face(face)->boundary_id() == boundary_component)
+                    {
+                      // This is only implemented, if the FE is a
+                      // Raviart-Thomas element. If the FE is a FESystem we
+                      // cannot check this.
+                      if (dynamic_cast<const FESystem<dim> *>(
+                            &cell->get_fe()) == nullptr)
+                        {
+                          AssertThrow(
+                            dynamic_cast<const FE_RaviartThomas<dim> *>(
+                              &cell->get_fe()) != nullptr,
+                            typename FiniteElement<
+                              dim>::ExcInterpolationNotImplemented());
+                        }
+
+                      fe_values.reinit(cell,
+                                       face +
+                                         cell->active_fe_index() *
+                                           GeometryInfo<dim>::faces_per_cell);
+
+                      const std::vector<DerivativeForm<1, dim, spacedim>>
+                        &jacobians =
+                          fe_values.get_present_fe_values().get_jacobians();
+
+                      fe_face_values.reinit(cell, face);
+                      internals::compute_face_projection_div_conforming(
+                        cell,
+                        face,
+                        fe_face_values,
+                        first_vector_component,
+                        boundary_function,
+                        jacobians,
+                        dof_values,
+                        projected_dofs);
+                    }
+
+            for (unsigned int dof = 0; dof < n_dofs; ++dof)
+              if ((projected_dofs[dof] != 0) &&
+                  !(constraints.is_constrained(dof)))
+                {
+                  constraints.add_line(dof);
+
+                  if (std::abs(dof_values[dof]) > 1e-14)
+                    constraints.set_inhomogeneity(dof, dof_values[dof]);
+                }
+
+            break;
+          }
+
+        default:
+          Assert(false, ExcNotImplemented());
+      }
+  }
+
+
+  template <int dim>
+  void
+  project_boundary_values_div_conforming(
+    const hp::DoFHandler<dim> &            dof_handler,
+    const unsigned int                     first_vector_component,
+    const Function<dim> &                  boundary_function,
+    const types::boundary_id               boundary_component,
+    AffineConstraints<double> &            constraints,
+    const hp::MappingCollection<dim, dim> &mapping_collection)
+  {
+    const unsigned int           spacedim = dim;
+    const hp::FECollection<dim> &fe_collection =
+      dof_handler.get_fe_collection();
+    hp::QCollection<dim - 1> face_quadrature_collection;
+    hp::QCollection<dim>     quadrature_collection;
+
+    for (unsigned int i = 0; i < fe_collection.size(); ++i)
+      {
+        const QGauss<dim - 1> quadrature(fe_collection[i].degree + 1);
+
+        face_quadrature_collection.push_back(quadrature);
+
+        for (unsigned int face : GeometryInfo<dim>::face_indices())
+          quadrature_collection.push_back(
+            QProjector<dim>::project_to_face(quadrature, face));
+      }
+
+    hp::FEFaceValues<dim> fe_face_values(mapping_collection,
+                                         fe_collection,
+                                         face_quadrature_collection,
+                                         update_JxW_values |
+                                           update_normal_vectors |
+                                           update_quadrature_points |
+                                           update_values);
+    hp::FEValues<dim>     fe_values(mapping_collection,
+                                fe_collection,
+                                quadrature_collection,
+                                update_jacobians);
+
+    switch (dim)
+      {
+        case 2:
+          {
+            for (const auto &cell : dof_handler.active_cell_iterators())
+              if (cell->at_boundary() && cell->is_locally_owned())
+                for (const unsigned int face :
+                     GeometryInfo<dim>::face_indices())
+                  if (cell->face(face)->boundary_id() == boundary_component)
+                    {
+                      // This is only
+                      // implemented, if the
+                      // FE is a Raviart-Thomas
+                      // element. If the FE is
+                      // a FESystem we cannot
+                      // check this.
+                      if (dynamic_cast<const FESystem<dim> *>(
+                            &cell->get_fe()) == nullptr)
+                        {
+                          AssertThrow(
+                            dynamic_cast<const FE_RaviartThomas<dim> *>(
+                              &cell->get_fe()) != nullptr,
+                            typename FiniteElement<
+                              dim>::ExcInterpolationNotImplemented());
+                        }
+
+                      fe_values.reinit(cell,
+                                       face +
+                                         cell->active_fe_index() *
+                                           GeometryInfo<dim>::faces_per_cell);
+
+                      const std::vector<DerivativeForm<1, dim, spacedim>>
+                        &jacobians =
+                          fe_values.get_present_fe_values().get_jacobians();
+
+                      fe_face_values.reinit(cell, face);
+                      internals::compute_face_projection_div_conforming(
+                        cell,
+                        face,
+                        fe_face_values.get_present_fe_values(),
+                        first_vector_component,
+                        boundary_function,
+                        jacobians,
+                        constraints);
+                    }
+
+            break;
+          }
+
+        case 3:
+          {
+            const unsigned int                   n_dofs = dof_handler.n_dofs();
+            std::vector<double>                  dof_values(n_dofs);
+            std::vector<types::global_dof_index> projected_dofs(n_dofs);
+
+            for (unsigned int dof = 0; dof < n_dofs; ++dof)
+              projected_dofs[dof] = 0;
+
+            for (const auto &cell : dof_handler.active_cell_iterators())
+              if (cell->at_boundary() && cell->is_locally_owned())
+                for (const unsigned int face :
+                     GeometryInfo<dim>::face_indices())
+                  if (cell->face(face)->boundary_id() == boundary_component)
+                    {
+                      // This is only
+                      // implemented, if the
+                      // FE is a Raviart-Thomas
+                      // element. If the FE is
+                      // a FESystem we cannot
+                      // check this.
+                      if (dynamic_cast<const FESystem<dim> *>(
+                            &cell->get_fe()) == nullptr)
+                        {
+                          AssertThrow(
+                            dynamic_cast<const FE_RaviartThomas<dim> *>(
+                              &cell->get_fe()) != nullptr,
+                            typename FiniteElement<
+                              dim>::ExcInterpolationNotImplemented());
+                        }
+
+                      fe_values.reinit(cell,
+                                       face +
+                                         cell->active_fe_index() *
+                                           GeometryInfo<dim>::faces_per_cell);
+
+                      const std::vector<DerivativeForm<1, dim, spacedim>>
+                        &jacobians =
+                          fe_values.get_present_fe_values().get_jacobians();
+
+                      fe_face_values.reinit(cell, face);
+                      internals::compute_face_projection_div_conforming(
+                        cell,
+                        face,
+                        fe_face_values.get_present_fe_values(),
+                        first_vector_component,
+                        boundary_function,
+                        jacobians,
+                        dof_values,
+                        projected_dofs);
+                    }
+
+            for (unsigned int dof = 0; dof < n_dofs; ++dof)
+              if ((projected_dofs[dof] != 0) &&
+                  !(constraints.is_constrained(dof)))
+                {
+                  constraints.add_line(dof);
+
+                  if (std::abs(dof_values[dof]) > 1e-14)
+                    constraints.set_inhomogeneity(dof, dof_values[dof]);
+                }
+
+            break;
+          }
+
+        default:
+          Assert(false, ExcNotImplemented());
+      }
+  }
+} // namespace VectorTools
+
+DEAL_II_NAMESPACE_CLOSE
+
+#endif // dealii_vector_tools_boundary_templates_h
diff --git a/include/deal.II/numerics/vector_tools_constraints.templates.h b/include/deal.II/numerics/vector_tools_constraints.templates.h
new file mode 100644 (file)
index 0000000..ea4bc8f
--- /dev/null
@@ -0,0 +1,1306 @@
+// ---------------------------------------------------------------------
+//
+// Copyright (C) 1998 - 2014 by the deal.II authors
+//
+// This file is part of the deal.II library.
+//
+// The deal.II library is free software; you can use it, redistribute
+// it, and/or modify it under the terms of the GNU Lesser General
+// Public License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+// The full text of the license can be found in the file LICENSE.md at
+// the top level directory of deal.II.
+//
+// ---------------------------------------------------------------------
+
+
+#ifndef dealii_vector_tools_constraints_templates_h
+#define dealii_vector_tools_constraints_templates_h
+
+#include <deal.II/grid/manifold.h>
+
+#include <deal.II/hp/fe_values.h>
+
+#include <deal.II/numerics/vector_tools.h>
+
+DEAL_II_NAMESPACE_OPEN
+
+namespace VectorTools
+{
+  namespace internal
+  {
+    /**
+     * A structure that stores the dim DoF indices that correspond to a
+     * vector-valued quantity at a single support point.
+     */
+    template <int dim>
+    struct VectorDoFTuple
+    {
+      types::global_dof_index dof_indices[dim];
+
+      VectorDoFTuple()
+      {
+        for (unsigned int i = 0; i < dim; ++i)
+          dof_indices[i] = numbers::invalid_dof_index;
+      }
+
+
+      bool
+      operator<(const VectorDoFTuple<dim> &other) const
+      {
+        for (unsigned int i = 0; i < dim; ++i)
+          if (dof_indices[i] < other.dof_indices[i])
+            return true;
+          else if (dof_indices[i] > other.dof_indices[i])
+            return false;
+        return false;
+      }
+
+      bool
+      operator==(const VectorDoFTuple<dim> &other) const
+      {
+        for (unsigned int i = 0; i < dim; ++i)
+          if (dof_indices[i] != other.dof_indices[i])
+            return false;
+
+        return true;
+      }
+
+      bool
+      operator!=(const VectorDoFTuple<dim> &other) const
+      {
+        return !(*this == other);
+      }
+    };
+
+
+    template <int dim>
+    std::ostream &
+    operator<<(std::ostream &out, const VectorDoFTuple<dim> &vdt)
+    {
+      for (unsigned int d = 0; d < dim; ++d)
+        out << vdt.dof_indices[d] << (d < dim - 1 ? " " : "");
+      return out;
+    }
+
+
+
+    /**
+     * Add the constraint $\vec n \cdot \vec u = inhom$ to the list of
+     * constraints.
+     *
+     * Here, $\vec u$ is represented by the set of given DoF indices, and
+     * $\vec n$ by the vector specified as the second argument.
+     *
+     * The function does not add constraints if a degree of freedom is already
+     * constrained in the constraints object.
+     */
+    template <int dim>
+    void
+    add_constraint(const VectorDoFTuple<dim> &dof_indices,
+                   const Tensor<1, dim> &     constraining_vector,
+                   AffineConstraints<double> &constraints,
+                   const double               inhomogeneity = 0)
+    {
+      // choose the DoF that has the largest component in the
+      // constraining_vector as the one to be constrained as this makes the
+      // process stable in cases where the constraining_vector has the form
+      // n=(1,0) or n=(0,1)
+      //
+      // we get constraints of the form x0 = a_1*x1 + a2*x2 + ... if one of
+      // the weights is essentially zero then skip this part. the
+      // AffineConstraints can also deal with cases like x0 = 0 if
+      // necessary
+      //
+      // there is a problem if we have a normal vector of the form
+      // (a,a,small) or (a,a,a). Depending on round-off we may choose the
+      // first or second component (or third, in the latter case) as the
+      // largest one, and depending on our choice one or another degree of
+      // freedom will be constrained. On a single processor this is not
+      // much of a problem, but it's a nightmare when we run in parallel
+      // and two processors disagree on which DoF should be constrained.
+      // This led to an incredibly difficult to find bug in step-32 when
+      // running in parallel with 9 or more processors.
+      //
+      // in practice, such normal vectors of the form (a,a,small) or
+      // (a,a,a) happen not infrequently since they lie on the diagonals
+      // where vertices frequently happen to land upon mesh refinement if
+      // one starts from a symmetric and regular body. we work around this
+      // problem in the following way: if we have a normal vector of the
+      // form (a,b) (similarly algorithm in 3d), we choose 'a' as the
+      // largest coefficient not if a>b but if a>b+1e-10. this shifts the
+      // problem away from the frequently visited diagonal to a line that
+      // is off the diagonal. there will of course be problems where the
+      // exact values of a and b differ by exactly 1e-10 and we get into
+      // the same instability, but from a practical viewpoint such problems
+      // should be much rarer. in particular, meshes have to be very fine
+      // for a vertex to land on this line if the original body had a
+      // vertex on the diagonal as well
+      switch (dim)
+        {
+          case 2:
+            {
+              if (std::fabs(constraining_vector[0]) >
+                  std::fabs(constraining_vector[1]) + 1e-10)
+                {
+                  if (!constraints.is_constrained(dof_indices.dof_indices[0]) &&
+                      constraints.can_store_line(dof_indices.dof_indices[0]))
+                    {
+                      constraints.add_line(dof_indices.dof_indices[0]);
+
+                      if (std::fabs(constraining_vector[1] /
+                                    constraining_vector[0]) >
+                          std::numeric_limits<double>::epsilon())
+                        constraints.add_entry(dof_indices.dof_indices[0],
+                                              dof_indices.dof_indices[1],
+                                              -constraining_vector[1] /
+                                                constraining_vector[0]);
+
+                      if (std::fabs(inhomogeneity / constraining_vector[0]) >
+                          std::numeric_limits<double>::epsilon())
+                        constraints.set_inhomogeneity(
+                          dof_indices.dof_indices[0],
+                          inhomogeneity / constraining_vector[0]);
+                    }
+                }
+              else
+                {
+                  if (!constraints.is_constrained(dof_indices.dof_indices[1]) &&
+                      constraints.can_store_line(dof_indices.dof_indices[1]))
+                    {
+                      constraints.add_line(dof_indices.dof_indices[1]);
+
+                      if (std::fabs(constraining_vector[0] /
+                                    constraining_vector[1]) >
+                          std::numeric_limits<double>::epsilon())
+                        constraints.add_entry(dof_indices.dof_indices[1],
+                                              dof_indices.dof_indices[0],
+                                              -constraining_vector[0] /
+                                                constraining_vector[1]);
+
+                      if (std::fabs(inhomogeneity / constraining_vector[1]) >
+                          std::numeric_limits<double>::epsilon())
+                        constraints.set_inhomogeneity(
+                          dof_indices.dof_indices[1],
+                          inhomogeneity / constraining_vector[1]);
+                    }
+                }
+              break;
+            }
+
+          case 3:
+            {
+              if ((std::fabs(constraining_vector[0]) >=
+                   std::fabs(constraining_vector[1]) + 1e-10) &&
+                  (std::fabs(constraining_vector[0]) >=
+                   std::fabs(constraining_vector[2]) + 2e-10))
+                {
+                  if (!constraints.is_constrained(dof_indices.dof_indices[0]) &&
+                      constraints.can_store_line(dof_indices.dof_indices[0]))
+                    {
+                      constraints.add_line(dof_indices.dof_indices[0]);
+
+                      if (std::fabs(constraining_vector[1] /
+                                    constraining_vector[0]) >
+                          std::numeric_limits<double>::epsilon())
+                        constraints.add_entry(dof_indices.dof_indices[0],
+                                              dof_indices.dof_indices[1],
+                                              -constraining_vector[1] /
+                                                constraining_vector[0]);
+
+                      if (std::fabs(constraining_vector[2] /
+                                    constraining_vector[0]) >
+                          std::numeric_limits<double>::epsilon())
+                        constraints.add_entry(dof_indices.dof_indices[0],
+                                              dof_indices.dof_indices[2],
+                                              -constraining_vector[2] /
+                                                constraining_vector[0]);
+
+                      if (std::fabs(inhomogeneity / constraining_vector[0]) >
+                          std::numeric_limits<double>::epsilon())
+                        constraints.set_inhomogeneity(
+                          dof_indices.dof_indices[0],
+                          inhomogeneity / constraining_vector[0]);
+                    }
+                }
+              else if ((std::fabs(constraining_vector[1]) + 1e-10 >=
+                        std::fabs(constraining_vector[0])) &&
+                       (std::fabs(constraining_vector[1]) >=
+                        std::fabs(constraining_vector[2]) + 1e-10))
+                {
+                  if (!constraints.is_constrained(dof_indices.dof_indices[1]) &&
+                      constraints.can_store_line(dof_indices.dof_indices[1]))
+                    {
+                      constraints.add_line(dof_indices.dof_indices[1]);
+
+                      if (std::fabs(constraining_vector[0] /
+                                    constraining_vector[1]) >
+                          std::numeric_limits<double>::epsilon())
+                        constraints.add_entry(dof_indices.dof_indices[1],
+                                              dof_indices.dof_indices[0],
+                                              -constraining_vector[0] /
+                                                constraining_vector[1]);
+
+                      if (std::fabs(constraining_vector[2] /
+                                    constraining_vector[1]) >
+                          std::numeric_limits<double>::epsilon())
+                        constraints.add_entry(dof_indices.dof_indices[1],
+                                              dof_indices.dof_indices[2],
+                                              -constraining_vector[2] /
+                                                constraining_vector[1]);
+
+                      if (std::fabs(inhomogeneity / constraining_vector[1]) >
+                          std::numeric_limits<double>::epsilon())
+                        constraints.set_inhomogeneity(
+                          dof_indices.dof_indices[1],
+                          inhomogeneity / constraining_vector[1]);
+                    }
+                }
+              else
+                {
+                  if (!constraints.is_constrained(dof_indices.dof_indices[2]) &&
+                      constraints.can_store_line(dof_indices.dof_indices[2]))
+                    {
+                      constraints.add_line(dof_indices.dof_indices[2]);
+
+                      if (std::fabs(constraining_vector[0] /
+                                    constraining_vector[2]) >
+                          std::numeric_limits<double>::epsilon())
+                        constraints.add_entry(dof_indices.dof_indices[2],
+                                              dof_indices.dof_indices[0],
+                                              -constraining_vector[0] /
+                                                constraining_vector[2]);
+
+                      if (std::fabs(constraining_vector[1] /
+                                    constraining_vector[2]) >
+                          std::numeric_limits<double>::epsilon())
+                        constraints.add_entry(dof_indices.dof_indices[2],
+                                              dof_indices.dof_indices[1],
+                                              -constraining_vector[1] /
+                                                constraining_vector[2]);
+
+                      if (std::fabs(inhomogeneity / constraining_vector[2]) >
+                          std::numeric_limits<double>::epsilon())
+                        constraints.set_inhomogeneity(
+                          dof_indices.dof_indices[2],
+                          inhomogeneity / constraining_vector[2]);
+                    }
+                }
+
+              break;
+            }
+
+          default:
+            Assert(false, ExcNotImplemented());
+        }
+    }
+
+
+    /**
+     * Add the constraint $(\vec u-\vec u_\Gamma) \| \vec t$ to the list of
+     * constraints. In 2d, this is a single constraint, in 3d these are two
+     * constraints.
+     *
+     * Here, $\vec u$ is represented by the set of given DoF indices, and
+     * $\vec t$ by the vector specified as the second argument.
+     *
+     * The function does not add constraints if a degree of freedom is already
+     * constrained in the constraints object.
+     */
+    template <int dim>
+    void
+    add_tangentiality_constraints(
+      const VectorDoFTuple<dim> &dof_indices,
+      const Tensor<1, dim> &     tangent_vector,
+      AffineConstraints<double> &constraints,
+      const Vector<double> &     b_values = Vector<double>(dim))
+    {
+      // choose the DoF that has the
+      // largest component in the
+      // tangent_vector as the
+      // independent component, and
+      // then constrain the others to
+      // it. specifically, if, say,
+      // component 0 of the tangent
+      // vector t is largest by
+      // magnitude, then
+      // x1=(b[1]*t[0]-b[0]*t[1])/t[0]+t[1]/t[0]*x_0, etc.
+      unsigned int largest_component = 0;
+      for (unsigned int d = 1; d < dim; ++d)
+        if (std::fabs(tangent_vector[d]) >
+            std::fabs(tangent_vector[largest_component]) + 1e-10)
+          largest_component = d;
+
+      // then constrain all of the
+      // other degrees of freedom in
+      // terms of the one just found
+      for (unsigned int d = 0; d < dim; ++d)
+        if (d != largest_component)
+          if (!constraints.is_constrained(dof_indices.dof_indices[d]) &&
+              constraints.can_store_line(dof_indices.dof_indices[d]))
+            {
+              constraints.add_line(dof_indices.dof_indices[d]);
+
+              if (std::fabs(tangent_vector[d] /
+                            tangent_vector[largest_component]) >
+                  std::numeric_limits<double>::epsilon())
+                constraints.add_entry(
+                  dof_indices.dof_indices[d],
+                  dof_indices.dof_indices[largest_component],
+                  tangent_vector[d] / tangent_vector[largest_component]);
+
+              const double inhomogeneity =
+                (b_values(d) * tangent_vector[largest_component] -
+                 b_values(largest_component) * tangent_vector[d]) /
+                tangent_vector[largest_component];
+
+              if (std::fabs(inhomogeneity) >
+                  std::numeric_limits<double>::epsilon())
+                constraints.set_inhomogeneity(dof_indices.dof_indices[d],
+                                              inhomogeneity);
+            }
+    }
+
+
+
+    /**
+     * Given a vector, compute a set of dim-1 vectors that are orthogonal to
+     * the first one and mutually orthonormal as well.
+     */
+    template <int dim>
+    void
+    compute_orthonormal_vectors(const Tensor<1, dim> &vector,
+                                Tensor<1, dim> (&orthonormals)[dim - 1])
+    {
+      switch (dim)
+        {
+          case 3:
+            {
+              // to do this in 3d, take
+              // one vector that is
+              // guaranteed to be not
+              // aligned with the
+              // average tangent and
+              // form the cross
+              // product. this yields
+              // one vector that is
+              // certainly
+              // perpendicular to the
+              // tangent; then take the
+              // cross product between
+              // this vector and the
+              // tangent and get one
+              // vector that is
+              // perpendicular to both
+
+              // construct a
+              // temporary vector
+              // by swapping the
+              // larger two
+              // components and
+              // flipping one
+              // sign; this can
+              // not be collinear
+              // with the average
+              // tangent
+              Tensor<1, dim> tmp = vector;
+              if ((std::fabs(tmp[0]) > std::fabs(tmp[1])) &&
+                  (std::fabs(tmp[0]) > std::fabs(tmp[2])))
+                {
+                  // entry zero
+                  // is the
+                  // largest
+                  if ((std::fabs(tmp[1]) > std::fabs(tmp[2])))
+                    std::swap(tmp[0], tmp[1]);
+                  else
+                    std::swap(tmp[0], tmp[2]);
+
+                  tmp[0] *= -1;
+                }
+              else if ((std::fabs(tmp[1]) > std::fabs(tmp[0])) &&
+                       (std::fabs(tmp[1]) > std::fabs(tmp[2])))
+                {
+                  // entry one
+                  // is the
+                  // largest
+                  if ((std::fabs(tmp[0]) > std::fabs(tmp[2])))
+                    std::swap(tmp[1], tmp[0]);
+                  else
+                    std::swap(tmp[1], tmp[2]);
+
+                  tmp[1] *= -1;
+                }
+              else
+                {
+                  // entry two
+                  // is the
+                  // largest
+                  if ((std::fabs(tmp[0]) > std::fabs(tmp[1])))
+                    std::swap(tmp[2], tmp[0]);
+                  else
+                    std::swap(tmp[2], tmp[1]);
+
+                  tmp[2] *= -1;
+                }
+
+              // make sure the two vectors
+              // are indeed not collinear
+              Assert(std::fabs(vector * tmp / vector.norm() / tmp.norm()) <
+                       (1 - 1e-12),
+                     ExcInternalError());
+
+              // now compute the
+              // two normals
+              orthonormals[0] = cross_product_3d(vector, tmp);
+              orthonormals[1] = cross_product_3d(vector, orthonormals[0]);
+
+              break;
+            }
+
+          default:
+            Assert(false, ExcNotImplemented());
+        }
+    }
+  } // namespace internal
+
+
+  template <int dim, int spacedim, template <int, int> class DoFHandlerType>
+  void
+  compute_nonzero_normal_flux_constraints(
+    const DoFHandlerType<dim, spacedim> &dof_handler,
+    const unsigned int                   first_vector_component,
+    const std::set<types::boundary_id> & boundary_ids,
+    const std::map<types::boundary_id, const Function<spacedim> *>
+      &                           function_map,
+    AffineConstraints<double> &   constraints,
+    const Mapping<dim, spacedim> &mapping)
+  {
+    Assert(dim > 1,
+           ExcMessage("This function is not useful in 1d because it amounts "
+                      "to imposing Dirichlet values on the vector-valued "
+                      "quantity."));
+
+    std::vector<types::global_dof_index> face_dofs;
+
+    // create FE and mapping collections for all elements in use by this
+    // DoFHandler
+    const hp::FECollection<dim, spacedim> &fe_collection =
+      dof_handler.get_fe_collection();
+    hp::MappingCollection<dim, spacedim> mapping_collection;
+    for (unsigned int i = 0; i < fe_collection.size(); ++i)
+      mapping_collection.push_back(mapping);
+
+    // now also create a quadrature collection for the faces of a cell. fill
+    // it with a quadrature formula with the support points on faces for each
+    // FE
+    hp::QCollection<dim - 1> face_quadrature_collection;
+    for (unsigned int i = 0; i < fe_collection.size(); ++i)
+      {
+        const std::vector<Point<dim - 1>> &unit_support_points =
+          fe_collection[i].get_unit_face_support_points();
+
+        Assert(unit_support_points.size() == fe_collection[i].dofs_per_face,
+               ExcInternalError());
+
+        face_quadrature_collection.push_back(
+          Quadrature<dim - 1>(unit_support_points));
+      }
+
+    // now create the object with which we will generate the normal vectors
+    hp::FEFaceValues<dim, spacedim> x_fe_face_values(mapping_collection,
+                                                     fe_collection,
+                                                     face_quadrature_collection,
+                                                     update_quadrature_points |
+                                                       update_normal_vectors);
+
+    // have a map that stores normal vectors for each vector-dof tuple we want
+    // to constrain. since we can get at the same vector dof tuple more than
+    // once (for example if it is located at a vertex that we visit from all
+    // adjacent cells), we will want to average later on the normal vectors
+    // computed on different cells as described in the documentation of this
+    // function. however, we can only average if the contributions came from
+    // different cells, whereas we want to constrain twice or more in case the
+    // contributions came from different faces of the same cell
+    // (i.e. constrain not just the *average normal direction* but *all normal
+    // directions* we find). consequently, we also have to store which cell a
+    // normal vector was computed on
+    using DoFToNormalsMap = std::multimap<
+      internal::VectorDoFTuple<dim>,
+      std::pair<Tensor<1, dim>,
+                typename DoFHandlerType<dim, spacedim>::active_cell_iterator>>;
+    std::map<internal::VectorDoFTuple<dim>, Vector<double>>
+      dof_vector_to_b_values;
+
+    DoFToNormalsMap dof_to_normals_map;
+
+    // now loop over all cells and all faces
+    typename DoFHandlerType<dim, spacedim>::active_cell_iterator
+      cell = dof_handler.begin_active(),
+      endc = dof_handler.end();
+    std::set<types::boundary_id>::iterator b_id;
+    for (; cell != endc; ++cell)
+      if (!cell->is_artificial())
+        for (const unsigned int face_no : GeometryInfo<dim>::face_indices())
+          if ((b_id = boundary_ids.find(cell->face(face_no)->boundary_id())) !=
+              boundary_ids.end())
+            {
+              const FiniteElement<dim> &fe = cell->get_fe();
+              typename DoFHandlerType<dim, spacedim>::face_iterator face =
+                cell->face(face_no);
+
+              // get the indices of the dofs on this cell...
+              face_dofs.resize(fe.dofs_per_face);
+              face->get_dof_indices(face_dofs, cell->active_fe_index());
+
+              x_fe_face_values.reinit(cell, face_no);
+              const FEFaceValues<dim> &fe_values =
+                x_fe_face_values.get_present_fe_values();
+
+              // then identify which of them correspond to the selected set of
+              // vector components
+              for (unsigned int i = 0; i < face_dofs.size(); ++i)
+                if (fe.face_system_to_component_index(i).first ==
+                    first_vector_component)
+                  {
+                    // find corresponding other components of vector
+                    internal::VectorDoFTuple<dim> vector_dofs;
+                    vector_dofs.dof_indices[0] = face_dofs[i];
+
+                    Assert(
+                      first_vector_component + dim <= fe.n_components(),
+                      ExcMessage(
+                        "Error: the finite element does not have enough components "
+                        "to define a normal direction."));
+
+                    for (unsigned int k = 0; k < fe.dofs_per_face; ++k)
+                      if ((k != i) &&
+                          (face_quadrature_collection[cell->active_fe_index()]
+                             .point(k) ==
+                           face_quadrature_collection[cell->active_fe_index()]
+                             .point(i)) &&
+                          (fe.face_system_to_component_index(k).first >=
+                           first_vector_component) &&
+                          (fe.face_system_to_component_index(k).first <
+                           first_vector_component + dim))
+                        vector_dofs.dof_indices
+                          [fe.face_system_to_component_index(k).first -
+                           first_vector_component] = face_dofs[k];
+
+                    for (unsigned int d = 0; d < dim; ++d)
+                      Assert(vector_dofs.dof_indices[d] < dof_handler.n_dofs(),
+                             ExcInternalError());
+
+                    // we need the normal vector on this face. we know that it
+                    // is a vector of length 1 but at least with higher order
+                    // mappings it isn't always possible to guarantee that
+                    // each component is exact up to zero tolerance. in
+                    // particular, as shown in the deal.II/no_flux_06 test, if
+                    // we just take the normal vector as given by the
+                    // fe_values object, we can get entries in the normal
+                    // vectors of the unit cube that have entries up to
+                    // several times 1e-14.
+                    //
+                    // the problem with this is that this later yields
+                    // constraints that are circular (e.g., in the testcase,
+                    // we get constraints of the form
+                    //
+                    // x22 =  2.93099e-14*x21 + 2.93099e-14*x23
+                    // x21 = -2.93099e-14*x22 + 2.93099e-14*x21
+                    //
+                    // in both of these constraints, the small numbers should
+                    // be zero and the constraints should simply be
+                    // x22 = x21 = 0
+                    //
+                    // to achieve this, we utilize that we know that the
+                    // normal vector has (or should have) length 1 and that we
+                    // can simply set small elements to zero (without having
+                    // to check that they are small *relative to something
+                    // else*). we do this and then normalize the length of the
+                    // vector back to one, just to be on the safe side
+                    //
+                    // one more point: we would like to use the "real" normal
+                    // vector here, as provided by the boundary description
+                    // and as opposed to what we get from the FEValues object.
+                    // we do this in the immediately next line, but as is
+                    // obvious, the boundary only has a vague idea which side
+                    // of a cell it is on -- indicated by the face number. in
+                    // other words, it may provide the inner or outer normal.
+                    // by and large, there is no harm from this, since the
+                    // tangential vector we compute is still the same.
+                    // however, we do average over normal vectors from
+                    // adjacent cells and if they have recorded normal vectors
+                    // from the inside once and from the outside the other
+                    // time, then this averaging is going to run into trouble.
+                    // as a consequence we ask the mapping after all for its
+                    // normal vector, but we only ask it so that we can
+                    // possibly correct the sign of the normal vector provided
+                    // by the boundary if they should point in different
+                    // directions. this is the case in
+                    // tests/deal.II/no_flux_11.
+                    Tensor<1, dim> normal_vector =
+                      (cell->face(face_no)->get_manifold().normal_vector(
+                        cell->face(face_no), fe_values.quadrature_point(i)));
+                    if (normal_vector * fe_values.normal_vector(i) < 0)
+                      normal_vector *= -1;
+                    Assert(std::fabs(normal_vector.norm() - 1) < 1e-14,
+                           ExcInternalError());
+                    for (unsigned int d = 0; d < dim; ++d)
+                      if (std::fabs(normal_vector[d]) < 1e-13)
+                        normal_vector[d] = 0;
+                    normal_vector /= normal_vector.norm();
+
+                    const Point<dim> point = fe_values.quadrature_point(i);
+                    Vector<double>   b_values(dim);
+                    function_map.at(*b_id)->vector_value(point, b_values);
+
+                    // now enter the (dofs,(normal_vector,cell)) entry into
+                    // the map
+                    dof_to_normals_map.insert(
+                      std::make_pair(vector_dofs,
+                                     std::make_pair(normal_vector, cell)));
+                    dof_vector_to_b_values.insert(
+                      std::make_pair(vector_dofs, b_values));
+
+#ifdef DEBUG_NO_NORMAL_FLUX
+                    std::cout << "Adding normal vector:" << std::endl
+                              << "   dofs=" << vector_dofs << std::endl
+                              << "   cell=" << cell << " at " << cell->center()
+                              << std::endl
+                              << "   normal=" << normal_vector << std::endl;
+#endif
+                  }
+            }
+
+    // Now do something with the collected information. To this end, loop
+    // through all sets of pairs (dofs,normal_vector) and identify which
+    // entries belong to the same set of dofs and then do as described in the
+    // documentation, i.e. either average the normal vector or don't for this
+    // particular set of dofs
+    typename DoFToNormalsMap::const_iterator p = dof_to_normals_map.begin();
+
+    while (p != dof_to_normals_map.end())
+      {
+        // first find the range of entries in the multimap that corresponds to
+        // the same vector-dof tuple. as usual, we define the range
+        // half-open. the first entry of course is 'p'
+        typename DoFToNormalsMap::const_iterator same_dof_range[2] = {p};
+        for (++p; p != dof_to_normals_map.end(); ++p)
+          if (p->first != same_dof_range[0]->first)
+            {
+              same_dof_range[1] = p;
+              break;
+            }
+        if (p == dof_to_normals_map.end())
+          same_dof_range[1] = dof_to_normals_map.end();
+
+#ifdef DEBUG_NO_NORMAL_FLUX
+        std::cout << "For dof indices <" << p->first
+                  << ">, found the following normals" << std::endl;
+        for (typename DoFToNormalsMap::const_iterator q = same_dof_range[0];
+             q != same_dof_range[1];
+             ++q)
+          std::cout << "   " << q->second.first << " from cell "
+                    << q->second.second << std::endl;
+#endif
+
+
+        // now compute the reverse mapping: for each of the cells that
+        // contributed to the current set of vector dofs, add up the normal
+        // vectors. the values of the map are pairs of normal vectors and
+        // number of cells that have contributed
+        using CellToNormalsMap =
+          std::map<typename DoFHandlerType<dim, spacedim>::active_cell_iterator,
+                   std::pair<Tensor<1, dim>, unsigned int>>;
+
+        CellToNormalsMap cell_to_normals_map;
+        for (typename DoFToNormalsMap::const_iterator q = same_dof_range[0];
+             q != same_dof_range[1];
+             ++q)
+          if (cell_to_normals_map.find(q->second.second) ==
+              cell_to_normals_map.end())
+            cell_to_normals_map[q->second.second] =
+              std::make_pair(q->second.first, 1U);
+          else
+            {
+              const Tensor<1, dim> old_normal =
+                cell_to_normals_map[q->second.second].first;
+              const unsigned int old_count =
+                cell_to_normals_map[q->second.second].second;
+
+              Assert(old_count > 0, ExcInternalError());
+
+              // in the same entry, store again the now averaged normal vector
+              // and the new count
+              cell_to_normals_map[q->second.second] =
+                std::make_pair((old_normal * old_count + q->second.first) /
+                                 (old_count + 1),
+                               old_count + 1);
+            }
+        Assert(cell_to_normals_map.size() >= 1, ExcInternalError());
+
+#ifdef DEBUG_NO_NORMAL_FLUX
+        std::cout << "   cell_to_normals_map:" << std::endl;
+        for (typename CellToNormalsMap::const_iterator x =
+               cell_to_normals_map.begin();
+             x != cell_to_normals_map.end();
+             ++x)
+          std::cout << "      " << x->first << " -> (" << x->second.first << ','
+                    << x->second.second << ')' << std::endl;
+#endif
+
+        // count the maximum number of contributions from each cell
+        unsigned int max_n_contributions_per_cell = 1;
+        for (typename CellToNormalsMap::const_iterator x =
+               cell_to_normals_map.begin();
+             x != cell_to_normals_map.end();
+             ++x)
+          max_n_contributions_per_cell =
+            std::max(max_n_contributions_per_cell, x->second.second);
+
+        // verify that each cell can have only contributed at most dim times,
+        // since that is the maximum number of faces that come together at a
+        // single place
+        Assert(max_n_contributions_per_cell <= dim, ExcInternalError());
+
+        switch (max_n_contributions_per_cell)
+          {
+            // first deal with the case that a number of cells all have
+            // registered that they have a normal vector defined at the
+            // location of a given vector dof, and that each of them have
+            // encountered this vector dof exactly once while looping over all
+            // their faces. as stated in the documentation, this is the case
+            // where we want to simply average over all normal vectors
+            //
+            // the typical case is in 2d where multiple cells meet at one
+            // vertex sitting on the boundary. same in 3d for a vertex that
+            // is associated with only one of the boundary indicators passed
+            // to this function
+            case 1:
+              {
+                // compute the average normal vector from all the ones that
+                // have the same set of dofs. we could add them up and divide
+                // them by the number of additions, or simply normalize them
+                // right away since we want them to have unit length anyway
+                Tensor<1, dim> normal;
+                for (typename CellToNormalsMap::const_iterator x =
+                       cell_to_normals_map.begin();
+                     x != cell_to_normals_map.end();
+                     ++x)
+                  normal += x->second.first;
+                normal /= normal.norm();
+
+                // normalize again
+                for (unsigned int d = 0; d < dim; ++d)
+                  if (std::fabs(normal[d]) < 1e-13)
+                    normal[d] = 0;
+                normal /= normal.norm();
+
+                // then construct constraints from this:
+                const internal::VectorDoFTuple<dim> &dof_indices =
+                  same_dof_range[0]->first;
+                double               normal_value = 0.;
+                const Vector<double> b_values =
+                  dof_vector_to_b_values[dof_indices];
+                for (unsigned int i = 0; i < dim; ++i)
+                  normal_value += b_values[i] * normal[i];
+                internal::add_constraint(dof_indices,
+                                         normal,
+                                         constraints,
+                                         normal_value);
+
+                break;
+              }
+
+            // this is the slightly more complicated case that a single cell
+            // has contributed with exactly DIM normal vectors to the same set
+            // of vector dofs. this is what happens in a corner in 2d and 3d
+            // (but not on an edge in 3d, where we have only 2, i.e. <DIM,
+            // contributions. Here we do not want to average the normal
+            // vectors. Since we have DIM contributions, let's assume (and
+            // verify) that they are in fact all linearly independent; in that
+            // case, all vector components are constrained and we need to set
+            // all of them to the corresponding boundary values
+            case dim:
+              {
+                // assert that indeed only a single cell has contributed
+                Assert(cell_to_normals_map.size() == 1, ExcInternalError());
+
+                // check linear independence by computing the determinant of
+                // the matrix created from all the normal vectors. if they are
+                // linearly independent, then the determinant is nonzero. if
+                // they are orthogonal, then the matrix is in fact equal to 1
+                // (since they are all unit vectors); make sure the
+                // determinant is larger than 1e-3 to avoid cases where cells
+                // are degenerate
+                {
+                  Tensor<2, dim> t;
+
+                  typename DoFToNormalsMap::const_iterator x =
+                    same_dof_range[0];
+                  for (unsigned int i = 0; i < dim; ++i, ++x)
+                    for (unsigned int j = 0; j < dim; ++j)
+                      t[i][j] = x->second.first[j];
+
+                  Assert(
+                    std::fabs(determinant(t)) > 1e-3,
+                    ExcMessage(
+                      "Found a set of normal vectors that are nearly collinear."));
+                }
+
+                // so all components of this vector dof are constrained. enter
+                // this into the AffineConstraints object
+                //
+                // ignore dofs already constrained
+                const internal::VectorDoFTuple<dim> &dof_indices =
+                  same_dof_range[0]->first;
+                const Vector<double> b_values =
+                  dof_vector_to_b_values[dof_indices];
+                for (unsigned int i = 0; i < dim; ++i)
+                  if (!constraints.is_constrained(
+                        same_dof_range[0]->first.dof_indices[i]) &&
+                      constraints.can_store_line(
+                        same_dof_range[0]->first.dof_indices[i]))
+                    {
+                      const types::global_dof_index line =
+                        dof_indices.dof_indices[i];
+                      constraints.add_line(line);
+                      if (std::fabs(b_values[i]) >
+                          std::numeric_limits<double>::epsilon())
+                        constraints.set_inhomogeneity(line, b_values[i]);
+                      // no add_entries here
+                    }
+
+                break;
+              }
+
+            // this is the case of an edge contribution in 3d, i.e. the vector
+            // is constrained in two directions but not the third.
+            default:
+              {
+                Assert(dim >= 3, ExcNotImplemented());
+                Assert(max_n_contributions_per_cell == 2, ExcInternalError());
+
+                // as described in the documentation, let us first collect
+                // what each of the cells contributed at the current point. we
+                // use a std::list instead of a std::set (which would be more
+                // natural) because std::set requires that the stored elements
+                // are comparable with operator<
+                using CellContributions = std::map<
+                  typename DoFHandlerType<dim, spacedim>::active_cell_iterator,
+                  std::list<Tensor<1, dim>>>;
+                CellContributions cell_contributions;
+
+                for (typename DoFToNormalsMap::const_iterator q =
+                       same_dof_range[0];
+                     q != same_dof_range[1];
+                     ++q)
+                  cell_contributions[q->second.second].push_back(
+                    q->second.first);
+                Assert(cell_contributions.size() >= 1, ExcInternalError());
+
+                // now for each cell that has contributed determine the number
+                // of normal vectors it has contributed. we currently only
+                // implement if this is dim-1 for all cells (if a single cell
+                // has contributed dim, or if all adjacent cells have
+                // contributed 1 normal vector, this is already handled
+                // above).
+                //
+                // we only implement the case that all cells contribute
+                // dim-1 because we assume that we are following an edge
+                // of the domain (think: we are looking at a vertex
+                // located on one of the edges of a refined cube where the
+                // boundary indicators of the two adjacent faces of the
+                // cube are both listed in the set of boundary indicators
+                // passed to this function). in that case, all cells along
+                // that edge of the domain are assumed to have contributed
+                // dim-1 normal vectors. however, there are cases where
+                // this assumption is not justified (see the lengthy
+                // explanation in test no_flux_12.cc) and in those cases
+                // we simply ignore the cell that contributes only
+                // once. this is also discussed at length in the
+                // documentation of this function.
+                //
+                // for each contributing cell compute the tangential vector
+                // that remains unconstrained
+                std::list<Tensor<1, dim>> tangential_vectors;
+                for (typename CellContributions::const_iterator contribution =
+                       cell_contributions.begin();
+                     contribution != cell_contributions.end();
+                     ++contribution)
+                  {
+#ifdef DEBUG_NO_NORMAL_FLUX
+                    std::cout
+                      << "   Treating edge case with dim-1 contributions."
+                      << std::endl
+                      << "   Looking at cell " << contribution->first
+                      << " which has contributed these normal vectors:"
+                      << std::endl;
+                    for (typename std::list<Tensor<1, dim>>::const_iterator t =
+                           contribution->second.begin();
+                         t != contribution->second.end();
+                         ++t)
+                      std::cout << "      " << *t << std::endl;
+#endif
+
+                    // as mentioned above, simply ignore cells that only
+                    // contribute once
+                    if (contribution->second.size() < dim - 1)
+                      continue;
+
+                    Tensor<1, dim> normals[dim - 1];
+                    {
+                      unsigned int index = 0;
+                      for (typename std::list<Tensor<1, dim>>::const_iterator
+                             t = contribution->second.begin();
+                           t != contribution->second.end();
+                           ++t, ++index)
+                        normals[index] = *t;
+                      Assert(index == dim - 1, ExcInternalError());
+                    }
+
+                    // calculate the tangent as the outer product of the
+                    // normal vectors. since these vectors do not need to be
+                    // orthogonal (think, for example, the case of the
+                    // deal.II/no_flux_07 test: a sheared cube in 3d, with Q2
+                    // elements, where we have constraints from the two normal
+                    // vectors of two faces of the sheared cube that are not
+                    // perpendicular to each other), we have to normalize the
+                    // outer product
+                    Tensor<1, dim> tangent;
+                    switch (dim)
+                      {
+                        case 3:
+                          // take cross product between normals[0] and
+                          // normals[1]. write it in the current form (with
+                          // [dim-2]) to make sure that compilers don't warn
+                          // about out-of-bounds accesses -- the warnings are
+                          // bogus since we get here only for dim==3, but at
+                          // least one isn't quite smart enough to notice this
+                          // and warns when compiling the function in 2d
+                          tangent =
+                            cross_product_3d(normals[0], normals[dim - 2]);
+                          break;
+                        default:
+                          Assert(false, ExcNotImplemented());
+                      }
+
+                    Assert(
+                      std::fabs(tangent.norm()) > 1e-12,
+                      ExcMessage(
+                        "Two normal vectors from adjacent faces are almost "
+                        "parallel."));
+                    tangent /= tangent.norm();
+
+                    tangential_vectors.push_back(tangent);
+                  }
+
+                // go through the list of tangents and make sure that they all
+                // roughly point in the same direction as the first one (i.e.
+                // have an angle less than 90 degrees); if they don't then
+                // flip their sign
+                {
+                  const Tensor<1, dim> first_tangent =
+                    tangential_vectors.front();
+                  typename std::list<Tensor<1, dim>>::iterator t =
+                    tangential_vectors.begin();
+                  ++t;
+                  for (; t != tangential_vectors.end(); ++t)
+                    if (*t * first_tangent < 0)
+                      *t *= -1;
+                }
+
+                // now compute the average tangent and normalize it
+                Tensor<1, dim> average_tangent;
+                for (typename std::list<Tensor<1, dim>>::const_iterator t =
+                       tangential_vectors.begin();
+                     t != tangential_vectors.end();
+                     ++t)
+                  average_tangent += *t;
+                average_tangent /= average_tangent.norm();
+
+                // now all that is left is that we add the constraints that
+                // the vector is parallel to the tangent
+                const internal::VectorDoFTuple<dim> &dof_indices =
+                  same_dof_range[0]->first;
+                const Vector<double> b_values =
+                  dof_vector_to_b_values[dof_indices];
+                internal::add_tangentiality_constraints(dof_indices,
+                                                        average_tangent,
+                                                        constraints,
+                                                        b_values);
+              }
+          }
+      }
+  }
+
+  namespace internal
+  {
+    template <int dim>
+    struct PointComparator
+    {
+      bool
+      operator()(const std::array<types::global_dof_index, dim> &p1,
+                 const std::array<types::global_dof_index, dim> &p2) const
+      {
+        for (unsigned int d = 0; d < dim; ++d)
+          if (p1[d] < p2[d])
+            return true;
+        return false;
+      }
+    };
+  } // namespace internal
+
+  template <int dim, int spacedim, template <int, int> class DoFHandlerType>
+  void
+  compute_nonzero_tangential_flux_constraints(
+    const DoFHandlerType<dim, spacedim> &dof_handler,
+    const unsigned int                   first_vector_component,
+    const std::set<types::boundary_id> & boundary_ids,
+    const std::map<types::boundary_id, const Function<spacedim> *>
+      &                           function_map,
+    AffineConstraints<double> &   constraints,
+    const Mapping<dim, spacedim> &mapping)
+  {
+    AffineConstraints<double> no_normal_flux_constraints(
+      constraints.get_local_lines());
+    compute_nonzero_normal_flux_constraints(dof_handler,
+                                            first_vector_component,
+                                            boundary_ids,
+                                            function_map,
+                                            no_normal_flux_constraints,
+                                            mapping);
+
+    const hp::FECollection<dim, spacedim> &fe_collection =
+      dof_handler.get_fe_collection();
+    hp::MappingCollection<dim, spacedim> mapping_collection;
+    for (unsigned int i = 0; i < fe_collection.size(); ++i)
+      mapping_collection.push_back(mapping);
+
+    // now also create a quadrature collection for the faces of a cell. fill
+    // it with a quadrature formula with the support points on faces for each
+    // FE
+    hp::QCollection<dim - 1> face_quadrature_collection;
+    for (unsigned int i = 0; i < fe_collection.size(); ++i)
+      {
+        const std::vector<Point<dim - 1>> &unit_support_points =
+          fe_collection[i].get_unit_face_support_points();
+
+        Assert(unit_support_points.size() == fe_collection[i].dofs_per_face,
+               ExcInternalError());
+
+        face_quadrature_collection.push_back(
+          Quadrature<dim - 1>(unit_support_points));
+      }
+
+    // now create the object with which we will generate the normal vectors
+    hp::FEFaceValues<dim, spacedim> x_fe_face_values(mapping_collection,
+                                                     fe_collection,
+                                                     face_quadrature_collection,
+                                                     update_quadrature_points |
+                                                       update_normal_vectors);
+
+    // Extract a list that collects all vector components that belong to the
+    // same node (scalar basis function). When creating that list, we use an
+    // array of dim components that stores the global degree of freedom.
+    std::set<std::array<types::global_dof_index, dim>,
+             internal::PointComparator<dim>>
+                                         vector_dofs;
+    std::vector<types::global_dof_index> face_dofs;
+
+    std::map<std::array<types::global_dof_index, dim>, Vector<double>>
+      dof_vector_to_b_values;
+
+    std::set<types::boundary_id>::iterator                b_id;
+    std::vector<std::array<types::global_dof_index, dim>> cell_vector_dofs;
+    for (const auto &cell : dof_handler.active_cell_iterators())
+      if (!cell->is_artificial())
+        for (const unsigned int face_no : GeometryInfo<dim>::face_indices())
+          if ((b_id = boundary_ids.find(cell->face(face_no)->boundary_id())) !=
+              boundary_ids.end())
+            {
+              const FiniteElement<dim> &fe = cell->get_fe();
+              typename DoFHandlerType<dim, spacedim>::face_iterator face =
+                cell->face(face_no);
+
+              // get the indices of the dofs on this cell...
+              face_dofs.resize(fe.dofs_per_face);
+              face->get_dof_indices(face_dofs, cell->active_fe_index());
+
+              x_fe_face_values.reinit(cell, face_no);
+              const FEFaceValues<dim> &fe_values =
+                x_fe_face_values.get_present_fe_values();
+
+              std::map<types::global_dof_index, double> dof_to_b_value;
+
+              unsigned int n_scalar_indices = 0;
+              cell_vector_dofs.resize(fe.dofs_per_face);
+              for (unsigned int i = 0; i < fe.dofs_per_face; ++i)
+                {
+                  if (fe.face_system_to_component_index(i).first >=
+                        first_vector_component &&
+                      fe.face_system_to_component_index(i).first <
+                        first_vector_component + dim)
+                    {
+                      const unsigned int component =
+                        fe.face_system_to_component_index(i).first -
+                        first_vector_component;
+                      n_scalar_indices =
+                        std::max(n_scalar_indices,
+                                 fe.face_system_to_component_index(i).second +
+                                   1);
+                      cell_vector_dofs[fe.face_system_to_component_index(i)
+                                         .second][component] = face_dofs[i];
+
+                      const Point<dim> point = fe_values.quadrature_point(i);
+                      const double     b_value =
+                        function_map.at(*b_id)->value(point, component);
+                      dof_to_b_value.insert(
+                        std::make_pair(face_dofs[i], b_value));
+                    }
+                }
+
+              // now we identified the vector indices on the cell, so next
+              // insert them into the set (it would be expensive to directly
+              // insert incomplete points into the set)
+              for (unsigned int i = 0; i < n_scalar_indices; ++i)
+                {
+                  vector_dofs.insert(cell_vector_dofs[i]);
+                  Vector<double> b_values(dim);
+                  for (unsigned int j = 0; j < dim; ++j)
+                    b_values[j] = dof_to_b_value[cell_vector_dofs[i][j]];
+                  dof_vector_to_b_values.insert(
+                    std::make_pair(cell_vector_dofs[i], b_values));
+                }
+            }
+
+    // iterate over the list of all vector components we found and see if we
+    // can find constrained ones
+    unsigned int n_total_constraints_found = 0;
+    for (const auto &dofs : vector_dofs)
+      {
+        unsigned int n_constraints = 0;
+        bool         is_constrained[dim];
+        for (unsigned int d = 0; d < dim; ++d)
+          if (no_normal_flux_constraints.is_constrained(dofs[d]))
+            {
+              is_constrained[d] = true;
+              ++n_constraints;
+              ++n_total_constraints_found;
+            }
+          else
+            is_constrained[d] = false;
+        if (n_constraints > 0)
+          {
+            // if more than one no-flux constraint is present, we need to
+            // constrain all vector degrees of freedom (we are in a corner
+            // where several faces meet and to get a continuous FE solution we
+            // need to set all conditions corresponding to the boundary
+            // function.).
+            if (n_constraints > 1)
+              {
+                const Vector<double> b_value = dof_vector_to_b_values[dofs];
+                for (unsigned int d = 0; d < dim; ++d)
+                  {
+                    constraints.add_line(dofs[d]);
+                    constraints.set_inhomogeneity(dofs[d], b_value(d));
+                  }
+                continue;
+              }
+
+            // ok, this is a no-flux constraint, so get the index of the dof
+            // that is currently constrained and make it unconstrained. The
+            // constraint indices will get the normal that contain the other
+            // indices.
+            Tensor<1, dim> normal;
+            unsigned       constrained_index = -1;
+            for (unsigned int d = 0; d < dim; ++d)
+              if (is_constrained[d])
+                {
+                  constrained_index = d;
+                  normal[d]         = 1.;
+                }
+            AssertIndexRange(constrained_index, dim);
+            const std::vector<std::pair<types::global_dof_index, double>>
+              *constrained = no_normal_flux_constraints.get_constraint_entries(
+                dofs[constrained_index]);
+            // find components to which this index is constrained to
+            Assert(constrained != nullptr, ExcInternalError());
+            Assert(constrained->size() < dim, ExcInternalError());
+            for (const auto &entry : *constrained)
+              {
+                int index = -1;
+                for (unsigned int d = 0; d < dim; ++d)
+                  if (entry.first == dofs[d])
+                    index = d;
+                Assert(index != -1, ExcInternalError());
+                normal[index] = entry.second;
+              }
+            Vector<double> boundary_value = dof_vector_to_b_values[dofs];
+            for (unsigned int d = 0; d < dim; ++d)
+              {
+                if (is_constrained[d])
+                  continue;
+                const unsigned int new_index = dofs[d];
+                if (!constraints.is_constrained(new_index))
+                  {
+                    constraints.add_line(new_index);
+                    if (std::abs(normal[d]) > 1e-13)
+                      constraints.add_entry(new_index,
+                                            dofs[constrained_index],
+                                            -normal[d]);
+                    constraints.set_inhomogeneity(new_index, boundary_value[d]);
+                  }
+              }
+          }
+      }
+    AssertDimension(n_total_constraints_found,
+                    no_normal_flux_constraints.n_constraints());
+  }
+
+
+  template <int dim, int spacedim, template <int, int> class DoFHandlerType>
+  void
+  compute_no_normal_flux_constraints(
+    const DoFHandlerType<dim, spacedim> &dof_handler,
+    const unsigned int                   first_vector_component,
+    const std::set<types::boundary_id> & boundary_ids,
+    AffineConstraints<double> &          constraints,
+    const Mapping<dim, spacedim> &       mapping)
+  {
+    ZeroFunction<dim>                                        zero_function(dim);
+    std::map<types::boundary_id, const Function<spacedim> *> function_map;
+    for (const types::boundary_id boundary_id : boundary_ids)
+      function_map[boundary_id] = &zero_function;
+    compute_nonzero_normal_flux_constraints(dof_handler,
+                                            first_vector_component,
+                                            boundary_ids,
+                                            function_map,
+                                            constraints,
+                                            mapping);
+  }
+
+  template <int dim, int spacedim, template <int, int> class DoFHandlerType>
+  void
+  compute_normal_flux_constraints(
+    const DoFHandlerType<dim, spacedim> &dof_handler,
+    const unsigned int                   first_vector_component,
+    const std::set<types::boundary_id> & boundary_ids,
+    AffineConstraints<double> &          constraints,
+    const Mapping<dim, spacedim> &       mapping)
+  {
+    ZeroFunction<dim>                                        zero_function(dim);
+    std::map<types::boundary_id, const Function<spacedim> *> function_map;
+    for (const types::boundary_id boundary_id : boundary_ids)
+      function_map[boundary_id] = &zero_function;
+    compute_nonzero_tangential_flux_constraints(dof_handler,
+                                                first_vector_component,
+                                                boundary_ids,
+                                                function_map,
+                                                constraints,
+                                                mapping);
+  }
+} // namespace VectorTools
+
+DEAL_II_NAMESPACE_CLOSE
+
+#endif // dealii_vector_tools_constraints_templates_h
diff --git a/include/deal.II/numerics/vector_tools_integrate_difference.templates.h b/include/deal.II/numerics/vector_tools_integrate_difference.templates.h
new file mode 100644 (file)
index 0000000..5893ac5
--- /dev/null
@@ -0,0 +1,1346 @@
+// ---------------------------------------------------------------------
+//
+// Copyright (C) 1998 - 2014 by the deal.II authors
+//
+// This file is part of the deal.II library.
+//
+// The deal.II library is free software; you can use it, redistribute
+// it, and/or modify it under the terms of the GNU Lesser General
+// Public License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+// The full text of the license can be found in the file LICENSE.md at
+// the top level directory of deal.II.
+//
+// ---------------------------------------------------------------------
+
+
+#ifndef dealii_vector_tools_integrate_difference_templates_h
+#define dealii_vector_tools_integrate_difference_templates_h
+
+
+#include <deal.II/hp/fe_values.h>
+
+#include <deal.II/lac/block_vector.h>
+#include <deal.II/lac/la_parallel_block_vector.h>
+#include <deal.II/lac/la_parallel_vector.h>
+#include <deal.II/lac/la_vector.h>
+#include <deal.II/lac/petsc_block_vector.h>
+#include <deal.II/lac/petsc_vector.h>
+#include <deal.II/lac/trilinos_parallel_block_vector.h>
+#include <deal.II/lac/trilinos_vector.h>
+
+#include <deal.II/numerics/vector_tools.h>
+
+DEAL_II_NAMESPACE_OPEN
+
+namespace VectorTools
+{
+  namespace internal
+  {
+    template <int dim, int spacedim, typename Number>
+    struct IDScratchData
+    {
+      IDScratchData(const dealii::hp::MappingCollection<dim, spacedim> &mapping,
+                    const dealii::hp::FECollection<dim, spacedim> &     fe,
+                    const dealii::hp::QCollection<dim> &                q,
+                    const UpdateFlags update_flags);
+
+      IDScratchData(const IDScratchData &data);
+
+      void
+      resize_vectors(const unsigned int n_q_points,
+                     const unsigned int n_components);
+
+      std::vector<Vector<Number>>                           function_values;
+      std::vector<std::vector<Tensor<1, spacedim, Number>>> function_grads;
+      std::vector<double>                                   weight_values;
+      std::vector<Vector<double>>                           weight_vectors;
+
+      std::vector<Vector<Number>>                           psi_values;
+      std::vector<std::vector<Tensor<1, spacedim, Number>>> psi_grads;
+      std::vector<Number>                                   psi_scalar;
+
+      std::vector<Number>                      tmp_values;
+      std::vector<Vector<Number>>              tmp_vector_values;
+      std::vector<Tensor<1, spacedim, Number>> tmp_gradients;
+      std::vector<std::vector<Tensor<1, spacedim, Number>>>
+        tmp_vector_gradients;
+
+      dealii::hp::FEValues<dim, spacedim> x_fe_values;
+    };
+
+
+    template <int dim, int spacedim, typename Number>
+    IDScratchData<dim, spacedim, Number>::IDScratchData(
+      const dealii::hp::MappingCollection<dim, spacedim> &mapping,
+      const dealii::hp::FECollection<dim, spacedim> &     fe,
+      const dealii::hp::QCollection<dim> &                q,
+      const UpdateFlags                                   update_flags)
+      : x_fe_values(mapping, fe, q, update_flags)
+    {}
+
+    template <int dim, int spacedim, typename Number>
+    IDScratchData<dim, spacedim, Number>::IDScratchData(
+      const IDScratchData &data)
+      : x_fe_values(data.x_fe_values.get_mapping_collection(),
+                    data.x_fe_values.get_fe_collection(),
+                    data.x_fe_values.get_quadrature_collection(),
+                    data.x_fe_values.get_update_flags())
+    {}
+
+    template <int dim, int spacedim, typename Number>
+    void
+    IDScratchData<dim, spacedim, Number>::resize_vectors(
+      const unsigned int n_q_points,
+      const unsigned int n_components)
+    {
+      function_values.resize(n_q_points, Vector<Number>(n_components));
+      function_grads.resize(
+        n_q_points, std::vector<Tensor<1, spacedim, Number>>(n_components));
+
+      weight_values.resize(n_q_points);
+      weight_vectors.resize(n_q_points, Vector<double>(n_components));
+
+      psi_values.resize(n_q_points, Vector<Number>(n_components));
+      psi_grads.resize(n_q_points,
+                       std::vector<Tensor<1, spacedim, Number>>(n_components));
+      psi_scalar.resize(n_q_points);
+
+      tmp_values.resize(n_q_points);
+      tmp_vector_values.resize(n_q_points, Vector<Number>(n_components));
+      tmp_gradients.resize(n_q_points);
+      tmp_vector_gradients.resize(
+        n_q_points, std::vector<Tensor<1, spacedim, Number>>(n_components));
+    }
+
+    template <int dim, int spacedim, typename Number>
+    struct DEAL_II_DEPRECATED DeprecatedIDScratchData
+    {
+      DeprecatedIDScratchData(
+        const dealii::hp::MappingCollection<dim, spacedim> &mapping,
+        const dealii::hp::FECollection<dim, spacedim> &     fe,
+        const dealii::hp::QCollection<dim> &                q,
+        const UpdateFlags                                   update_flags);
+
+      DeprecatedIDScratchData(const DeprecatedIDScratchData &data);
+
+      void
+      resize_vectors(const unsigned int n_q_points,
+                     const unsigned int n_components);
+
+      std::vector<Vector<Number>>                           function_values;
+      std::vector<std::vector<Tensor<1, spacedim, Number>>> function_grads;
+      std::vector<double>                                   weight_values;
+      std::vector<Vector<double>>                           weight_vectors;
+
+      std::vector<Vector<Number>>                           psi_values;
+      std::vector<std::vector<Tensor<1, spacedim, Number>>> psi_grads;
+      std::vector<Number>                                   psi_scalar;
+
+      std::vector<double>                           tmp_values;
+      std::vector<Vector<double>>                   tmp_vector_values;
+      std::vector<Tensor<1, spacedim>>              tmp_gradients;
+      std::vector<std::vector<Tensor<1, spacedim>>> tmp_vector_gradients;
+
+      dealii::hp::FEValues<dim, spacedim> x_fe_values;
+    };
+
+
+    template <int dim, int spacedim, typename Number>
+    DeprecatedIDScratchData<dim, spacedim, Number>::DeprecatedIDScratchData(
+      const dealii::hp::MappingCollection<dim, spacedim> &mapping,
+      const dealii::hp::FECollection<dim, spacedim> &     fe,
+      const dealii::hp::QCollection<dim> &                q,
+      const UpdateFlags                                   update_flags)
+      : x_fe_values(mapping, fe, q, update_flags)
+    {}
+
+    template <int dim, int spacedim, typename Number>
+    DeprecatedIDScratchData<dim, spacedim, Number>::DeprecatedIDScratchData(
+      const DeprecatedIDScratchData &data)
+      : x_fe_values(data.x_fe_values.get_mapping_collection(),
+                    data.x_fe_values.get_fe_collection(),
+                    data.x_fe_values.get_quadrature_collection(),
+                    data.x_fe_values.get_update_flags())
+    {}
+
+    template <int dim, int spacedim, typename Number>
+    void
+    DeprecatedIDScratchData<dim, spacedim, Number>::resize_vectors(
+      const unsigned int n_q_points,
+      const unsigned int n_components)
+    {
+      function_values.resize(n_q_points, Vector<Number>(n_components));
+      function_grads.resize(
+        n_q_points, std::vector<Tensor<1, spacedim, Number>>(n_components));
+
+      weight_values.resize(n_q_points);
+      weight_vectors.resize(n_q_points, Vector<double>(n_components));
+
+      psi_values.resize(n_q_points, Vector<Number>(n_components));
+      psi_grads.resize(n_q_points,
+                       std::vector<Tensor<1, spacedim, Number>>(n_components));
+      psi_scalar.resize(n_q_points);
+
+      tmp_values.resize(n_q_points);
+      tmp_vector_values.resize(n_q_points, Vector<double>(n_components));
+      tmp_gradients.resize(n_q_points);
+      tmp_vector_gradients.resize(
+        n_q_points, std::vector<Tensor<1, spacedim>>(n_components));
+    }
+
+    namespace internal
+    {
+      template <typename number>
+      double
+      mean_to_double(const number &mean_value)
+      {
+        return mean_value;
+      }
+
+      template <typename number>
+      double
+      mean_to_double(const std::complex<number> &mean_value)
+      {
+        // we need to return double as a norm, but mean value is a complex
+        // number. Panic and return real-part while warning the user that
+        // they shall never do that.
+        Assert(
+          false,
+          ExcMessage(
+            "Mean value norm is not implemented for complex-valued vectors"));
+        return mean_value.real();
+      }
+    } // namespace internal
+
+
+    // avoid compiling inner function for many vector types when we always
+    // really do the same thing by putting the main work into this helper
+    // function
+    template <int dim, int spacedim, typename Number>
+    double
+    integrate_difference_inner(const Function<spacedim, Number> &exact_solution,
+                               const NormType &                  norm,
+                               const Function<spacedim> *        weight,
+                               const UpdateFlags                 update_flags,
+                               const double                      exponent,
+                               const unsigned int                n_components,
+                               IDScratchData<dim, spacedim, Number> &data)
+    {
+      const bool                             fe_is_system = (n_components != 1);
+      const dealii::FEValues<dim, spacedim> &fe_values =
+        data.x_fe_values.get_present_fe_values();
+      const unsigned int n_q_points = fe_values.n_quadrature_points;
+
+      if (weight != nullptr)
+        {
+          if (weight->n_components > 1)
+            weight->vector_value_list(fe_values.get_quadrature_points(),
+                                      data.weight_vectors);
+          else
+            {
+              weight->value_list(fe_values.get_quadrature_points(),
+                                 data.weight_values);
+              for (unsigned int k = 0; k < n_q_points; ++k)
+                data.weight_vectors[k] = data.weight_values[k];
+            }
+        }
+      else
+        {
+          for (unsigned int k = 0; k < n_q_points; ++k)
+            data.weight_vectors[k] = 1.;
+        }
+
+
+      if (update_flags & update_values)
+        {
+          // first compute the exact solution (vectors) at the quadrature
+          // points. try to do this as efficient as possible by avoiding a
+          // second virtual function call in case the function really has only
+          // one component
+          //
+          // TODO: we have to work a bit here because the Function<dim,double>
+          //   interface of the argument denoting the exact function only
+          //   provides us with double/Tensor<1,dim> values, rather than
+          //   with the correct data type. so evaluate into a temp
+          //   object, then copy around
+          if (fe_is_system)
+            {
+              exact_solution.vector_value_list(
+                fe_values.get_quadrature_points(), data.tmp_vector_values);
+              for (unsigned int i = 0; i < n_q_points; ++i)
+                data.psi_values[i] = data.tmp_vector_values[i];
+            }
+          else
+            {
+              exact_solution.value_list(fe_values.get_quadrature_points(),
+                                        data.tmp_values);
+              for (unsigned int i = 0; i < n_q_points; ++i)
+                data.psi_values[i](0) = data.tmp_values[i];
+            }
+
+          // then subtract finite element fe_function
+          for (unsigned int q = 0; q < n_q_points; ++q)
+            for (unsigned int i = 0; i < data.psi_values[q].size(); ++i)
+              data.psi_values[q][i] -= data.function_values[q][i];
+        }
+
+      // Do the same for gradients, if required
+      if (update_flags & update_gradients)
+        {
+          // try to be a little clever to avoid recursive virtual function
+          // calls when calling gradient_list for functions that are really
+          // scalar functions
+          if (fe_is_system)
+            {
+              exact_solution.vector_gradient_list(
+                fe_values.get_quadrature_points(), data.tmp_vector_gradients);
+              for (unsigned int i = 0; i < n_q_points; ++i)
+                for (unsigned int comp = 0; comp < data.psi_grads[i].size();
+                     ++comp)
+                  data.psi_grads[i][comp] = data.tmp_vector_gradients[i][comp];
+            }
+          else
+            {
+              exact_solution.gradient_list(fe_values.get_quadrature_points(),
+                                           data.tmp_gradients);
+              for (unsigned int i = 0; i < n_q_points; ++i)
+                data.psi_grads[i][0] = data.tmp_gradients[i];
+            }
+
+          // then subtract finite element function_grads. We need to be
+          // careful in the codimension one case, since there we only have
+          // tangential gradients in the finite element function, not the full
+          // gradient. This is taken care of, by subtracting the normal
+          // component of the gradient from the exact function.
+          if (update_flags & update_normal_vectors)
+            for (unsigned int k = 0; k < n_components; ++k)
+              for (unsigned int q = 0; q < n_q_points; ++q)
+                {
+                  // compute (f.n) n
+                  const typename ProductType<Number, double>::type f_dot_n =
+                    data.psi_grads[q][k] * fe_values.normal_vector(q);
+                  const Tensor<1, spacedim, Number> f_dot_n_times_n =
+                    f_dot_n * fe_values.normal_vector(q);
+
+                  data.psi_grads[q][k] -=
+                    (data.function_grads[q][k] + f_dot_n_times_n);
+                }
+          else
+            for (unsigned int k = 0; k < n_components; ++k)
+              for (unsigned int q = 0; q < n_q_points; ++q)
+                for (unsigned int d = 0; d < spacedim; ++d)
+                  data.psi_grads[q][k][d] -= data.function_grads[q][k][d];
+        }
+
+      double diff      = 0;
+      Number diff_mean = 0;
+
+      // First work on function values:
+      switch (norm)
+        {
+          case mean:
+            // Compute values in quadrature points and integrate
+            for (unsigned int q = 0; q < n_q_points; ++q)
+              {
+                Number sum = 0;
+                for (unsigned int k = 0; k < n_components; ++k)
+                  if (data.weight_vectors[q](k) != 0)
+                    sum += data.psi_values[q](k) * data.weight_vectors[q](k);
+                diff_mean += sum * fe_values.JxW(q);
+              }
+            break;
+
+          case Lp_norm:
+          case L1_norm:
+          case W1p_norm:
+            // Compute values in quadrature points and integrate
+            for (unsigned int q = 0; q < n_q_points; ++q)
+              {
+                double sum = 0;
+                for (unsigned int k = 0; k < n_components; ++k)
+                  if (data.weight_vectors[q](k) != 0)
+                    sum += std::pow(static_cast<double>(
+                                      numbers::NumberTraits<Number>::abs_square(
+                                        data.psi_values[q](k))),
+                                    exponent / 2.) *
+                           data.weight_vectors[q](k);
+                diff += sum * fe_values.JxW(q);
+              }
+
+            // Compute the root only if no derivative values are added later
+            if (!(update_flags & update_gradients))
+              diff = std::pow(diff, 1. / exponent);
+            break;
+
+          case L2_norm:
+          case H1_norm:
+            // Compute values in quadrature points and integrate
+            for (unsigned int q = 0; q < n_q_points; ++q)
+              {
+                double sum = 0;
+                for (unsigned int k = 0; k < n_components; ++k)
+                  if (data.weight_vectors[q](k) != 0)
+                    sum += numbers::NumberTraits<Number>::abs_square(
+                             data.psi_values[q](k)) *
+                           data.weight_vectors[q](k);
+                diff += sum * fe_values.JxW(q);
+              }
+            // Compute the root only, if no derivative values are added later
+            if (norm == L2_norm)
+              diff = std::sqrt(diff);
+            break;
+
+          case Linfty_norm:
+          case W1infty_norm:
+            for (unsigned int q = 0; q < n_q_points; ++q)
+              for (unsigned int k = 0; k < n_components; ++k)
+                if (data.weight_vectors[q](k) != 0)
+                  diff = std::max(diff,
+                                  double(std::abs(data.psi_values[q](k) *
+                                                  data.weight_vectors[q](k))));
+            break;
+
+          case H1_seminorm:
+          case Hdiv_seminorm:
+          case W1p_seminorm:
+          case W1infty_seminorm:
+            // function values are not used for these norms
+            break;
+
+          default:
+            Assert(false, ExcNotImplemented());
+            break;
+        }
+
+      // Now compute terms depending on derivatives:
+      switch (norm)
+        {
+          case W1p_seminorm:
+          case W1p_norm:
+            for (unsigned int q = 0; q < n_q_points; ++q)
+              {
+                double sum = 0;
+                for (unsigned int k = 0; k < n_components; ++k)
+                  if (data.weight_vectors[q](k) != 0)
+                    sum += std::pow(data.psi_grads[q][k].norm_square(),
+                                    exponent / 2.) *
+                           data.weight_vectors[q](k);
+                diff += sum * fe_values.JxW(q);
+              }
+            diff = std::pow(diff, 1. / exponent);
+            break;
+
+          case H1_seminorm:
+          case H1_norm:
+            for (unsigned int q = 0; q < n_q_points; ++q)
+              {
+                double sum = 0;
+                for (unsigned int k = 0; k < n_components; ++k)
+                  if (data.weight_vectors[q](k) != 0)
+                    sum += data.psi_grads[q][k].norm_square() *
+                           data.weight_vectors[q](k);
+                diff += sum * fe_values.JxW(q);
+              }
+            diff = std::sqrt(diff);
+            break;
+
+          case Hdiv_seminorm:
+            for (unsigned int q = 0; q < n_q_points; ++q)
+              {
+                unsigned int idx = 0;
+                if (weight != nullptr)
+                  for (; idx < n_components; ++idx)
+                    if (data.weight_vectors[0](idx) > 0)
+                      break;
+
+                Assert(
+                  n_components >= idx + dim,
+                  ExcMessage(
+                    "You can only ask for the Hdiv norm for a finite element "
+                    "with at least 'dim' components. In that case, this function "
+                    "will find the index of the first non-zero weight and take "
+                    "the divergence of the 'dim' components that follow it."));
+
+                Number sum = 0;
+                // take the trace of the derivatives scaled by the weight and
+                // square it
+                for (unsigned int k = idx; k < idx + dim; ++k)
+                  if (data.weight_vectors[q](k) != 0)
+                    sum += data.psi_grads[q][k][k - idx] *
+                           std::sqrt(data.weight_vectors[q](k));
+                diff += numbers::NumberTraits<Number>::abs_square(sum) *
+                        fe_values.JxW(q);
+              }
+            diff = std::sqrt(diff);
+            break;
+
+          case W1infty_seminorm:
+          case W1infty_norm:
+            {
+              double t = 0;
+              for (unsigned int q = 0; q < n_q_points; ++q)
+                for (unsigned int k = 0; k < n_components; ++k)
+                  if (data.weight_vectors[q](k) != 0)
+                    for (unsigned int d = 0; d < dim; ++d)
+                      t = std::max(t,
+                                   double(std::abs(data.psi_grads[q][k][d]) *
+                                          data.weight_vectors[q](k)));
+
+              // then add seminorm to norm if that had previously been
+              // computed
+              diff += t;
+            }
+            break;
+          default:
+            break;
+        }
+
+      if (norm == mean)
+        diff = internal::mean_to_double(diff_mean);
+
+      // append result of this cell to the end of the vector
+      AssertIsFinite(diff);
+      return diff;
+    }
+
+    template <int dim, int spacedim, typename Number>
+    DEAL_II_DEPRECATED
+      typename std::enable_if<!std::is_same<Number, double>::value,
+                              double>::type
+      integrate_difference_inner(
+        const Function<spacedim> &                      exact_solution,
+        const NormType &                                norm,
+        const Function<spacedim> *                      weight,
+        const UpdateFlags                               update_flags,
+        const double                                    exponent,
+        const unsigned int                              n_components,
+        DeprecatedIDScratchData<dim, spacedim, Number> &data)
+    {
+      const bool                             fe_is_system = (n_components != 1);
+      const dealii::FEValues<dim, spacedim> &fe_values =
+        data.x_fe_values.get_present_fe_values();
+      const unsigned int n_q_points = fe_values.n_quadrature_points;
+
+      if (weight != nullptr)
+        {
+          if (weight->n_components > 1)
+            weight->vector_value_list(fe_values.get_quadrature_points(),
+                                      data.weight_vectors);
+          else
+            {
+              weight->value_list(fe_values.get_quadrature_points(),
+                                 data.weight_values);
+              for (unsigned int k = 0; k < n_q_points; ++k)
+                data.weight_vectors[k] = data.weight_values[k];
+            }
+        }
+      else
+        {
+          for (unsigned int k = 0; k < n_q_points; ++k)
+            data.weight_vectors[k] = 1.;
+        }
+
+
+      if (update_flags & update_values)
+        {
+          // first compute the exact solution (vectors) at the quadrature
+          // points. try to do this as efficient as possible by avoiding a
+          // second virtual function call in case the function really has only
+          // one component
+          //
+          // TODO: we have to work a bit here because the Function<dim,double>
+          //   interface of the argument denoting the exact function only
+          //   provides us with double/Tensor<1,dim> values, rather than
+          //   with the correct data type. so evaluate into a temp
+          //   object, then copy around
+          if (fe_is_system)
+            {
+              exact_solution.vector_value_list(
+                fe_values.get_quadrature_points(), data.tmp_vector_values);
+              for (unsigned int i = 0; i < n_q_points; ++i)
+                data.psi_values[i] = data.tmp_vector_values[i];
+            }
+          else
+            {
+              exact_solution.value_list(fe_values.get_quadrature_points(),
+                                        data.tmp_values);
+              for (unsigned int i = 0; i < n_q_points; ++i)
+                data.psi_values[i](0) = data.tmp_values[i];
+            }
+
+          // then subtract finite element fe_function
+          for (unsigned int q = 0; q < n_q_points; ++q)
+            for (unsigned int i = 0; i < data.psi_values[q].size(); ++i)
+              data.psi_values[q][i] -= data.function_values[q][i];
+        }
+
+      // Do the same for gradients, if required
+      if (update_flags & update_gradients)
+        {
+          // try to be a little clever to avoid recursive virtual function
+          // calls when calling gradient_list for functions that are really
+          // scalar functions
+          if (fe_is_system)
+            {
+              exact_solution.vector_gradient_list(
+                fe_values.get_quadrature_points(), data.tmp_vector_gradients);
+              for (unsigned int i = 0; i < n_q_points; ++i)
+                for (unsigned int comp = 0; comp < data.psi_grads[i].size();
+                     ++comp)
+                  data.psi_grads[i][comp] = data.tmp_vector_gradients[i][comp];
+            }
+          else
+            {
+              exact_solution.gradient_list(fe_values.get_quadrature_points(),
+                                           data.tmp_gradients);
+              for (unsigned int i = 0; i < n_q_points; ++i)
+                data.psi_grads[i][0] = data.tmp_gradients[i];
+            }
+
+          // then subtract finite element function_grads. We need to be
+          // careful in the codimension one case, since there we only have
+          // tangential gradients in the finite element function, not the full
+          // gradient. This is taken care of, by subtracting the normal
+          // component of the gradient from the exact function.
+          if (update_flags & update_normal_vectors)
+            for (unsigned int k = 0; k < n_components; ++k)
+              for (unsigned int q = 0; q < n_q_points; ++q)
+                {
+                  // compute (f.n) n
+                  const typename ProductType<Number, double>::type f_dot_n =
+                    data.psi_grads[q][k] * fe_values.normal_vector(q);
+                  const Tensor<1, spacedim, Number> f_dot_n_times_n =
+                    f_dot_n * fe_values.normal_vector(q);
+
+                  data.psi_grads[q][k] -=
+                    (data.function_grads[q][k] + f_dot_n_times_n);
+                }
+          else
+            for (unsigned int k = 0; k < n_components; ++k)
+              for (unsigned int q = 0; q < n_q_points; ++q)
+                for (unsigned int d = 0; d < spacedim; ++d)
+                  data.psi_grads[q][k][d] -= data.function_grads[q][k][d];
+        }
+
+      double diff      = 0;
+      Number diff_mean = 0;
+
+      // First work on function values:
+      switch (norm)
+        {
+          case mean:
+            // Compute values in quadrature points and integrate
+            for (unsigned int q = 0; q < n_q_points; ++q)
+              {
+                Number sum = 0;
+                for (unsigned int k = 0; k < n_components; ++k)
+                  if (data.weight_vectors[q](k) != 0)
+                    sum += data.psi_values[q](k) * data.weight_vectors[q](k);
+                diff_mean += sum * fe_values.JxW(q);
+              }
+            break;
+
+          case Lp_norm:
+          case L1_norm:
+          case W1p_norm:
+            // Compute values in quadrature points and integrate
+            for (unsigned int q = 0; q < n_q_points; ++q)
+              {
+                double sum = 0;
+                for (unsigned int k = 0; k < n_components; ++k)
+                  if (data.weight_vectors[q](k) != 0)
+                    sum += std::pow(static_cast<double>(
+                                      numbers::NumberTraits<Number>::abs_square(
+                                        data.psi_values[q](k))),
+                                    exponent / 2.) *
+                           data.weight_vectors[q](k);
+                diff += sum * fe_values.JxW(q);
+              }
+
+            // Compute the root only if no derivative values are added later
+            if (!(update_flags & update_gradients))
+              diff = std::pow(diff, 1. / exponent);
+            break;
+
+          case L2_norm:
+          case H1_norm:
+            // Compute values in quadrature points and integrate
+            for (unsigned int q = 0; q < n_q_points; ++q)
+              {
+                double sum = 0;
+                for (unsigned int k = 0; k < n_components; ++k)
+                  if (data.weight_vectors[q](k) != 0)
+                    sum += numbers::NumberTraits<Number>::abs_square(
+                             data.psi_values[q](k)) *
+                           data.weight_vectors[q](k);
+                diff += sum * fe_values.JxW(q);
+              }
+            // Compute the root only, if no derivative values are added later
+            if (norm == L2_norm)
+              diff = std::sqrt(diff);
+            break;
+
+          case Linfty_norm:
+          case W1infty_norm:
+            for (unsigned int q = 0; q < n_q_points; ++q)
+              for (unsigned int k = 0; k < n_components; ++k)
+                if (data.weight_vectors[q](k) != 0)
+                  diff = std::max(diff,
+                                  double(std::abs(data.psi_values[q](k) *
+                                                  data.weight_vectors[q](k))));
+            break;
+
+          case H1_seminorm:
+          case Hdiv_seminorm:
+          case W1p_seminorm:
+          case W1infty_seminorm:
+            // function values are not used for these norms
+            break;
+
+          default:
+            Assert(false, ExcNotImplemented());
+            break;
+        }
+
+      // Now compute terms depending on derivatives:
+      switch (norm)
+        {
+          case W1p_seminorm:
+          case W1p_norm:
+            for (unsigned int q = 0; q < n_q_points; ++q)
+              {
+                double sum = 0;
+                for (unsigned int k = 0; k < n_components; ++k)
+                  if (data.weight_vectors[q](k) != 0)
+                    sum += std::pow(data.psi_grads[q][k].norm_square(),
+                                    exponent / 2.) *
+                           data.weight_vectors[q](k);
+                diff += sum * fe_values.JxW(q);
+              }
+            diff = std::pow(diff, 1. / exponent);
+            break;
+
+          case H1_seminorm:
+          case H1_norm:
+            for (unsigned int q = 0; q < n_q_points; ++q)
+              {
+                double sum = 0;
+                for (unsigned int k = 0; k < n_components; ++k)
+                  if (data.weight_vectors[q](k) != 0)
+                    sum += data.psi_grads[q][k].norm_square() *
+                           data.weight_vectors[q](k);
+                diff += sum * fe_values.JxW(q);
+              }
+            diff = std::sqrt(diff);
+            break;
+
+          case Hdiv_seminorm:
+            for (unsigned int q = 0; q < n_q_points; ++q)
+              {
+                unsigned int idx = 0;
+                if (weight != nullptr)
+                  for (; idx < n_components; ++idx)
+                    if (data.weight_vectors[0](idx) > 0)
+                      break;
+
+                Assert(
+                  n_components >= idx + dim,
+                  ExcMessage(
+                    "You can only ask for the Hdiv norm for a finite element "
+                    "with at least 'dim' components. In that case, this function "
+                    "will find the index of the first non-zero weight and take "
+                    "the divergence of the 'dim' components that follow it."));
+
+                Number sum = 0;
+                // take the trace of the derivatives scaled by the weight and
+                // square it
+                for (unsigned int k = idx; k < idx + dim; ++k)
+                  if (data.weight_vectors[q](k) != 0)
+                    sum += data.psi_grads[q][k][k - idx] *
+                           std::sqrt(data.weight_vectors[q](k));
+                diff += numbers::NumberTraits<Number>::abs_square(sum) *
+                        fe_values.JxW(q);
+              }
+            diff = std::sqrt(diff);
+            break;
+
+          case W1infty_seminorm:
+          case W1infty_norm:
+            {
+              double t = 0;
+              for (unsigned int q = 0; q < n_q_points; ++q)
+                for (unsigned int k = 0; k < n_components; ++k)
+                  if (data.weight_vectors[q](k) != 0)
+                    for (unsigned int d = 0; d < dim; ++d)
+                      t = std::max(t,
+                                   double(std::abs(data.psi_grads[q][k][d]) *
+                                          data.weight_vectors[q](k)));
+
+              // then add seminorm to norm if that had previously been
+              // computed
+              diff += t;
+            }
+            break;
+          default:
+            break;
+        }
+
+      if (norm == mean)
+        diff = internal::mean_to_double(diff_mean);
+
+      // append result of this cell to the end of the vector
+      AssertIsFinite(diff);
+      return diff;
+    }
+
+    template <int dim,
+              class InVector,
+              class OutVector,
+              typename DoFHandlerType,
+              int spacedim>
+    static void
+    do_integrate_difference(
+      const dealii::hp::MappingCollection<dim, spacedim> &     mapping,
+      const DoFHandlerType &                                   dof,
+      const InVector &                                         fe_function,
+      const Function<spacedim, typename InVector::value_type> &exact_solution,
+      OutVector &                                              difference,
+      const dealii::hp::QCollection<dim> &                     q,
+      const NormType &                                         norm,
+      const Function<spacedim> *                               weight,
+      const double                                             exponent_1)
+    {
+      using Number = typename InVector::value_type;
+      // we mark the "exponent" parameter to this function "const" since it is
+      // strictly incoming, but we need to set it to something different later
+      // on, if necessary, so have a read-write version of it:
+      double exponent = exponent_1;
+
+      const unsigned int n_components = dof.get_fe(0).n_components();
+
+      Assert(exact_solution.n_components == n_components,
+             ExcDimensionMismatch(exact_solution.n_components, n_components));
+
+      if (weight != nullptr)
+        {
+          Assert((weight->n_components == 1) ||
+                   (weight->n_components == n_components),
+                 ExcDimensionMismatch(weight->n_components, n_components));
+        }
+
+      difference.reinit(dof.get_triangulation().n_active_cells());
+
+      switch (norm)
+        {
+          case L2_norm:
+          case H1_seminorm:
+          case H1_norm:
+          case Hdiv_seminorm:
+            exponent = 2.;
+            break;
+
+          case L1_norm:
+            exponent = 1.;
+            break;
+
+          default:
+            break;
+        }
+
+      UpdateFlags update_flags =
+        UpdateFlags(update_quadrature_points | update_JxW_values);
+      switch (norm)
+        {
+          case H1_seminorm:
+          case Hdiv_seminorm:
+          case W1p_seminorm:
+          case W1infty_seminorm:
+            update_flags |= UpdateFlags(update_gradients);
+            if (spacedim == dim + 1)
+              update_flags |= UpdateFlags(update_normal_vectors);
+
+            break;
+
+          case H1_norm:
+          case W1p_norm:
+          case W1infty_norm:
+            update_flags |= UpdateFlags(update_gradients);
+            if (spacedim == dim + 1)
+              update_flags |= UpdateFlags(update_normal_vectors);
+            DEAL_II_FALLTHROUGH;
+
+          default:
+            update_flags |= UpdateFlags(update_values);
+            break;
+        }
+
+      const dealii::hp::FECollection<dim, spacedim> &fe_collection =
+        dof.get_fe_collection();
+      IDScratchData<dim, spacedim, Number> data(mapping,
+                                                fe_collection,
+                                                q,
+                                                update_flags);
+
+      // loop over all cells
+      for (const auto &cell : dof.active_cell_iterators())
+        if (cell->is_locally_owned())
+          {
+            // initialize for this cell
+            data.x_fe_values.reinit(cell);
+
+            const dealii::FEValues<dim, spacedim> &fe_values =
+              data.x_fe_values.get_present_fe_values();
+            const unsigned int n_q_points = fe_values.n_quadrature_points;
+            data.resize_vectors(n_q_points, n_components);
+
+            if (update_flags & update_values)
+              fe_values.get_function_values(fe_function, data.function_values);
+            if (update_flags & update_gradients)
+              fe_values.get_function_gradients(fe_function,
+                                               data.function_grads);
+
+            difference(cell->active_cell_index()) =
+              integrate_difference_inner<dim, spacedim, Number>(exact_solution,
+                                                                norm,
+                                                                weight,
+                                                                update_flags,
+                                                                exponent,
+                                                                n_components,
+                                                                data);
+          }
+        else
+          // the cell is a ghost cell or is artificial. write a zero into the
+          // corresponding value of the returned vector
+          difference(cell->active_cell_index()) = 0;
+    }
+
+    template <int dim,
+              class InVector,
+              class OutVector,
+              typename DoFHandlerType,
+              int spacedim>
+    DEAL_II_DEPRECATED static typename std::enable_if<
+      !std::is_same<typename InVector::value_type, double>::value>::type
+    do_integrate_difference(
+      const dealii::hp::MappingCollection<dim, spacedim> &mapping,
+      const DoFHandlerType &                              dof,
+      const InVector &                                    fe_function,
+      const Function<spacedim> &                          exact_solution,
+      OutVector &                                         difference,
+      const dealii::hp::QCollection<dim> &                q,
+      const NormType &                                    norm,
+      const Function<spacedim> *                          weight,
+      const double                                        exponent_1)
+    {
+      using Number = typename InVector::value_type;
+      // we mark the "exponent" parameter to this function "const" since it is
+      // strictly incoming, but we need to set it to something different later
+      // on, if necessary, so have a read-write version of it:
+      double exponent = exponent_1;
+
+      const unsigned int n_components = dof.get_fe(0).n_components();
+
+      Assert(exact_solution.n_components == n_components,
+             ExcDimensionMismatch(exact_solution.n_components, n_components));
+
+      if (weight != nullptr)
+        {
+          Assert((weight->n_components == 1) ||
+                   (weight->n_components == n_components),
+                 ExcDimensionMismatch(weight->n_components, n_components));
+        }
+
+      difference.reinit(dof.get_triangulation().n_active_cells());
+
+      switch (norm)
+        {
+          case L2_norm:
+          case H1_seminorm:
+          case H1_norm:
+          case Hdiv_seminorm:
+            exponent = 2.;
+            break;
+
+          case L1_norm:
+            exponent = 1.;
+            break;
+
+          default:
+            break;
+        }
+
+      UpdateFlags update_flags =
+        UpdateFlags(update_quadrature_points | update_JxW_values);
+      switch (norm)
+        {
+          case H1_seminorm:
+          case Hdiv_seminorm:
+          case W1p_seminorm:
+          case W1infty_seminorm:
+            update_flags |= UpdateFlags(update_gradients);
+            if (spacedim == dim + 1)
+              update_flags |= UpdateFlags(update_normal_vectors);
+
+            break;
+
+          case H1_norm:
+          case W1p_norm:
+          case W1infty_norm:
+            update_flags |= UpdateFlags(update_gradients);
+            if (spacedim == dim + 1)
+              update_flags |= UpdateFlags(update_normal_vectors);
+            DEAL_II_FALLTHROUGH;
+
+          default:
+            update_flags |= UpdateFlags(update_values);
+            break;
+        }
+
+      const dealii::hp::FECollection<dim, spacedim> &fe_collection =
+        dof.get_fe_collection();
+      DeprecatedIDScratchData<dim, spacedim, Number> data(mapping,
+                                                          fe_collection,
+                                                          q,
+                                                          update_flags);
+
+      // loop over all cells
+      for (const auto &cell : dof.active_cell_iterators())
+        if (cell->is_locally_owned())
+          {
+            // initialize for this cell
+            data.x_fe_values.reinit(cell);
+
+            const dealii::FEValues<dim, spacedim> &fe_values =
+              data.x_fe_values.get_present_fe_values();
+            const unsigned int n_q_points = fe_values.n_quadrature_points;
+            data.resize_vectors(n_q_points, n_components);
+
+            if (update_flags & update_values)
+              fe_values.get_function_values(fe_function, data.function_values);
+            if (update_flags & update_gradients)
+              fe_values.get_function_gradients(fe_function,
+                                               data.function_grads);
+
+            difference(cell->active_cell_index()) =
+              integrate_difference_inner<dim, spacedim, Number>(exact_solution,
+                                                                norm,
+                                                                weight,
+                                                                update_flags,
+                                                                exponent,
+                                                                n_components,
+                                                                data);
+          }
+        else
+          // the cell is a ghost cell or is artificial. write a zero into the
+          // corresponding value of the returned vector
+          difference(cell->active_cell_index()) = 0;
+    }
+
+  } // namespace internal
+
+  template <int dim, class InVector, class OutVector, int spacedim>
+  void
+  integrate_difference(
+    const Mapping<dim, spacedim> &                           mapping,
+    const DoFHandler<dim, spacedim> &                        dof,
+    const InVector &                                         fe_function,
+    const Function<spacedim, typename InVector::value_type> &exact_solution,
+    OutVector &                                              difference,
+    const Quadrature<dim> &                                  q,
+    const NormType &                                         norm,
+    const Function<spacedim> *                               weight,
+    const double                                             exponent)
+  {
+    internal ::do_integrate_difference(hp::MappingCollection<dim, spacedim>(
+                                         mapping),
+                                       dof,
+                                       fe_function,
+                                       exact_solution,
+                                       difference,
+                                       hp::QCollection<dim>(q),
+                                       norm,
+                                       weight,
+                                       exponent);
+  }
+
+  template <int dim, class InVector, class OutVector, int spacedim>
+  DEAL_II_DEPRECATED typename std::enable_if<
+    !std::is_same<typename InVector::value_type, double>::value>::type
+  integrate_difference(const Mapping<dim, spacedim> &   mapping,
+                       const DoFHandler<dim, spacedim> &dof,
+                       const InVector &                 fe_function,
+                       const Function<spacedim> &       exact_solution,
+                       OutVector &                      difference,
+                       const Quadrature<dim> &          q,
+                       const NormType &                 norm,
+                       const Function<spacedim> *       weight,
+                       const double                     exponent)
+  {
+    internal ::do_integrate_difference(hp::MappingCollection<dim, spacedim>(
+                                         mapping),
+                                       dof,
+                                       fe_function,
+                                       exact_solution,
+                                       difference,
+                                       hp::QCollection<dim>(q),
+                                       norm,
+                                       weight,
+                                       exponent);
+  }
+
+
+  template <int dim, class InVector, class OutVector, int spacedim>
+  void
+  integrate_difference(
+    const DoFHandler<dim, spacedim> &                        dof,
+    const InVector &                                         fe_function,
+    const Function<spacedim, typename InVector::value_type> &exact_solution,
+    OutVector &                                              difference,
+    const Quadrature<dim> &                                  q,
+    const NormType &                                         norm,
+    const Function<spacedim> *                               weight,
+    const double                                             exponent)
+  {
+    internal ::do_integrate_difference(
+      hp::StaticMappingQ1<dim, spacedim>::mapping_collection,
+      dof,
+      fe_function,
+      exact_solution,
+      difference,
+      hp::QCollection<dim>(q),
+      norm,
+      weight,
+      exponent);
+  }
+
+
+  template <int dim, class InVector, class OutVector, int spacedim>
+  DEAL_II_DEPRECATED typename std::enable_if<
+    !std::is_same<typename InVector::value_type, double>::value>::type
+  integrate_difference(const DoFHandler<dim, spacedim> &dof,
+                       const InVector &                 fe_function,
+                       const Function<spacedim> &       exact_solution,
+                       OutVector &                      difference,
+                       const Quadrature<dim> &          q,
+                       const NormType &                 norm,
+                       const Function<spacedim> *       weight,
+                       const double                     exponent)
+  {
+    internal ::do_integrate_difference(
+      hp::StaticMappingQ1<dim, spacedim>::mapping_collection,
+      dof,
+      fe_function,
+      exact_solution,
+      difference,
+      hp::QCollection<dim>(q),
+      norm,
+      weight,
+      exponent);
+  }
+
+
+
+  template <int dim, class InVector, class OutVector, int spacedim>
+  void
+  integrate_difference(
+    const dealii::hp::MappingCollection<dim, spacedim> &     mapping,
+    const dealii::hp::DoFHandler<dim, spacedim> &            dof,
+    const InVector &                                         fe_function,
+    const Function<spacedim, typename InVector::value_type> &exact_solution,
+    OutVector &                                              difference,
+    const dealii::hp::QCollection<dim> &                     q,
+    const NormType &                                         norm,
+    const Function<spacedim> *                               weight,
+    const double                                             exponent)
+  {
+    internal ::do_integrate_difference(hp::MappingCollection<dim, spacedim>(
+                                         mapping),
+                                       dof,
+                                       fe_function,
+                                       exact_solution,
+                                       difference,
+                                       q,
+                                       norm,
+                                       weight,
+                                       exponent);
+  }
+
+  template <int dim, class InVector, class OutVector, int spacedim>
+  DEAL_II_DEPRECATED typename std::enable_if<
+    !std::is_same<typename InVector::value_type, double>::value>::type
+  integrate_difference(
+    const dealii::hp::MappingCollection<dim, spacedim> &mapping,
+    const dealii::hp::DoFHandler<dim, spacedim> &       dof,
+    const InVector &                                    fe_function,
+    const Function<spacedim> &                          exact_solution,
+    OutVector &                                         difference,
+    const dealii::hp::QCollection<dim> &                q,
+    const NormType &                                    norm,
+    const Function<spacedim> *                          weight,
+    const double                                        exponent)
+  {
+    internal ::do_integrate_difference(hp::MappingCollection<dim, spacedim>(
+                                         mapping),
+                                       dof,
+                                       fe_function,
+                                       exact_solution,
+                                       difference,
+                                       q,
+                                       norm,
+                                       weight,
+                                       exponent);
+  }
+
+
+  template <int dim, class InVector, class OutVector, int spacedim>
+  void
+  integrate_difference(
+    const dealii::hp::DoFHandler<dim, spacedim> &            dof,
+    const InVector &                                         fe_function,
+    const Function<spacedim, typename InVector::value_type> &exact_solution,
+    OutVector &                                              difference,
+    const dealii::hp::QCollection<dim> &                     q,
+    const NormType &                                         norm,
+    const Function<spacedim> *                               weight,
+    const double                                             exponent)
+  {
+    internal ::do_integrate_difference(
+      hp::StaticMappingQ1<dim, spacedim>::mapping_collection,
+      dof,
+      fe_function,
+      exact_solution,
+      difference,
+      q,
+      norm,
+      weight,
+      exponent);
+  }
+
+  template <int dim, class InVector, class OutVector, int spacedim>
+  DEAL_II_DEPRECATED typename std::enable_if<
+    !std::is_same<typename InVector::value_type, double>::value>::type
+  integrate_difference(const dealii::hp::DoFHandler<dim, spacedim> &dof,
+                       const InVector &                             fe_function,
+                       const Function<spacedim> &          exact_solution,
+                       OutVector &                         difference,
+                       const dealii::hp::QCollection<dim> &q,
+                       const NormType &                    norm,
+                       const Function<spacedim> *          weight,
+                       const double                        exponent)
+  {
+    internal ::do_integrate_difference(
+      hp::StaticMappingQ1<dim, spacedim>::mapping_collection,
+      dof,
+      fe_function,
+      exact_solution,
+      difference,
+      q,
+      norm,
+      weight,
+      exponent);
+  }
+
+  template <int dim, int spacedim, class InVector>
+  double
+  compute_global_error(const Triangulation<dim, spacedim> &tria,
+                       const InVector &                    cellwise_error,
+                       const NormType &                    norm,
+                       const double                        exponent)
+  {
+    Assert(cellwise_error.size() == tria.n_active_cells(),
+           ExcMessage("input vector cell_error has invalid size!"));
+#ifdef DEBUG
+    {
+      // check that off-processor entries are zero. Otherwise we will compute
+      // wrong results below!
+      typename InVector::size_type                                i = 0;
+      typename Triangulation<dim, spacedim>::active_cell_iterator it =
+        tria.begin_active();
+      for (; i < cellwise_error.size(); ++i, ++it)
+        if (!it->is_locally_owned())
+          Assert(
+            std::fabs(cellwise_error[i]) < 1e-20,
+            ExcMessage(
+              "cellwise_error of cells that are not locally owned need to be zero!"));
+    }
+#endif
+
+    MPI_Comm comm = MPI_COMM_SELF;
+#ifdef DEAL_II_WITH_MPI
+    if (const parallel::TriangulationBase<dim, spacedim> *ptria =
+          dynamic_cast<const parallel::TriangulationBase<dim, spacedim> *>(
+            &tria))
+      comm = ptria->get_communicator();
+#endif
+
+    switch (norm)
+      {
+        case L2_norm:
+        case H1_seminorm:
+        case H1_norm:
+        case Hdiv_seminorm:
+          {
+            const double local = cellwise_error.l2_norm();
+            return std::sqrt(Utilities::MPI::sum(local * local, comm));
+          }
+
+        case L1_norm:
+          {
+            const double local = cellwise_error.l1_norm();
+            return Utilities::MPI::sum(local, comm);
+          }
+
+        case Linfty_norm:
+        case W1infty_seminorm:
+          {
+            const double local = cellwise_error.linfty_norm();
+            return Utilities::MPI::max(local, comm);
+          }
+
+        case W1infty_norm:
+          {
+            AssertThrow(false,
+                        ExcMessage(
+                          "compute_global_error() is impossible for "
+                          "the W1infty_norm. See the documentation for "
+                          "NormType::W1infty_norm for more information."));
+            return std::numeric_limits<double>::infinity();
+          }
+
+        case mean:
+          {
+            // Note: mean is defined as int_\Omega f = sum_K \int_K f, so we
+            // need the sum of the cellwise errors not the Euclidean mean
+            // value that is returned by Vector<>::mean_value().
+            const double local =
+              cellwise_error.mean_value() * cellwise_error.size();
+            return Utilities::MPI::sum(local, comm);
+          }
+
+        case Lp_norm:
+        case W1p_norm:
+        case W1p_seminorm:
+          {
+            double                       local = 0;
+            typename InVector::size_type i;
+            typename Triangulation<dim, spacedim>::active_cell_iterator it =
+              tria.begin_active();
+            for (i = 0; i < cellwise_error.size(); ++i, ++it)
+              if (it->is_locally_owned())
+                local += std::pow(cellwise_error[i], exponent);
+
+            return std::pow(Utilities::MPI::sum(local, comm), 1. / exponent);
+          }
+
+        default:
+          AssertThrow(false, ExcNotImplemented());
+          break;
+      }
+    return 0.0;
+  }
+} // namespace VectorTools
+
+DEAL_II_NAMESPACE_CLOSE
+
+#endif // dealii_vector_tools_integrate_difference_templates_h
diff --git a/include/deal.II/numerics/vector_tools_interpolate.templates.h b/include/deal.II/numerics/vector_tools_interpolate.templates.h
new file mode 100644 (file)
index 0000000..88d5a14
--- /dev/null
@@ -0,0 +1,986 @@
+// ---------------------------------------------------------------------
+//
+// Copyright (C) 1998 - 2014 by the deal.II authors
+//
+// This file is part of the deal.II library.
+//
+// The deal.II library is free software; you can use it, redistribute
+// it, and/or modify it under the terms of the GNU Lesser General
+// Public License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+// The full text of the license can be found in the file LICENSE.md at
+// the top level directory of deal.II.
+//
+// ---------------------------------------------------------------------
+
+#ifndef dealii_vector_tools_interpolate_templates_h
+#define dealii_vector_tools_interpolate_templates_h
+
+
+#include <deal.II/fe/fe_q.h>
+#include <deal.II/fe/fe_system.h>
+
+#include <deal.II/grid/grid_tools.h>
+#include <deal.II/grid/intergrid_map.h>
+
+#include <deal.II/hp/fe_values.h>
+
+#include <deal.II/lac/block_vector.h>
+#include <deal.II/lac/la_parallel_block_vector.h>
+#include <deal.II/lac/la_parallel_vector.h>
+#include <deal.II/lac/la_vector.h>
+#include <deal.II/lac/petsc_block_vector.h>
+#include <deal.II/lac/petsc_vector.h>
+#include <deal.II/lac/trilinos_parallel_block_vector.h>
+#include <deal.II/lac/trilinos_vector.h>
+
+#include <deal.II/numerics/vector_tools.h>
+
+DEAL_II_NAMESPACE_OPEN
+
+namespace VectorTools
+{
+  // This namespace contains the actual implementation called
+  // by VectorTools::interpolate and variants (such as
+  // VectorTools::interpolate_by_material_id).
+  namespace internal
+  {
+    // A small helper function to transform a component range starting
+    // at offset from the real to the unit cell according to the
+    // supplied conformity. The function_values vector is transformed
+    // in place.
+    //
+    // FIXME: This should be refactored into the mapping (i.e.
+    // implement the inverse function of Mapping<dim, spacedim>::transform).
+    // Further, the finite element should make the information about
+    // the correct mapping directly accessible (i.e. which MappingKind
+    // should be used). Using fe.conforming_space might be a bit of a
+    // problem because we only support doing nothing, Hcurl, and Hdiv
+    // conforming mappings.
+    //
+    // Input:
+    //  conformity: conformity of the finite element, used to select
+    //              appropriate type of transformation
+    //  fe_values_jacobians: used for jacobians (and inverses of
+    //                        jacobians). the object is supposed to be
+    //                        reinit()'d for the current cell
+    //  function_values, offset: function_values is manipulated in place
+    //                           starting at position offset
+    template <int dim, int spacedim, typename FEValuesType, typename T3>
+    void
+    transform(const typename FiniteElementData<dim>::Conformity conformity,
+              const unsigned int                                offset,
+              const FEValuesType &fe_values_jacobians,
+              T3 &                function_values)
+    {
+      switch (conformity)
+        {
+          case FiniteElementData<dim>::Hcurl:
+            // See Monk, Finite Element Methods for Maxwell's Equations,
+            // p. 77ff, formula (3.76) and Corollary 3.58.
+            // For given mapping F_K: \hat K \to K, we have to transform
+            //  \hat u = (dF_K)^T u\circ F_K
+
+            for (unsigned int i = 0; i < function_values.size(); ++i)
+              {
+                const auto &jacobians =
+                  fe_values_jacobians.get_present_fe_values().get_jacobians();
+
+                const ArrayView<typename T3::value_type::value_type> source(
+                  &function_values[i][0] + offset, dim);
+
+                Tensor<1,
+                       dim,
+                       typename ProductType<typename T3::value_type::value_type,
+                                            double>::type>
+                  destination;
+
+                // value[m] <- sum jacobian_transpose[m][n] * old_value[n]:
+                TensorAccessors::contract<1, 2, 1, dim>(
+                  destination, jacobians[i].transpose(), source);
+
+                // now copy things back into the input=output vector
+                for (unsigned int d = 0; d < dim; ++d)
+                  source[d] = destination[d];
+              }
+            break;
+
+          case FiniteElementData<dim>::Hdiv:
+            // See Monk, Finite Element Methods for Maxwell's Equations,
+            // p. 79ff, formula (3.77) and Lemma 3.59.
+            // For given mapping F_K: \hat K \to K, we have to transform
+            //  \hat w = det(dF_K) (dF_K)^{-1} w\circ F_K
+
+            for (unsigned int i = 0; i < function_values.size(); ++i)
+              {
+                const auto &jacobians =
+                  fe_values_jacobians.get_present_fe_values().get_jacobians();
+                const auto &inverse_jacobians =
+                  fe_values_jacobians.get_present_fe_values()
+                    .get_inverse_jacobians();
+
+                const ArrayView<typename T3::value_type::value_type> source(
+                  &function_values[i][0] + offset, dim);
+
+                Tensor<1,
+                       dim,
+                       typename ProductType<typename T3::value_type::value_type,
+                                            double>::type>
+                  destination;
+
+                // value[m] <- sum inverse_jacobians[m][n] * old_value[n]:
+                TensorAccessors::contract<1, 2, 1, dim>(destination,
+                                                        inverse_jacobians[i],
+                                                        source);
+                destination *= jacobians[i].determinant();
+
+                // now copy things back into the input=output vector
+                for (unsigned int d = 0; d < dim; ++d)
+                  source[d] = destination[d];
+              }
+            break;
+
+          case FiniteElementData<dim>::H1:
+            DEAL_II_FALLTHROUGH;
+          case FiniteElementData<dim>::L2:
+            // See Monk, Finite Element Methods for Maxwell's Equations,
+            // p. 77ff, formula (3.74).
+            // For given mapping F_K: \hat K \to K, we have to transform
+            //  \hat p = p\circ F_K
+            //  i.e., do nothing.
+            break;
+
+          default:
+            // In case we deal with an unknown conformity, just assume we
+            // deal with a Lagrange element and do nothing.
+            break;
+
+        } /*switch*/
+    }
+
+
+    // A small helper function that iteratively applies above transform
+    // function to a vector function_values recursing over a given finite
+    // element decomposing it into base elements:
+    //
+    // Input
+    //   fe: the full finite element corresponding to function_values
+    //   [ rest see above]
+    // Output: the offset after we have handled the element at
+    //   a given offset
+    template <int dim, int spacedim, typename FEValuesType, typename T3>
+    unsigned int
+    apply_transform(const FiniteElement<dim, spacedim> &fe,
+                    const unsigned int                  offset,
+                    const FEValuesType &                fe_values_jacobians,
+                    T3 &                                function_values)
+    {
+      if (const auto *system =
+            dynamic_cast<const FESystem<dim, spacedim> *>(&fe))
+        {
+          // In case of an FESystem transform every (vector) component
+          // separately:
+          unsigned current_offset = offset;
+          for (unsigned int i = 0; i < system->n_base_elements(); ++i)
+            {
+              const auto &base_fe      = system->base_element(i);
+              const auto  multiplicity = system->element_multiplicity(i);
+              for (unsigned int m = 0; m < multiplicity; ++m)
+                {
+                  // recursively call apply_transform to make sure to
+                  // correctly handle nested fe systems.
+                  current_offset = apply_transform(base_fe,
+                                                   current_offset,
+                                                   fe_values_jacobians,
+                                                   function_values);
+                }
+            }
+          return current_offset;
+        }
+      else
+        {
+          transform<dim, spacedim>(fe.conforming_space,
+                                   offset,
+                                   fe_values_jacobians,
+                                   function_values);
+          return (offset + fe.n_components());
+        }
+    }
+
+
+    // Internal implementation of interpolate that takes a generic functor
+    // function such that function(cell) is of type
+    // Function<spacedim, typename VectorType::value_type>*
+    //
+    // A given cell is skipped if function(cell) == nullptr
+    template <int dim,
+              int spacedim,
+              typename VectorType,
+              template <int, int> class DoFHandlerType,
+              typename T>
+    void
+    interpolate(const Mapping<dim, spacedim> &       mapping,
+                const DoFHandlerType<dim, spacedim> &dof_handler,
+                T &                                  function,
+                VectorType &                         vec,
+                const ComponentMask &                component_mask)
+    {
+      Assert(component_mask.represents_n_components(
+               dof_handler.get_fe_collection().n_components()),
+             ExcMessage(
+               "The number of components in the mask has to be either "
+               "zero or equal to the number of components in the finite "
+               "element."));
+
+      Assert(vec.size() == dof_handler.n_dofs(),
+             ExcDimensionMismatch(vec.size(), dof_handler.n_dofs()));
+
+      Assert(component_mask.n_selected_components(
+               dof_handler.get_fe_collection().n_components()) > 0,
+             ComponentMask::ExcNoComponentSelected());
+
+      //
+      // Computing the generalized interpolant isn't quite as straightforward
+      // as for classical Lagrange elements. A major complication is the fact
+      // it generally doesn't hold true that a function evaluates to the same
+      // dof coefficient on different cells. This means *setting* the value
+      // of a (global) degree of freedom computed on one cell doesn't
+      // necessarily lead to the same result when computed on a neighboring
+      // cell (that shares the same global degree of freedom).
+      //
+      // We thus, do the following operation:
+      //
+      // On each cell:
+      //
+      //  - We first determine all function values u(x_i) in generalized
+      //    support points
+      //
+      //  - We transform these function values back to the unit cell
+      //    according to the conformity of the component (scalar, Hdiv, or
+      //    Hcurl conforming); see [Monk, Finite Element Methods for Maxwell's
+      //    Equations, p.77ff Section 3.9] for details. This results in
+      //    \hat u(\hat x_i)
+      //
+      //  - We convert these generalized support point values to nodal values
+      //
+      //  - For every global dof we take the average 1 / n_K \sum_{K} dof_K
+      //    where n_K is the number of cells sharing the global dof and dof_K
+      //    is the computed value on the cell K.
+      //
+      // For every degree of freedom that is shared by k cells, we compute
+      // its value on all k cells and take the weighted average with respect
+      // to the JxW values.
+      //
+
+      using number = typename VectorType::value_type;
+
+      const hp::FECollection<dim, spacedim> &fe(
+        dof_handler.get_fe_collection());
+
+      std::vector<types::global_dof_index> dofs_on_cell(fe.max_dofs_per_cell());
+
+      // Temporary storage for cell-wise interpolation operation. We store a
+      // variant for every fe we encounter to speed up resizing operations.
+      // The first vector is used for local function evaluation. The vector
+      // dof_values is used to store intermediate cell-wise interpolation
+      // results (see the detailed explanation in the for loop further down
+      // below).
+
+      std::vector<std::vector<Vector<number>>> fe_function_values(fe.size());
+      std::vector<std::vector<number>>         fe_dof_values(fe.size());
+
+      // We will need two temporary global vectors that store the new values
+      // and weights.
+      VectorType interpolation;
+      VectorType weights;
+      interpolation.reinit(vec);
+      weights.reinit(vec);
+
+      // Store locally owned dofs, so that we can skip all non-local dofs,
+      // if they do not need to be interpolated.
+      const IndexSet locally_owned_dofs = vec.locally_owned_elements();
+
+      // We use an FEValues object to transform all generalized support
+      // points from the unit cell to the real cell coordinates. Thus,
+      // initialize a quadrature with all generalized support points and
+      // create an FEValues object with it.
+
+      hp::QCollection<dim> support_quadrature;
+      for (unsigned int fe_index = 0; fe_index < fe.size(); ++fe_index)
+        {
+          const auto &points = fe[fe_index].get_generalized_support_points();
+          support_quadrature.push_back(Quadrature<dim>(points));
+        }
+
+      const hp::MappingCollection<dim, spacedim> mapping_collection(mapping);
+
+      // An FEValues object to evaluate (generalized) support point
+      // locations as well as Jacobians and their inverses.
+      // the latter are only needed for Hcurl or Hdiv conforming elements,
+      // but we'll just always include them.
+      hp::FEValues<dim, spacedim> fe_values(mapping_collection,
+                                            fe,
+                                            support_quadrature,
+                                            update_quadrature_points |
+                                              update_jacobians |
+                                              update_inverse_jacobians);
+
+      //
+      // Now loop over all locally owned, active cells.
+      //
+
+      for (const auto &cell : dof_handler.active_cell_iterators())
+        {
+          // If this cell is not locally owned, do nothing.
+          if (!cell->is_locally_owned())
+            continue;
+
+          const unsigned int fe_index = cell->active_fe_index();
+
+          // Do nothing if there are no local degrees of freedom.
+          if (fe[fe_index].dofs_per_cell == 0)
+            continue;
+
+          // Skip processing of the current cell if the function object is
+          // invalid. This is used by interpolate_by_material_id to skip
+          // interpolating over cells with unknown material id.
+          if (!function(cell))
+            continue;
+
+          // Get transformed, generalized support points
+          fe_values.reinit(cell);
+          const std::vector<Point<spacedim>> &generalized_support_points =
+            fe_values.get_present_fe_values().get_quadrature_points();
+
+          // Get indices of the dofs on this cell
+          const auto n_dofs = fe[fe_index].dofs_per_cell;
+          dofs_on_cell.resize(n_dofs);
+          cell->get_dof_indices(dofs_on_cell);
+
+          // Prepare temporary storage
+          auto &function_values = fe_function_values[fe_index];
+          auto &dof_values      = fe_dof_values[fe_index];
+
+          const auto n_components = fe[fe_index].n_components();
+          function_values.resize(generalized_support_points.size(),
+                                 Vector<number>(n_components));
+          dof_values.resize(n_dofs);
+
+          // Get all function values:
+          Assert(
+            n_components == function(cell)->n_components,
+            ExcDimensionMismatch(dof_handler.get_fe_collection().n_components(),
+                                 function(cell)->n_components));
+          function(cell)->vector_value_list(generalized_support_points,
+                                            function_values);
+
+          {
+            // Before we can average, we have to transform all function values
+            // from the real cell back to the unit cell. We query the finite
+            // element for the correct transformation. Matters get a bit more
+            // complicated because we have to apply said transformation for
+            // every base element.
+
+            const unsigned int offset =
+              apply_transform(fe[fe_index],
+                              /* starting_offset = */ 0,
+                              fe_values,
+                              function_values);
+            (void)offset;
+            Assert(offset == n_components, ExcInternalError());
+          }
+
+          FETools::convert_generalized_support_point_values_to_dof_values(
+            fe[fe_index], function_values, dof_values);
+
+          for (unsigned int i = 0; i < n_dofs; ++i)
+            {
+              const auto &nonzero_components =
+                fe[fe_index].get_nonzero_components(i);
+
+              // Figure out whether the component mask applies. We assume
+              // that we are allowed to set degrees of freedom if at least
+              // one of the components (of the dof) is selected.
+              bool selected = false;
+              for (unsigned int c = 0; c < nonzero_components.size(); ++c)
+                selected =
+                  selected || (nonzero_components[c] && component_mask[c]);
+
+              if (selected)
+                {
+#ifdef DEBUG
+                  // make sure that all selected base elements are indeed
+                  // interpolatory
+
+                  if (const auto fe_system =
+                        dynamic_cast<const FESystem<dim> *>(&fe[fe_index]))
+                    {
+                      const auto index =
+                        fe_system->system_to_base_index(i).first.first;
+                      Assert(fe_system->base_element(index)
+                               .has_generalized_support_points(),
+                             ExcMessage("The component mask supplied to "
+                                        "VectorTools::interpolate selects a "
+                                        "non-interpolatory element."));
+                    }
+#endif
+
+                  // Add local values to the global vectors
+                  ::dealii::internal::ElementAccess<VectorType>::add(
+                    dof_values[i], dofs_on_cell[i], interpolation);
+                  ::dealii::internal::ElementAccess<VectorType>::add(
+                    typename VectorType::value_type(1.0),
+                    dofs_on_cell[i],
+                    weights);
+                }
+              else
+                {
+                  // If a component is ignored, copy the dof values
+                  // from the vector "vec", but only if they are locally
+                  // available
+                  if (locally_owned_dofs.is_element(dofs_on_cell[i]))
+                    {
+                      const auto value =
+                        ::dealii::internal::ElementAccess<VectorType>::get(
+                          vec, dofs_on_cell[i]);
+                      ::dealii::internal::ElementAccess<VectorType>::add(
+                        value, dofs_on_cell[i], interpolation);
+                      ::dealii::internal::ElementAccess<VectorType>::add(
+                        typename VectorType::value_type(1.0),
+                        dofs_on_cell[i],
+                        weights);
+                    }
+                }
+            }
+        } /* loop over dof_handler.active_cell_iterators() */
+
+      interpolation.compress(VectorOperation::add);
+      weights.compress(VectorOperation::add);
+
+      for (const auto i : interpolation.locally_owned_elements())
+        {
+          const auto weight =
+            ::dealii::internal::ElementAccess<VectorType>::get(weights, i);
+
+          // See if we touched this DoF at all. If so, set the average
+          // of the value we computed in the output vector. Otherwise,
+          // don't touch the value at all.
+          if (weight != number(0))
+            {
+              const auto value =
+                ::dealii::internal::ElementAccess<VectorType>::get(
+                  interpolation, i);
+              ::dealii::internal::ElementAccess<VectorType>::set(value / weight,
+                                                                 i,
+                                                                 vec);
+            }
+        }
+      vec.compress(VectorOperation::insert);
+    }
+
+  } // namespace internal
+
+
+
+  template <int dim,
+            int spacedim,
+            typename VectorType,
+            template <int, int> class DoFHandlerType>
+  void
+  interpolate(
+    const Mapping<dim, spacedim> &                             mapping,
+    const DoFHandlerType<dim, spacedim> &                      dof_handler,
+    const Function<spacedim, typename VectorType::value_type> &function,
+    VectorType &                                               vec,
+    const ComponentMask &                                      component_mask)
+  {
+    Assert(dof_handler.get_fe_collection().n_components() ==
+             function.n_components,
+           ExcDimensionMismatch(dof_handler.get_fe_collection().n_components(),
+                                function.n_components));
+
+    // Create a small lambda capture wrapping function and call the
+    // internal implementation
+    const auto function_map = [&function](
+      const typename DoFHandlerType<dim, spacedim>::active_cell_iterator &)
+      -> const Function<spacedim, typename VectorType::value_type> *
+    {
+      return &function;
+    };
+
+    internal::interpolate(
+      mapping, dof_handler, function_map, vec, component_mask);
+  }
+
+
+
+  template <int dim,
+            int spacedim,
+            typename VectorType,
+            template <int, int> class DoFHandlerType>
+  void
+  interpolate(
+    const DoFHandlerType<dim, spacedim> &                      dof,
+    const Function<spacedim, typename VectorType::value_type> &function,
+    VectorType &                                               vec,
+    const ComponentMask &                                      component_mask)
+  {
+    interpolate(StaticMappingQ1<dim, spacedim>::mapping,
+                dof,
+                function,
+                vec,
+                component_mask);
+  }
+
+
+
+  template <int dim, class InVector, class OutVector, int spacedim>
+  void
+  interpolate(const DoFHandler<dim, spacedim> &dof_1,
+              const DoFHandler<dim, spacedim> &dof_2,
+              const FullMatrix<double> &       transfer,
+              const InVector &                 data_1,
+              OutVector &                      data_2)
+  {
+    using number = typename OutVector::value_type;
+    Vector<number> cell_data_1(dof_1.get_fe().dofs_per_cell);
+    Vector<number> cell_data_2(dof_2.get_fe().dofs_per_cell);
+
+    // Reset output vector.
+    data_2 = static_cast<number>(0);
+
+    // Store how many cells share each dof (unghosted).
+    OutVector touch_count;
+    touch_count.reinit(data_2);
+
+    std::vector<types::global_dof_index> local_dof_indices(
+      dof_2.get_fe().dofs_per_cell);
+
+    typename DoFHandler<dim, spacedim>::active_cell_iterator cell_1 =
+      dof_1.begin_active();
+    typename DoFHandler<dim, spacedim>::active_cell_iterator cell_2 =
+      dof_2.begin_active();
+    const typename DoFHandler<dim, spacedim>::cell_iterator end_1 = dof_1.end();
+
+    for (; cell_1 != end_1; ++cell_1, ++cell_2)
+      {
+        if (cell_1->is_locally_owned())
+          {
+            Assert(cell_2->is_locally_owned(), ExcInternalError());
+
+            // Perform dof interpolation.
+            cell_1->get_dof_values(data_1, cell_data_1);
+            transfer.vmult(cell_data_2, cell_data_1);
+
+            cell_2->get_dof_indices(local_dof_indices);
+
+            // Distribute cell vector.
+            for (unsigned int j = 0; j < dof_2.get_fe().dofs_per_cell; ++j)
+              {
+                ::dealii::internal::ElementAccess<OutVector>::add(
+                  cell_data_2(j), local_dof_indices[j], data_2);
+
+                // Count cells that share each dof.
+                ::dealii::internal::ElementAccess<OutVector>::add(
+                  static_cast<number>(1), local_dof_indices[j], touch_count);
+              }
+          }
+      }
+
+    // Collect information over all the parallel processes.
+    data_2.compress(VectorOperation::add);
+    touch_count.compress(VectorOperation::add);
+
+    // Compute the mean value of the sum which has been placed in
+    // each entry of the output vector only at locally owned elements.
+    for (const auto &i : data_2.locally_owned_elements())
+      {
+        const number touch_count_i =
+          ::dealii::internal::ElementAccess<OutVector>::get(touch_count, i);
+
+        Assert(touch_count_i != static_cast<number>(0), ExcInternalError());
+
+        const number value =
+          ::dealii::internal::ElementAccess<OutVector>::get(data_2, i) /
+          touch_count_i;
+
+        ::dealii::internal::ElementAccess<OutVector>::set(value, i, data_2);
+      }
+
+    // Compress data_2 to set the proper values on all the parallel processes.
+    data_2.compress(VectorOperation::insert);
+  }
+
+
+  template <int dim,
+            int spacedim,
+            template <int, int> class DoFHandlerType,
+            typename VectorType>
+  void
+  get_position_vector(const DoFHandlerType<dim, spacedim> &dh,
+                      VectorType &                         vector,
+                      const ComponentMask &                mask)
+  {
+    AssertDimension(vector.size(), dh.n_dofs());
+    const FiniteElement<dim, spacedim> &fe = dh.get_fe();
+
+    // Construct default fe_mask;
+    const ComponentMask fe_mask(
+      mask.size() ? mask :
+                    ComponentMask(fe.get_nonzero_components(0).size(), true));
+
+    AssertDimension(fe_mask.size(), fe.get_nonzero_components(0).size());
+
+    std::vector<unsigned int> fe_to_real(fe_mask.size(),
+                                         numbers::invalid_unsigned_int);
+    unsigned int              size = 0;
+    for (unsigned int i = 0; i < fe_mask.size(); ++i)
+      {
+        if (fe_mask[i])
+          fe_to_real[i] = size++;
+      }
+    Assert(
+      size == spacedim,
+      ExcMessage(
+        "The Component Mask you provided is invalid. It has to select exactly spacedim entries."));
+
+
+    if (fe.has_support_points())
+      {
+        const Quadrature<dim> quad(fe.get_unit_support_points());
+
+        MappingQGeneric<dim, spacedim> map_q(fe.degree);
+        FEValues<dim, spacedim> fe_v(map_q, fe, quad, update_quadrature_points);
+        std::vector<types::global_dof_index> dofs(fe.dofs_per_cell);
+
+        AssertDimension(fe.dofs_per_cell, fe.get_unit_support_points().size());
+        Assert(fe.is_primitive(),
+               ExcMessage("FE is not Primitive! This won't work."));
+
+        for (const auto &cell : dh.active_cell_iterators())
+          if (cell->is_locally_owned())
+            {
+              fe_v.reinit(cell);
+              cell->get_dof_indices(dofs);
+              const std::vector<Point<spacedim>> &points =
+                fe_v.get_quadrature_points();
+              for (unsigned int q = 0; q < points.size(); ++q)
+                {
+                  const unsigned int comp =
+                    fe.system_to_component_index(q).first;
+                  if (fe_mask[comp])
+                    ::dealii::internal::ElementAccess<VectorType>::set(
+                      points[q][fe_to_real[comp]], dofs[q], vector);
+                }
+            }
+      }
+    else
+      {
+        // Construct a FiniteElement with FE_Q^spacedim, and call this
+        // function again.
+        //
+        // Once we have this, interpolate with the given finite element
+        // to get a Mapping which is interpolatory at the support points
+        // of FE_Q(fe.degree())
+        const FESystem<dim, spacedim> *fe_system =
+          dynamic_cast<const FESystem<dim, spacedim> *>(&fe);
+        Assert(fe_system, ExcNotImplemented());
+        unsigned int degree = numbers::invalid_unsigned_int;
+
+        // Get information about the blocks
+        for (unsigned int i = 0; i < fe_mask.size(); ++i)
+          if (fe_mask[i])
+            {
+              const unsigned int base_i =
+                fe_system->component_to_base_index(i).first;
+              Assert(degree == numbers::invalid_unsigned_int ||
+                       degree == fe_system->base_element(base_i).degree,
+                     ExcNotImplemented());
+              Assert(fe_system->base_element(base_i).is_primitive(),
+                     ExcNotImplemented());
+              degree = fe_system->base_element(base_i).degree;
+            }
+
+        // We create an intermediate FE_Q vector space, and then
+        // interpolate from that vector space to this one, by
+        // carefully selecting the right components.
+
+        FESystem<dim, spacedim> feq(FE_Q<dim, spacedim>(degree), spacedim);
+        DoFHandlerType<dim, spacedim> dhq(dh.get_triangulation());
+        dhq.distribute_dofs(feq);
+        Vector<double>      eulerq(dhq.n_dofs());
+        const ComponentMask maskq(spacedim, true);
+        get_position_vector(dhq, eulerq);
+
+        FullMatrix<double> transfer(fe.dofs_per_cell, feq.dofs_per_cell);
+        FullMatrix<double> local_transfer(feq.dofs_per_cell);
+        const std::vector<Point<dim>> &points = feq.get_unit_support_points();
+
+        // Here we construct the interpolation matrix from
+        // FE_Q^spacedim to the FiniteElement used by
+        // euler_dof_handler.
+        //
+        // In order to construct such interpolation matrix, we have to
+        // solve the following system:
+        //
+        // v_j phi_j(q_i) = w_k psi_k(q_i) = w_k delta_ki = w_i
+        //
+        // where psi_k are the basis functions for fe_q, and phi_i are
+        // the basis functions of the target space while q_i are the
+        // support points for the fe_q space. With this choice of
+        // interpolation points, on the matrices is the identity
+        // matrix, and we have to invert only one matrix. The
+        // resulting vector will be interpolatory at the support
+        // points of fe_q, even if the finite element does not have
+        // support points.
+        //
+        // Morally, we should invert the matrix T_ij = phi_i(q_j),
+        // however in general this matrix is not invertible, since
+        // there may be components which do not contribute to the
+        // displacement vector. Since we are not interested in those
+        // components, we construct a square matrix with the same
+        // number of components of the FE_Q system. The FE_Q system
+        // was constructed above in such a way that the polynomial
+        // degree of the FE_Q system and that of the given FE are the
+        // same on the cell, which should guarantee that, for the
+        // displacement components only, the interpolation matrix is
+        // invertible. We construct a mapping between indices first,
+        // and check that this is the case. If not, we bail out, not
+        // knowing what to do in this case.
+
+        std::vector<unsigned int> fe_to_feq(fe.dofs_per_cell,
+                                            numbers::invalid_unsigned_int);
+        unsigned int              index = 0;
+        for (unsigned int i = 0; i < fe.dofs_per_cell; ++i)
+          if (fe_mask[fe.system_to_component_index(i).first])
+            fe_to_feq[i] = index++;
+
+        // If index is not the same as feq.dofs_per_cell, we won't
+        // know how to invert the resulting matrix. Bail out.
+        Assert(index == feq.dofs_per_cell, ExcNotImplemented());
+
+        for (unsigned int j = 0; j < fe.dofs_per_cell; ++j)
+          {
+            const unsigned int comp_j = fe.system_to_component_index(j).first;
+            if (fe_mask[comp_j])
+              for (unsigned int i = 0; i < points.size(); ++i)
+                {
+                  if (fe_to_real[comp_j] ==
+                      feq.system_to_component_index(i).first)
+                    local_transfer(i, fe_to_feq[j]) =
+                      fe.shape_value(j, points[i]);
+                }
+          }
+
+        // Now we construct the rectangular interpolation matrix. This
+        // one is filled only with the information from the components
+        // of the displacement. The rest is set to zero.
+        local_transfer.invert(local_transfer);
+        for (unsigned int i = 0; i < fe.dofs_per_cell; ++i)
+          if (fe_to_feq[i] != numbers::invalid_unsigned_int)
+            for (unsigned int j = 0; j < feq.dofs_per_cell; ++j)
+              transfer(i, j) = local_transfer(fe_to_feq[i], j);
+
+        // The interpolation matrix is then passed to the
+        // VectorTools::interpolate() function to generate the correct
+        // interpolation.
+        interpolate(dhq, dh, transfer, eulerq, vector);
+      }
+  }
+
+  template <int dim,
+            int spacedim,
+            typename VectorType,
+            template <int, int> class DoFHandlerType>
+  void
+  interpolate_based_on_material_id(
+    const Mapping<dim, spacedim> &       mapping,
+    const DoFHandlerType<dim, spacedim> &dof_handler,
+    const std::map<types::material_id,
+                   const Function<spacedim, typename VectorType::value_type> *>
+      &                  functions,
+    VectorType &         vec,
+    const ComponentMask &component_mask)
+  {
+    // Create a small lambda capture wrapping the function map and call the
+    // internal implementation
+    const auto function_map = [&functions](
+      const typename DoFHandlerType<dim, spacedim>::active_cell_iterator &cell)
+      -> const Function<spacedim, typename VectorType::value_type> *
+    {
+      const auto function = functions.find(cell->material_id());
+      if (function != functions.end())
+        return function->second;
+      else
+        return nullptr;
+    };
+
+    internal::interpolate(
+      mapping, dof_handler, function_map, vec, component_mask);
+  }
+
+  namespace internal
+  {
+    /**
+     * Return whether the cell and all of its descendants are locally owned.
+     */
+    template <typename cell_iterator>
+    bool
+    is_locally_owned(const cell_iterator &cell)
+    {
+      if (cell->is_active())
+        return cell->is_locally_owned();
+
+      for (unsigned int c = 0; c < cell->n_children(); ++c)
+        if (!is_locally_owned(cell->child(c)))
+          return false;
+
+      return true;
+    }
+  } // namespace internal
+
+  template <int dim,
+            int spacedim,
+            typename VectorType,
+            template <int, int> class DoFHandlerType>
+  void
+  interpolate_to_different_mesh(const DoFHandlerType<dim, spacedim> &dof1,
+                                const VectorType &                   u1,
+                                const DoFHandlerType<dim, spacedim> &dof2,
+                                VectorType &                         u2)
+  {
+    Assert(GridTools::have_same_coarse_mesh(dof1, dof2),
+           ExcMessage("The two DoF handlers must represent triangulations that "
+                      "have the same coarse meshes"));
+
+    InterGridMap<DoFHandlerType<dim, spacedim>> intergridmap;
+    intergridmap.make_mapping(dof1, dof2);
+
+    AffineConstraints<typename VectorType::value_type> dummy;
+    dummy.close();
+
+    interpolate_to_different_mesh(intergridmap, u1, dummy, u2);
+  }
+
+
+
+  template <int dim,
+            int spacedim,
+            typename VectorType,
+            template <int, int> class DoFHandlerType>
+  void
+  interpolate_to_different_mesh(
+    const DoFHandlerType<dim, spacedim> &                     dof1,
+    const VectorType &                                        u1,
+    const DoFHandlerType<dim, spacedim> &                     dof2,
+    const AffineConstraints<typename VectorType::value_type> &constraints,
+    VectorType &                                              u2)
+  {
+    Assert(GridTools::have_same_coarse_mesh(dof1, dof2),
+           ExcMessage("The two DoF handlers must represent triangulations that "
+                      "have the same coarse meshes"));
+
+    InterGridMap<DoFHandlerType<dim, spacedim>> intergridmap;
+    intergridmap.make_mapping(dof1, dof2);
+
+    interpolate_to_different_mesh(intergridmap, u1, constraints, u2);
+  }
+
+  template <int dim,
+            int spacedim,
+            typename VectorType,
+            template <int, int> class DoFHandlerType>
+  void
+  interpolate_to_different_mesh(
+    const InterGridMap<DoFHandlerType<dim, spacedim>> &       intergridmap,
+    const VectorType &                                        u1,
+    const AffineConstraints<typename VectorType::value_type> &constraints,
+    VectorType &                                              u2)
+  {
+    const DoFHandlerType<dim, spacedim> &dof1 = intergridmap.get_source_grid();
+    const DoFHandlerType<dim, spacedim> &dof2 =
+      intergridmap.get_destination_grid();
+    (void)dof2;
+
+    Assert(dof1.get_fe_collection() == dof2.get_fe_collection(),
+           ExcMessage(
+             "The FECollections of both DoFHandler objects must match"));
+    Assert(u1.size() == dof1.n_dofs(),
+           ExcDimensionMismatch(u1.size(), dof1.n_dofs()));
+    Assert(u2.size() == dof2.n_dofs(),
+           ExcDimensionMismatch(u2.size(), dof2.n_dofs()));
+
+    Vector<typename VectorType::value_type> cache;
+
+    // Looping over the finest common
+    // mesh, this means that source and
+    // destination cells have to be on the
+    // same level and at least one has to
+    // be active.
+    //
+    // Therefore, loop over all cells
+    // (active and inactive) of the source
+    // grid ..
+    typename DoFHandlerType<dim, spacedim>::cell_iterator cell1 = dof1.begin();
+    const typename DoFHandlerType<dim, spacedim>::cell_iterator endc1 =
+      dof1.end();
+
+    for (; cell1 != endc1; ++cell1)
+      {
+        const typename DoFHandlerType<dim, spacedim>::cell_iterator cell2 =
+          intergridmap[cell1];
+
+        // .. and skip if source and destination
+        // cells are not on the same level ..
+        if (cell1->level() != cell2->level())
+          continue;
+        // .. or none of them is active.
+        if (!cell1->is_active() && !cell2->is_active())
+          continue;
+
+        Assert(
+          internal::is_locally_owned(cell1) ==
+            internal::is_locally_owned(cell2),
+          ExcMessage(
+            "The two Triangulations are required to have the same parallel partitioning."));
+
+        // Skip foreign cells.
+        if (cell1->is_active() && !cell1->is_locally_owned())
+          continue;
+        if (cell2->is_active() && !cell2->is_locally_owned())
+          continue;
+
+        // Get and set the corresponding
+        // dof_values by interpolation.
+        if (cell1->is_active())
+          {
+            cache.reinit(cell1->get_fe().dofs_per_cell);
+            cell1->get_interpolated_dof_values(u1,
+                                               cache,
+                                               cell1->active_fe_index());
+            cell2->set_dof_values_by_interpolation(cache,
+                                                   u2,
+                                                   cell1->active_fe_index());
+          }
+        else
+          {
+            cache.reinit(cell2->get_fe().dofs_per_cell);
+            cell1->get_interpolated_dof_values(u1,
+                                               cache,
+                                               cell2->active_fe_index());
+            cell2->set_dof_values_by_interpolation(cache,
+                                                   u2,
+                                                   cell2->active_fe_index());
+          }
+      }
+
+    // finish the work on parallel vectors
+    u2.compress(VectorOperation::insert);
+    // Apply hanging node constraints.
+    constraints.distribute(u2);
+  }
+} // namespace VectorTools
+
+DEAL_II_NAMESPACE_CLOSE
+
+#endif // dealii_vector_tools_interpolate_templates_h
diff --git a/include/deal.II/numerics/vector_tools_mean_value.templates.h b/include/deal.II/numerics/vector_tools_mean_value.templates.h
new file mode 100644 (file)
index 0000000..ea15f69
--- /dev/null
@@ -0,0 +1,207 @@
+// ---------------------------------------------------------------------
+//
+// Copyright (C) 1998 - 2017 by the deal.II authors
+//
+// This file is part of the deal.II library.
+//
+// The deal.II library is free software; you can use it, redistribute
+// it, and/or modify it under the terms of the GNU Lesser General
+// Public License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+// The full text of the license can be found in the file LICENSE.md at
+// the top level directory of deal.II.
+//
+// ---------------------------------------------------------------------
+
+#ifndef dealii_vector_tools_mean_value_templates_h
+#define dealii_vector_tools_mean_value_templates_h
+
+
+#include <deal.II/fe/fe_values.h>
+
+#include <deal.II/lac/block_vector.h>
+#include <deal.II/lac/la_parallel_block_vector.h>
+#include <deal.II/lac/la_parallel_vector.h>
+#include <deal.II/lac/la_vector.h>
+#include <deal.II/lac/petsc_block_vector.h>
+#include <deal.II/lac/petsc_vector.h>
+#include <deal.II/lac/trilinos_parallel_block_vector.h>
+#include <deal.II/lac/trilinos_vector.h>
+
+#include <deal.II/numerics/vector_tools.h>
+
+
+DEAL_II_NAMESPACE_OPEN
+
+namespace VectorTools
+{
+  namespace internal
+  {
+    template <typename VectorType>
+    typename std::enable_if<dealii::is_serial_vector<VectorType>::value ==
+                            true>::type
+    subtract_mean_value(VectorType &v, const std::vector<bool> &p_select)
+    {
+      if (p_select.size() == 0)
+        {
+          // In case of an empty boolean mask operate on the whole vector:
+          v.add(-v.mean_value());
+        }
+      else
+        {
+          const unsigned int n = v.size();
+
+          Assert(p_select.size() == n,
+                 ExcDimensionMismatch(p_select.size(), n));
+
+          typename VectorType::value_type s       = 0.;
+          unsigned int                    counter = 0;
+          for (unsigned int i = 0; i < n; ++i)
+            if (p_select[i])
+              {
+                typename VectorType::value_type vi = v(i);
+                s += vi;
+                ++counter;
+              }
+          // Error out if we have not constrained anything. Note that in this
+          // case the vector v is always nonempty.
+          Assert(n == 0 || counter > 0,
+                 ComponentMask::ExcNoComponentSelected());
+
+          s /= counter;
+
+          for (unsigned int i = 0; i < n; ++i)
+            if (p_select[i])
+              v(i) -= s;
+        }
+    }
+
+
+
+    template <typename VectorType>
+    typename std::enable_if<dealii::is_serial_vector<VectorType>::value ==
+                            false>::type
+    subtract_mean_value(VectorType &v, const std::vector<bool> &p_select)
+    {
+      (void)p_select;
+      Assert(p_select.size() == 0, ExcNotImplemented());
+      // In case of an empty boolean mask operate on the whole vector:
+      v.add(-v.mean_value());
+    }
+  } // namespace internal
+
+
+  template <typename VectorType>
+  void
+  subtract_mean_value(VectorType &v, const std::vector<bool> &p_select)
+  {
+    internal::subtract_mean_value(v, p_select);
+  }
+
+  namespace internal
+  {
+    template <typename Number>
+    void
+    set_possibly_complex_number(const double r, const double, Number &n)
+    {
+      n = r;
+    }
+
+
+
+    template <typename Type>
+    void
+    set_possibly_complex_number(const double        r,
+                                const double        i,
+                                std::complex<Type> &n)
+    {
+      n = std::complex<Type>(r, i);
+    }
+  } // namespace internal
+
+  template <int dim, typename VectorType, int spacedim>
+  typename VectorType::value_type
+  compute_mean_value(const Mapping<dim, spacedim> &   mapping,
+                     const DoFHandler<dim, spacedim> &dof,
+                     const Quadrature<dim> &          quadrature,
+                     const VectorType &               v,
+                     const unsigned int               component)
+  {
+    using Number = typename VectorType::value_type;
+    Assert(v.size() == dof.n_dofs(),
+           ExcDimensionMismatch(v.size(), dof.n_dofs()));
+    AssertIndexRange(component, dof.get_fe(0).n_components());
+
+    FEValues<dim, spacedim> fe(mapping,
+                               dof.get_fe(),
+                               quadrature,
+                               UpdateFlags(update_JxW_values | update_values));
+
+    std::vector<Vector<Number>> values(
+      quadrature.size(), Vector<Number>(dof.get_fe(0).n_components()));
+
+    Number                                            mean = Number();
+    typename numbers::NumberTraits<Number>::real_type area = 0.;
+    // Compute mean value
+    for (const auto &cell : dof.active_cell_iterators())
+      if (cell->is_locally_owned())
+        {
+          fe.reinit(cell);
+          fe.get_function_values(v, values);
+          for (unsigned int k = 0; k < quadrature.size(); ++k)
+            {
+              mean += fe.JxW(k) * values[k](component);
+              area += fe.JxW(k);
+            }
+        }
+
+#ifdef DEAL_II_WITH_MPI
+    // if this was a distributed DoFHandler, we need to do the reduction
+    // over the entire domain
+    if (const parallel::TriangulationBase<dim, spacedim> *p_triangulation =
+          dynamic_cast<const parallel::TriangulationBase<dim, spacedim> *>(
+            &dof.get_triangulation()))
+      {
+        // The type used to store the elements of the global vector may be a
+        // real or a complex number. Do the global reduction always with real
+        // and imaginary types so that we don't have to distinguish, and to
+        // this end just copy everything into a complex number and, later,
+        // back into the original data type.
+        std::complex<double> mean_double = mean;
+        double my_values[3] = {mean_double.real(), mean_double.imag(), area};
+        double global_values[3];
+
+        const int ierr = MPI_Allreduce(my_values,
+                                       global_values,
+                                       3,
+                                       MPI_DOUBLE,
+                                       MPI_SUM,
+                                       p_triangulation->get_communicator());
+        AssertThrowMPI(ierr);
+
+        internal::set_possibly_complex_number(global_values[0],
+                                              global_values[1],
+                                              mean);
+        area = global_values[2];
+      }
+#endif
+
+    return (mean / area);
+  }
+
+
+  template <int dim, typename VectorType, int spacedim>
+  typename VectorType::value_type
+  compute_mean_value(const DoFHandler<dim, spacedim> &dof,
+                     const Quadrature<dim> &          quadrature,
+                     const VectorType &               v,
+                     const unsigned int               component)
+  {
+    return compute_mean_value(
+      StaticMappingQ1<dim, spacedim>::mapping, dof, quadrature, v, component);
+  }
+} // namespace VectorTools
+
+DEAL_II_NAMESPACE_CLOSE
+
+#endif // dealii_vector_tools_mean_value_templates_h
diff --git a/include/deal.II/numerics/vector_tools_point_gradient.templates.h b/include/deal.II/numerics/vector_tools_point_gradient.templates.h
new file mode 100644 (file)
index 0000000..3499088
--- /dev/null
@@ -0,0 +1,232 @@
+// ---------------------------------------------------------------------
+//
+// Copyright (C) 1998 - 2015 by the deal.II authors
+//
+// This file is part of the deal.II library.
+//
+// The deal.II library is free software; you can use it, redistribute
+// it, and/or modify it under the terms of the GNU Lesser General
+// Public License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+// The full text of the license can be found in the file LICENSE.md at
+// the top level directory of deal.II.
+//
+// ---------------------------------------------------------------------
+
+#ifndef dealii_vector_tools_point_gradient_templates_h
+#define dealii_vector_tools_point_gradient_templates_h
+
+
+#include <deal.II/fe/fe_values.h>
+
+#include <deal.II/grid/grid_tools.h>
+
+#include <deal.II/hp/fe_values.h>
+
+#include <deal.II/lac/block_vector.h>
+#include <deal.II/lac/la_parallel_block_vector.h>
+#include <deal.II/lac/la_parallel_vector.h>
+#include <deal.II/lac/la_vector.h>
+#include <deal.II/lac/petsc_block_vector.h>
+#include <deal.II/lac/petsc_vector.h>
+#include <deal.II/lac/trilinos_parallel_block_vector.h>
+#include <deal.II/lac/trilinos_vector.h>
+
+#include <deal.II/numerics/vector_tools.h>
+
+DEAL_II_NAMESPACE_OPEN
+
+namespace VectorTools
+{
+  template <int dim, typename VectorType, int spacedim>
+  void
+  point_gradient(
+    const DoFHandler<dim, spacedim> &dof,
+    const VectorType &               fe_function,
+    const Point<spacedim> &          point,
+    std::vector<Tensor<1, spacedim, typename VectorType::value_type>>
+      &gradients)
+  {
+    point_gradient(StaticMappingQ1<dim, spacedim>::mapping,
+                   dof,
+                   fe_function,
+                   point,
+                   gradients);
+  }
+
+
+  template <int dim, typename VectorType, int spacedim>
+  void
+  point_gradient(
+    const hp::DoFHandler<dim, spacedim> &dof,
+    const VectorType &                   fe_function,
+    const Point<spacedim> &              point,
+    std::vector<Tensor<1, spacedim, typename VectorType::value_type>>
+      &gradients)
+  {
+    point_gradient(hp::StaticMappingQ1<dim, spacedim>::mapping_collection,
+                   dof,
+                   fe_function,
+                   point,
+                   gradients);
+  }
+
+
+  template <int dim, typename VectorType, int spacedim>
+  Tensor<1, spacedim, typename VectorType::value_type>
+  point_gradient(const DoFHandler<dim, spacedim> &dof,
+                 const VectorType &               fe_function,
+                 const Point<spacedim> &          point)
+  {
+    return point_gradient(StaticMappingQ1<dim, spacedim>::mapping,
+                          dof,
+                          fe_function,
+                          point);
+  }
+
+
+  template <int dim, typename VectorType, int spacedim>
+  Tensor<1, spacedim, typename VectorType::value_type>
+  point_gradient(const hp::DoFHandler<dim, spacedim> &dof,
+                 const VectorType &                   fe_function,
+                 const Point<spacedim> &              point)
+  {
+    return point_gradient(
+      hp::StaticMappingQ1<dim, spacedim>::mapping_collection,
+      dof,
+      fe_function,
+      point);
+  }
+
+
+  template <int dim, typename VectorType, int spacedim>
+  void
+  point_gradient(
+    const Mapping<dim, spacedim> &   mapping,
+    const DoFHandler<dim, spacedim> &dof,
+    const VectorType &               fe_function,
+    const Point<spacedim> &          point,
+    std::vector<Tensor<1, spacedim, typename VectorType::value_type>> &gradient)
+  {
+    const FiniteElement<dim> &fe = dof.get_fe();
+
+    Assert(gradient.size() == fe.n_components(),
+           ExcDimensionMismatch(gradient.size(), fe.n_components()));
+
+    // first find the cell in which this point
+    // is, initialize a quadrature rule with
+    // it, and then a FEValues object
+    const std::pair<typename DoFHandler<dim, spacedim>::active_cell_iterator,
+                    Point<spacedim>>
+      cell_point =
+        GridTools::find_active_cell_around_point(mapping, dof, point);
+
+    AssertThrow(cell_point.first->is_locally_owned(),
+                ExcPointNotAvailableHere());
+    Assert(GeometryInfo<dim>::distance_to_unit_cell(cell_point.second) < 1e-10,
+           ExcInternalError());
+
+    const Quadrature<dim> quadrature(
+      GeometryInfo<dim>::project_to_unit_cell(cell_point.second));
+
+    FEValues<dim> fe_values(mapping, fe, quadrature, update_gradients);
+    fe_values.reinit(cell_point.first);
+
+    // then use this to get the gradients of
+    // the given fe_function at this point
+    using Number = typename VectorType::value_type;
+    std::vector<std::vector<Tensor<1, dim, Number>>> u_gradient(
+      1, std::vector<Tensor<1, dim, Number>>(fe.n_components()));
+    fe_values.get_function_gradients(fe_function, u_gradient);
+
+    gradient = u_gradient[0];
+  }
+
+
+  template <int dim, typename VectorType, int spacedim>
+  void
+  point_gradient(
+    const hp::MappingCollection<dim, spacedim> &mapping,
+    const hp::DoFHandler<dim, spacedim> &       dof,
+    const VectorType &                          fe_function,
+    const Point<spacedim> &                     point,
+    std::vector<Tensor<1, spacedim, typename VectorType::value_type>> &gradient)
+  {
+    using Number                              = typename VectorType::value_type;
+    const hp::FECollection<dim, spacedim> &fe = dof.get_fe_collection();
+
+    Assert(gradient.size() == fe.n_components(),
+           ExcDimensionMismatch(gradient.size(), fe.n_components()));
+
+    // first find the cell in which this point
+    // is, initialize a quadrature rule with
+    // it, and then a FEValues object
+    const std::pair<
+      typename hp::DoFHandler<dim, spacedim>::active_cell_iterator,
+      Point<spacedim>>
+      cell_point =
+        GridTools::find_active_cell_around_point(mapping, dof, point);
+
+    AssertThrow(cell_point.first->is_locally_owned(),
+                ExcPointNotAvailableHere());
+    Assert(GeometryInfo<dim>::distance_to_unit_cell(cell_point.second) < 1e-10,
+           ExcInternalError());
+
+    const Quadrature<dim> quadrature(
+      GeometryInfo<dim>::project_to_unit_cell(cell_point.second));
+    hp::FEValues<dim, spacedim> hp_fe_values(mapping,
+                                             fe,
+                                             hp::QCollection<dim>(quadrature),
+                                             update_gradients);
+    hp_fe_values.reinit(cell_point.first);
+    const FEValues<dim, spacedim> &fe_values =
+      hp_fe_values.get_present_fe_values();
+
+    std::vector<std::vector<Tensor<1, dim, Number>>> u_gradient(
+      1, std::vector<Tensor<1, dim, Number>>(fe.n_components()));
+    fe_values.get_function_gradients(fe_function, u_gradient);
+
+    gradient = u_gradient[0];
+  }
+
+
+  template <int dim, typename VectorType, int spacedim>
+  Tensor<1, spacedim, typename VectorType::value_type>
+  point_gradient(const Mapping<dim, spacedim> &   mapping,
+                 const DoFHandler<dim, spacedim> &dof,
+                 const VectorType &               fe_function,
+                 const Point<spacedim> &          point)
+  {
+    Assert(dof.get_fe(0).n_components() == 1,
+           ExcMessage(
+             "Finite element is not scalar as is necessary for this function"));
+
+    std::vector<Tensor<1, dim, typename VectorType::value_type>> gradient(1);
+    point_gradient(mapping, dof, fe_function, point, gradient);
+
+    return gradient[0];
+  }
+
+
+
+  template <int dim, typename VectorType, int spacedim>
+  Tensor<1, spacedim, typename VectorType::value_type>
+  point_gradient(const hp::MappingCollection<dim, spacedim> &mapping,
+                 const hp::DoFHandler<dim, spacedim> &       dof,
+                 const VectorType &                          fe_function,
+                 const Point<spacedim> &                     point)
+  {
+    Assert(dof.get_fe(0).n_components() == 1,
+           ExcMessage(
+             "Finite element is not scalar as is necessary for this function"));
+
+    std::vector<Tensor<1, dim, typename VectorType::value_type>> gradient(1);
+    point_gradient(mapping, dof, fe_function, point, gradient);
+
+    return gradient[0];
+  }
+} // namespace VectorTools
+
+DEAL_II_NAMESPACE_CLOSE
+
+#endif // dealii_vector_tools_point_gradient_templates_h
diff --git a/include/deal.II/numerics/vector_tools_point_value.templates.h b/include/deal.II/numerics/vector_tools_point_value.templates.h
new file mode 100644 (file)
index 0000000..fc0b223
--- /dev/null
@@ -0,0 +1,510 @@
+// ---------------------------------------------------------------------
+//
+// Copyright (C) 1998 - 2014 by the deal.II authors
+//
+// This file is part of the deal.II library.
+//
+// The deal.II library is free software; you can use it, redistribute
+// it, and/or modify it under the terms of the GNU Lesser General
+// Public License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+// The full text of the license can be found in the file LICENSE.md at
+// the top level directory of deal.II.
+//
+// ---------------------------------------------------------------------
+
+#ifndef dealii_vector_tools_point_value_templates_h
+#define dealii_vector_tools_point_value_templates_h
+
+
+#include <deal.II/fe/fe_values.h>
+
+#include <deal.II/grid/grid_tools.h>
+
+#include <deal.II/hp/fe_values.h>
+
+#include <deal.II/lac/block_vector.h>
+#include <deal.II/lac/la_parallel_block_vector.h>
+#include <deal.II/lac/la_parallel_vector.h>
+#include <deal.II/lac/la_vector.h>
+#include <deal.II/lac/petsc_block_vector.h>
+#include <deal.II/lac/petsc_vector.h>
+#include <deal.II/lac/trilinos_parallel_block_vector.h>
+#include <deal.II/lac/trilinos_vector.h>
+
+#include <deal.II/numerics/vector_tools.h>
+
+
+DEAL_II_NAMESPACE_OPEN
+
+namespace VectorTools
+{
+  template <int dim, typename VectorType, int spacedim>
+  void
+  point_value(const DoFHandler<dim, spacedim> &        dof,
+              const VectorType &                       fe_function,
+              const Point<spacedim> &                  point,
+              Vector<typename VectorType::value_type> &value)
+  {
+    point_value(
+      StaticMappingQ1<dim, spacedim>::mapping, dof, fe_function, point, value);
+  }
+
+
+  template <int dim, typename VectorType, int spacedim>
+  void
+  point_value(const hp::DoFHandler<dim, spacedim> &    dof,
+              const VectorType &                       fe_function,
+              const Point<spacedim> &                  point,
+              Vector<typename VectorType::value_type> &value)
+  {
+    point_value(hp::StaticMappingQ1<dim, spacedim>::mapping_collection,
+                dof,
+                fe_function,
+                point,
+                value);
+  }
+
+
+  template <int dim, typename VectorType, int spacedim>
+  typename VectorType::value_type
+  point_value(const DoFHandler<dim, spacedim> &dof,
+              const VectorType &               fe_function,
+              const Point<spacedim> &          point)
+  {
+    return point_value(StaticMappingQ1<dim, spacedim>::mapping,
+                       dof,
+                       fe_function,
+                       point);
+  }
+
+
+  template <int dim, typename VectorType, int spacedim>
+  typename VectorType::value_type
+  point_value(const hp::DoFHandler<dim, spacedim> &dof,
+              const VectorType &                   fe_function,
+              const Point<spacedim> &              point)
+  {
+    return point_value(hp::StaticMappingQ1<dim, spacedim>::mapping_collection,
+                       dof,
+                       fe_function,
+                       point);
+  }
+
+
+  template <int dim, typename VectorType, int spacedim>
+  void
+  point_value(const Mapping<dim, spacedim> &           mapping,
+              const DoFHandler<dim, spacedim> &        dof,
+              const VectorType &                       fe_function,
+              const Point<spacedim> &                  point,
+              Vector<typename VectorType::value_type> &value)
+  {
+    using Number                 = typename VectorType::value_type;
+    const FiniteElement<dim> &fe = dof.get_fe();
+
+    Assert(value.size() == fe.n_components(),
+           ExcDimensionMismatch(value.size(), fe.n_components()));
+
+    // first find the cell in which this point
+    // is, initialize a quadrature rule with
+    // it, and then a FEValues object
+    const std::pair<typename DoFHandler<dim, spacedim>::active_cell_iterator,
+                    Point<spacedim>>
+      cell_point =
+        GridTools::find_active_cell_around_point(mapping, dof, point);
+
+    AssertThrow(cell_point.first->is_locally_owned(),
+                ExcPointNotAvailableHere());
+    Assert(GeometryInfo<dim>::distance_to_unit_cell(cell_point.second) < 1e-10,
+           ExcInternalError());
+
+    const Quadrature<dim> quadrature(
+      GeometryInfo<dim>::project_to_unit_cell(cell_point.second));
+
+    FEValues<dim> fe_values(mapping, fe, quadrature, update_values);
+    fe_values.reinit(cell_point.first);
+
+    // then use this to get at the values of
+    // the given fe_function at this point
+    std::vector<Vector<Number>> u_value(1, Vector<Number>(fe.n_components()));
+    fe_values.get_function_values(fe_function, u_value);
+
+    value = u_value[0];
+  }
+
+
+  template <int dim, typename VectorType, int spacedim>
+  void
+  point_value(const hp::MappingCollection<dim, spacedim> &mapping,
+              const hp::DoFHandler<dim, spacedim> &       dof,
+              const VectorType &                          fe_function,
+              const Point<spacedim> &                     point,
+              Vector<typename VectorType::value_type> &   value)
+  {
+    using Number                              = typename VectorType::value_type;
+    const hp::FECollection<dim, spacedim> &fe = dof.get_fe_collection();
+
+    Assert(value.size() == fe.n_components(),
+           ExcDimensionMismatch(value.size(), fe.n_components()));
+
+    // first find the cell in which this point
+    // is, initialize a quadrature rule with
+    // it, and then a FEValues object
+    const std::pair<
+      typename hp::DoFHandler<dim, spacedim>::active_cell_iterator,
+      Point<spacedim>>
+      cell_point =
+        GridTools::find_active_cell_around_point(mapping, dof, point);
+
+    AssertThrow(cell_point.first->is_locally_owned(),
+                ExcPointNotAvailableHere());
+    Assert(GeometryInfo<dim>::distance_to_unit_cell(cell_point.second) < 1e-10,
+           ExcInternalError());
+
+    const Quadrature<dim> quadrature(
+      GeometryInfo<dim>::project_to_unit_cell(cell_point.second));
+    hp::FEValues<dim, spacedim> hp_fe_values(mapping,
+                                             fe,
+                                             hp::QCollection<dim>(quadrature),
+                                             update_values);
+    hp_fe_values.reinit(cell_point.first);
+    const FEValues<dim, spacedim> &fe_values =
+      hp_fe_values.get_present_fe_values();
+
+    // then use this to get at the values of
+    // the given fe_function at this point
+    std::vector<Vector<Number>> u_value(1, Vector<Number>(fe.n_components()));
+    fe_values.get_function_values(fe_function, u_value);
+
+    value = u_value[0];
+  }
+
+
+  template <int dim, typename VectorType, int spacedim>
+  typename VectorType::value_type
+  point_value(const Mapping<dim, spacedim> &   mapping,
+              const DoFHandler<dim, spacedim> &dof,
+              const VectorType &               fe_function,
+              const Point<spacedim> &          point)
+  {
+    Assert(dof.get_fe(0).n_components() == 1,
+           ExcMessage(
+             "Finite element is not scalar as is necessary for this function"));
+
+    Vector<typename VectorType::value_type> value(1);
+    point_value(mapping, dof, fe_function, point, value);
+
+    return value(0);
+  }
+
+
+  template <int dim, typename VectorType, int spacedim>
+  typename VectorType::value_type
+  point_value(const hp::MappingCollection<dim, spacedim> &mapping,
+              const hp::DoFHandler<dim, spacedim> &       dof,
+              const VectorType &                          fe_function,
+              const Point<spacedim> &                     point)
+  {
+    Assert(dof.get_fe(0).n_components() == 1,
+           ExcMessage(
+             "Finite element is not scalar as is necessary for this function"));
+
+    Vector<typename VectorType::value_type> value(1);
+    point_value(mapping, dof, fe_function, point, value);
+
+    return value(0);
+  }
+
+  template <int dim, typename VectorType, int spacedim>
+  void
+  point_difference(
+    const DoFHandler<dim, spacedim> &                          dof,
+    const VectorType &                                         fe_function,
+    const Function<spacedim, typename VectorType::value_type> &exact_function,
+    Vector<typename VectorType::value_type> &                  difference,
+    const Point<spacedim> &                                    point)
+  {
+    point_difference(StaticMappingQ1<dim>::mapping,
+                     dof,
+                     fe_function,
+                     exact_function,
+                     difference,
+                     point);
+  }
+
+
+  template <int dim, typename VectorType, int spacedim>
+  void
+  point_difference(
+    const Mapping<dim, spacedim> &                             mapping,
+    const DoFHandler<dim, spacedim> &                          dof,
+    const VectorType &                                         fe_function,
+    const Function<spacedim, typename VectorType::value_type> &exact_function,
+    Vector<typename VectorType::value_type> &                  difference,
+    const Point<spacedim> &                                    point)
+  {
+    using Number                 = typename VectorType::value_type;
+    const FiniteElement<dim> &fe = dof.get_fe();
+
+    Assert(difference.size() == fe.n_components(),
+           ExcDimensionMismatch(difference.size(), fe.n_components()));
+
+    // first find the cell in which this point
+    // is, initialize a quadrature rule with
+    // it, and then a FEValues object
+    const std::pair<typename DoFHandler<dim, spacedim>::active_cell_iterator,
+                    Point<spacedim>>
+      cell_point =
+        GridTools::find_active_cell_around_point(mapping, dof, point);
+
+    AssertThrow(cell_point.first->is_locally_owned(),
+                ExcPointNotAvailableHere());
+    Assert(GeometryInfo<dim>::distance_to_unit_cell(cell_point.second) < 1e-10,
+           ExcInternalError());
+
+    const Quadrature<dim> quadrature(
+      GeometryInfo<dim>::project_to_unit_cell(cell_point.second));
+    FEValues<dim> fe_values(mapping, fe, quadrature, update_values);
+    fe_values.reinit(cell_point.first);
+
+    // then use this to get at the values of
+    // the given fe_function at this point
+    std::vector<Vector<Number>> u_value(1, Vector<Number>(fe.n_components()));
+    fe_values.get_function_values(fe_function, u_value);
+
+    if (fe.n_components() == 1)
+      difference(0) = exact_function.value(point);
+    else
+      exact_function.vector_value(point, difference);
+
+    for (unsigned int i = 0; i < difference.size(); ++i)
+      difference(i) -= u_value[0](i);
+  }
+
+  template <int dim, int spacedim>
+  void
+  create_point_source_vector(const Mapping<dim, spacedim> &   mapping,
+                             const DoFHandler<dim, spacedim> &dof_handler,
+                             const Point<spacedim> &          p,
+                             Vector<double> &                 rhs_vector)
+  {
+    Assert(rhs_vector.size() == dof_handler.n_dofs(),
+           ExcDimensionMismatch(rhs_vector.size(), dof_handler.n_dofs()));
+    Assert(dof_handler.get_fe(0).n_components() == 1,
+           ExcMessage("This function only works for scalar finite elements"));
+
+    rhs_vector = 0;
+
+    std::pair<typename DoFHandler<dim, spacedim>::active_cell_iterator,
+              Point<spacedim>>
+      cell_point =
+        GridTools::find_active_cell_around_point(mapping, dof_handler, p);
+
+    Quadrature<dim> q(
+      GeometryInfo<dim>::project_to_unit_cell(cell_point.second));
+
+    FEValues<dim, spacedim> fe_values(mapping,
+                                      dof_handler.get_fe(),
+                                      q,
+                                      UpdateFlags(update_values));
+    fe_values.reinit(cell_point.first);
+
+    const unsigned int dofs_per_cell = dof_handler.get_fe().dofs_per_cell;
+
+    std::vector<types::global_dof_index> local_dof_indices(dofs_per_cell);
+    cell_point.first->get_dof_indices(local_dof_indices);
+
+    for (unsigned int i = 0; i < dofs_per_cell; i++)
+      rhs_vector(local_dof_indices[i]) = fe_values.shape_value(i, 0);
+  }
+
+
+
+  template <int dim, int spacedim>
+  void
+  create_point_source_vector(const DoFHandler<dim, spacedim> &dof_handler,
+                             const Point<spacedim> &          p,
+                             Vector<double> &                 rhs_vector)
+  {
+    create_point_source_vector(StaticMappingQ1<dim, spacedim>::mapping,
+                               dof_handler,
+                               p,
+                               rhs_vector);
+  }
+
+
+  template <int dim, int spacedim>
+  void
+  create_point_source_vector(
+    const hp::MappingCollection<dim, spacedim> &mapping,
+    const hp::DoFHandler<dim, spacedim> &       dof_handler,
+    const Point<spacedim> &                     p,
+    Vector<double> &                            rhs_vector)
+  {
+    Assert(rhs_vector.size() == dof_handler.n_dofs(),
+           ExcDimensionMismatch(rhs_vector.size(), dof_handler.n_dofs()));
+    Assert(dof_handler.get_fe(0).n_components() == 1,
+           ExcMessage("This function only works for scalar finite elements"));
+
+    rhs_vector = 0;
+
+    std::pair<typename hp::DoFHandler<dim, spacedim>::active_cell_iterator,
+              Point<spacedim>>
+      cell_point =
+        GridTools::find_active_cell_around_point(mapping, dof_handler, p);
+
+    Quadrature<dim> q(
+      GeometryInfo<dim>::project_to_unit_cell(cell_point.second));
+
+    FEValues<dim> fe_values(mapping[cell_point.first->active_fe_index()],
+                            cell_point.first->get_fe(),
+                            q,
+                            UpdateFlags(update_values));
+    fe_values.reinit(cell_point.first);
+
+    const unsigned int dofs_per_cell = cell_point.first->get_fe().dofs_per_cell;
+
+    std::vector<types::global_dof_index> local_dof_indices(dofs_per_cell);
+    cell_point.first->get_dof_indices(local_dof_indices);
+
+    for (unsigned int i = 0; i < dofs_per_cell; i++)
+      rhs_vector(local_dof_indices[i]) = fe_values.shape_value(i, 0);
+  }
+
+
+
+  template <int dim, int spacedim>
+  void
+  create_point_source_vector(const hp::DoFHandler<dim, spacedim> &dof_handler,
+                             const Point<spacedim> &              p,
+                             Vector<double> &                     rhs_vector)
+  {
+    create_point_source_vector(hp::StaticMappingQ1<dim>::mapping_collection,
+                               dof_handler,
+                               p,
+                               rhs_vector);
+  }
+
+
+
+  template <int dim, int spacedim>
+  void
+  create_point_source_vector(const Mapping<dim, spacedim> &   mapping,
+                             const DoFHandler<dim, spacedim> &dof_handler,
+                             const Point<spacedim> &          p,
+                             const Point<dim> &               orientation,
+                             Vector<double> &                 rhs_vector)
+  {
+    Assert(rhs_vector.size() == dof_handler.n_dofs(),
+           ExcDimensionMismatch(rhs_vector.size(), dof_handler.n_dofs()));
+    Assert(dof_handler.get_fe(0).n_components() == dim,
+           ExcMessage(
+             "This function only works for vector-valued finite elements."));
+
+    rhs_vector = 0;
+
+    const std::pair<typename DoFHandler<dim, spacedim>::active_cell_iterator,
+                    Point<spacedim>>
+      cell_point =
+        GridTools::find_active_cell_around_point(mapping, dof_handler, p);
+
+    const Quadrature<dim> q(
+      GeometryInfo<dim>::project_to_unit_cell(cell_point.second));
+
+    const FEValuesExtractors::Vector vec(0);
+    FEValues<dim, spacedim>          fe_values(mapping,
+                                      dof_handler.get_fe(),
+                                      q,
+                                      UpdateFlags(update_values));
+    fe_values.reinit(cell_point.first);
+
+    const unsigned int dofs_per_cell = dof_handler.get_fe().dofs_per_cell;
+
+    std::vector<types::global_dof_index> local_dof_indices(dofs_per_cell);
+    cell_point.first->get_dof_indices(local_dof_indices);
+
+    for (unsigned int i = 0; i < dofs_per_cell; i++)
+      rhs_vector(local_dof_indices[i]) =
+        orientation * fe_values[vec].value(i, 0);
+  }
+
+
+
+  template <int dim, int spacedim>
+  void
+  create_point_source_vector(const DoFHandler<dim, spacedim> &dof_handler,
+                             const Point<spacedim> &          p,
+                             const Point<dim> &               orientation,
+                             Vector<double> &                 rhs_vector)
+  {
+    create_point_source_vector(StaticMappingQ1<dim, spacedim>::mapping,
+                               dof_handler,
+                               p,
+                               orientation,
+                               rhs_vector);
+  }
+
+
+  template <int dim, int spacedim>
+  void
+  create_point_source_vector(
+    const hp::MappingCollection<dim, spacedim> &mapping,
+    const hp::DoFHandler<dim, spacedim> &       dof_handler,
+    const Point<spacedim> &                     p,
+    const Point<dim> &                          orientation,
+    Vector<double> &                            rhs_vector)
+  {
+    Assert(rhs_vector.size() == dof_handler.n_dofs(),
+           ExcDimensionMismatch(rhs_vector.size(), dof_handler.n_dofs()));
+    Assert(dof_handler.get_fe(0).n_components() == dim,
+           ExcMessage(
+             "This function only works for vector-valued finite elements."));
+
+    rhs_vector = 0;
+
+    std::pair<typename hp::DoFHandler<dim, spacedim>::active_cell_iterator,
+              Point<spacedim>>
+      cell_point =
+        GridTools::find_active_cell_around_point(mapping, dof_handler, p);
+
+    Quadrature<dim> q(
+      GeometryInfo<dim>::project_to_unit_cell(cell_point.second));
+
+    const FEValuesExtractors::Vector vec(0);
+    FEValues<dim> fe_values(mapping[cell_point.first->active_fe_index()],
+                            cell_point.first->get_fe(),
+                            q,
+                            UpdateFlags(update_values));
+    fe_values.reinit(cell_point.first);
+
+    const unsigned int dofs_per_cell = cell_point.first->get_fe().dofs_per_cell;
+
+    std::vector<types::global_dof_index> local_dof_indices(dofs_per_cell);
+    cell_point.first->get_dof_indices(local_dof_indices);
+
+    for (unsigned int i = 0; i < dofs_per_cell; i++)
+      rhs_vector(local_dof_indices[i]) =
+        orientation * fe_values[vec].value(i, 0);
+  }
+
+
+
+  template <int dim, int spacedim>
+  void
+  create_point_source_vector(const hp::DoFHandler<dim, spacedim> &dof_handler,
+                             const Point<spacedim> &              p,
+                             const Point<dim> &                   orientation,
+                             Vector<double> &                     rhs_vector)
+  {
+    create_point_source_vector(hp::StaticMappingQ1<dim>::mapping_collection,
+                               dof_handler,
+                               p,
+                               orientation,
+                               rhs_vector);
+  }
+} // namespace VectorTools
+
+DEAL_II_NAMESPACE_CLOSE
+
+#endif // dealii_vector_tools_point_value_templates_h
diff --git a/include/deal.II/numerics/vector_tools_project.templates.h b/include/deal.II/numerics/vector_tools_project.templates.h
new file mode 100644 (file)
index 0000000..b401e80
--- /dev/null
@@ -0,0 +1,1086 @@
+// ---------------------------------------------------------------------
+//
+// Copyright (C) 1998 - 2018 by the deal.II authors
+//
+// This file is part of the deal.II library.
+//
+// The deal.II library is free software; you can use it, redistribute
+// it, and/or modify it under the terms of the GNU Lesser General
+// Public License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+// The full text of the license can be found in the file LICENSE.md at
+// the top level directory of deal.II.
+//
+// ---------------------------------------------------------------------
+
+#ifndef dealii_vector_tools_project_templates_h
+#define dealii_vector_tools_project_templates_h
+
+
+#include <deal.II/lac/block_vector.h>
+#include <deal.II/lac/la_parallel_block_vector.h>
+#include <deal.II/lac/la_parallel_vector.h>
+#include <deal.II/lac/la_vector.h>
+#include <deal.II/lac/petsc_block_vector.h>
+#include <deal.II/lac/petsc_vector.h>
+#include <deal.II/lac/precondition.h>
+#include <deal.II/lac/solver_cg.h>
+#include <deal.II/lac/solver_control.h>
+#include <deal.II/lac/sparse_matrix.h>
+#include <deal.II/lac/trilinos_parallel_block_vector.h>
+#include <deal.II/lac/trilinos_vector.h>
+
+#include <deal.II/matrix_free/operators.h>
+
+#include <deal.II/numerics/matrix_tools.h>
+#include <deal.II/numerics/vector_tools.h>
+
+DEAL_II_NAMESPACE_OPEN
+
+namespace VectorTools
+{
+  namespace internal
+  {
+    /**
+     * Interpolate zero boundary values. We don't need to worry about a
+     * mapping here because the function we evaluate for the DoFs is zero in
+     * the mapped locations as well as in the original, unmapped locations
+     */
+    template <int dim,
+              int spacedim,
+              template <int, int> class DoFHandlerType,
+              typename number>
+    void
+    interpolate_zero_boundary_values(
+      const DoFHandlerType<dim, spacedim> &      dof_handler,
+      std::map<types::global_dof_index, number> &boundary_values)
+    {
+      // loop over all boundary faces
+      // to get all dof indices of
+      // dofs on the boundary. note
+      // that in 3d there are cases
+      // where a face is not at the
+      // boundary, yet one of its
+      // lines is, and we should
+      // consider the degrees of
+      // freedom on it as boundary
+      // nodes. likewise, in 2d and
+      // 3d there are cases where a
+      // cell is only at the boundary
+      // by one vertex. nevertheless,
+      // since we do not support
+      // boundaries with dimension
+      // less or equal to dim-2, each
+      // such boundary dof is also
+      // found from some other face
+      // that is actually wholly on
+      // the boundary, not only by
+      // one line or one vertex
+      typename DoFHandlerType<dim, spacedim>::active_cell_iterator
+        cell = dof_handler.begin_active(),
+        endc = dof_handler.end();
+      std::vector<types::global_dof_index> face_dof_indices;
+      for (; cell != endc; ++cell)
+        for (auto f : GeometryInfo<dim>::face_indices())
+          if (cell->at_boundary(f))
+            {
+              face_dof_indices.resize(cell->get_fe().dofs_per_face);
+              cell->face(f)->get_dof_indices(face_dof_indices,
+                                             cell->active_fe_index());
+              for (unsigned int i = 0; i < cell->get_fe().dofs_per_face; ++i)
+                // enter zero boundary values
+                // for all boundary nodes
+                //
+                // we need not care about
+                // vector valued elements here,
+                // since we set all components
+                boundary_values[face_dof_indices[i]] = 0.;
+            }
+    }
+
+    /**
+     * Compute the boundary values to be used in the project() functions.
+     */
+    template <int dim,
+              int spacedim,
+              template <int, int> class DoFHandlerType,
+              template <int, int> class M_or_MC,
+              template <int> class Q_or_QC,
+              typename number>
+    void
+    project_compute_b_v(
+      const M_or_MC<dim, spacedim> &             mapping,
+      const DoFHandlerType<dim, spacedim> &      dof,
+      const Function<spacedim, number> &         function,
+      const bool                                 enforce_zero_boundary,
+      const Q_or_QC<dim - 1> &                   q_boundary,
+      const bool                                 project_to_boundary_first,
+      std::map<types::global_dof_index, number> &boundary_values)
+    {
+      if (enforce_zero_boundary == true)
+        // no need to project boundary
+        // values, but enforce
+        // homogeneous boundary values
+        // anyway
+        interpolate_zero_boundary_values(dof, boundary_values);
+
+      else
+        // no homogeneous boundary values
+        if (project_to_boundary_first == true)
+        // boundary projection required
+        {
+          // set up a list of boundary
+          // functions for the
+          // different boundary
+          // parts. We want the
+          // function to hold on
+          // all parts of the boundary
+          const std::vector<types::boundary_id> used_boundary_ids =
+            dof.get_triangulation().get_boundary_ids();
+
+          std::map<types::boundary_id, const Function<spacedim, number> *>
+            boundary_functions;
+          for (const auto used_boundary_id : used_boundary_ids)
+            boundary_functions[used_boundary_id] = &function;
+          project_boundary_values(
+            mapping, dof, boundary_functions, q_boundary, boundary_values);
+        }
+    }
+
+    /*
+     * MatrixFree implementation of project() for an arbitrary number of
+     * components and arbitrary degree of the FiniteElement.
+     */
+    template <int components,
+              int fe_degree,
+              int dim,
+              typename Number,
+              int spacedim>
+    void
+    project_matrix_free(
+      const Mapping<dim, spacedim> &   mapping,
+      const DoFHandler<dim, spacedim> &dof,
+      const AffineConstraints<Number> &constraints,
+      const Quadrature<dim> &          quadrature,
+      const Function<
+        spacedim,
+        typename LinearAlgebra::distributed::Vector<Number>::value_type>
+        &                                         function,
+      LinearAlgebra::distributed::Vector<Number> &work_result,
+      const bool                                  enforce_zero_boundary,
+      const Quadrature<dim - 1> &                 q_boundary,
+      const bool                                  project_to_boundary_first)
+    {
+      Assert(project_to_boundary_first == false, ExcNotImplemented());
+      Assert(enforce_zero_boundary == false, ExcNotImplemented());
+      (void)enforce_zero_boundary;
+      (void)project_to_boundary_first;
+      (void)q_boundary;
+
+      Assert(dof.get_fe(0).n_components() == function.n_components,
+             ExcDimensionMismatch(dof.get_fe(0).n_components(),
+                                  function.n_components));
+      Assert(fe_degree == -1 ||
+               dof.get_fe().degree == static_cast<unsigned int>(fe_degree),
+             ExcDimensionMismatch(fe_degree, dof.get_fe().degree));
+      Assert(dof.get_fe(0).n_components() == components,
+             ExcDimensionMismatch(components, dof.get_fe(0).n_components()));
+
+      // set up mass matrix and right hand side
+      typename MatrixFree<dim, Number>::AdditionalData additional_data;
+      additional_data.tasks_parallel_scheme =
+        MatrixFree<dim, Number>::AdditionalData::partition_color;
+      additional_data.mapping_update_flags =
+        (update_values | update_JxW_values);
+      std::shared_ptr<MatrixFree<dim, Number>> matrix_free(
+        new MatrixFree<dim, Number>());
+      matrix_free->reinit(mapping,
+                          dof,
+                          constraints,
+                          QGauss<1>(dof.get_fe().degree + 2),
+                          additional_data);
+      using MatrixType = MatrixFreeOperators::MassOperator<
+        dim,
+        fe_degree,
+        fe_degree + 2,
+        components,
+        LinearAlgebra::distributed::Vector<Number>>;
+      MatrixType mass_matrix;
+      mass_matrix.initialize(matrix_free);
+      mass_matrix.compute_diagonal();
+
+      LinearAlgebra::distributed::Vector<Number> rhs, inhomogeneities;
+      matrix_free->initialize_dof_vector(work_result);
+      matrix_free->initialize_dof_vector(rhs);
+      matrix_free->initialize_dof_vector(inhomogeneities);
+      constraints.distribute(inhomogeneities);
+      inhomogeneities *= -1.;
+
+      {
+        create_right_hand_side(
+          mapping, dof, quadrature, function, rhs, constraints);
+
+        // account for inhomogeneous constraints
+        inhomogeneities.update_ghost_values();
+        FEEvaluation<dim, fe_degree, fe_degree + 2, components, Number> phi(
+          *matrix_free);
+        for (unsigned int cell = 0; cell < matrix_free->n_macro_cells(); ++cell)
+          {
+            phi.reinit(cell);
+            phi.read_dof_values_plain(inhomogeneities);
+            phi.evaluate(true, false);
+            for (unsigned int q = 0; q < phi.n_q_points; ++q)
+              phi.submit_value(phi.get_value(q), q);
+
+            phi.integrate(true, false);
+            phi.distribute_local_to_global(rhs);
+          }
+        rhs.compress(VectorOperation::add);
+      }
+
+      // now invert the matrix
+      // Allow for a maximum of 6*n steps to reduce the residual by 10^-12. n
+      // steps may not be sufficient, since roundoff errors may accumulate for
+      // badly conditioned matrices. This behavior can be observed, e.g. for
+      // FE_Q_Hierarchical for degree higher than three.
+      ReductionControl control(6 * rhs.size(), 0., 1e-12, false, false);
+      SolverCG<LinearAlgebra::distributed::Vector<Number>> cg(control);
+      PreconditionJacobi<MatrixType>                       preconditioner;
+      preconditioner.initialize(mass_matrix, 1.);
+      cg.solve(mass_matrix, work_result, rhs, preconditioner);
+      work_result += inhomogeneities;
+
+      constraints.distribute(work_result);
+    }
+
+
+
+    /**
+     * Helper interface. After figuring out the number of components in
+     * project_matrix_free_component, we determine the degree of the
+     * FiniteElement and call project_matrix_free with the appropriate
+     * template arguments.
+     */
+    template <int components, int dim, typename Number, int spacedim>
+    void
+    project_matrix_free_degree(
+      const Mapping<dim, spacedim> &   mapping,
+      const DoFHandler<dim, spacedim> &dof,
+      const AffineConstraints<Number> &constraints,
+      const Quadrature<dim> &          quadrature,
+      const Function<
+        spacedim,
+        typename LinearAlgebra::distributed::Vector<Number>::value_type>
+        &                                         function,
+      LinearAlgebra::distributed::Vector<Number> &work_result,
+      const bool                                  enforce_zero_boundary,
+      const Quadrature<dim - 1> &                 q_boundary,
+      const bool                                  project_to_boundary_first)
+    {
+      switch (dof.get_fe().degree)
+        {
+          case 1:
+            project_matrix_free<components, 1>(mapping,
+                                               dof,
+                                               constraints,
+                                               quadrature,
+                                               function,
+                                               work_result,
+                                               enforce_zero_boundary,
+                                               q_boundary,
+                                               project_to_boundary_first);
+            break;
+
+          case 2:
+            project_matrix_free<components, 2>(mapping,
+                                               dof,
+                                               constraints,
+                                               quadrature,
+                                               function,
+                                               work_result,
+                                               enforce_zero_boundary,
+                                               q_boundary,
+                                               project_to_boundary_first);
+            break;
+
+          case 3:
+            project_matrix_free<components, 3>(mapping,
+                                               dof,
+                                               constraints,
+                                               quadrature,
+                                               function,
+                                               work_result,
+                                               enforce_zero_boundary,
+                                               q_boundary,
+                                               project_to_boundary_first);
+            break;
+
+          default:
+            project_matrix_free<components, -1>(mapping,
+                                                dof,
+                                                constraints,
+                                                quadrature,
+                                                function,
+                                                work_result,
+                                                enforce_zero_boundary,
+                                                q_boundary,
+                                                project_to_boundary_first);
+        }
+    }
+
+
+
+    // Helper interface for the matrix-free implementation of project().
+    // Used to determine the number of components.
+    template <int dim, typename Number, int spacedim>
+    void
+    project_matrix_free_component(
+      const Mapping<dim, spacedim> &   mapping,
+      const DoFHandler<dim, spacedim> &dof,
+      const AffineConstraints<Number> &constraints,
+      const Quadrature<dim> &          quadrature,
+      const Function<
+        spacedim,
+        typename LinearAlgebra::distributed::Vector<Number>::value_type>
+        &                                         function,
+      LinearAlgebra::distributed::Vector<Number> &work_result,
+      const bool                                  enforce_zero_boundary,
+      const Quadrature<dim - 1> &                 q_boundary,
+      const bool                                  project_to_boundary_first)
+    {
+      switch (dof.get_fe(0).n_components())
+        {
+          case 1:
+            project_matrix_free_degree<1>(mapping,
+                                          dof,
+                                          constraints,
+                                          quadrature,
+                                          function,
+                                          work_result,
+                                          enforce_zero_boundary,
+                                          q_boundary,
+                                          project_to_boundary_first);
+            break;
+
+          case 2:
+            project_matrix_free_degree<2>(mapping,
+                                          dof,
+                                          constraints,
+                                          quadrature,
+                                          function,
+                                          work_result,
+                                          enforce_zero_boundary,
+                                          q_boundary,
+                                          project_to_boundary_first);
+            break;
+
+          case 3:
+            project_matrix_free_degree<3>(mapping,
+                                          dof,
+                                          constraints,
+                                          quadrature,
+                                          function,
+                                          work_result,
+                                          enforce_zero_boundary,
+                                          q_boundary,
+                                          project_to_boundary_first);
+            break;
+
+          case 4:
+            project_matrix_free_degree<4>(mapping,
+                                          dof,
+                                          constraints,
+                                          quadrature,
+                                          function,
+                                          work_result,
+                                          enforce_zero_boundary,
+                                          q_boundary,
+                                          project_to_boundary_first);
+            break;
+
+          default:
+            Assert(false, ExcInternalError());
+        }
+    }
+
+
+
+    /**
+     * Helper interface for the matrix-free implementation of project(): avoid
+     * instantiating the other helper functions for more than one VectorType
+     * by copying from a LinearAlgebra::distributed::Vector.
+     */
+    template <int dim, typename VectorType, int spacedim>
+    void
+    project_matrix_free_copy_vector(
+      const Mapping<dim, spacedim> &                             mapping,
+      const DoFHandler<dim, spacedim> &                          dof,
+      const AffineConstraints<typename VectorType::value_type> & constraints,
+      const Quadrature<dim> &                                    quadrature,
+      const Function<spacedim, typename VectorType::value_type> &function,
+      VectorType &                                               vec_result,
+      const bool                 enforce_zero_boundary,
+      const Quadrature<dim - 1> &q_boundary,
+      const bool                 project_to_boundary_first)
+    {
+      Assert(vec_result.size() == dof.n_dofs(),
+             ExcDimensionMismatch(vec_result.size(), dof.n_dofs()));
+
+      LinearAlgebra::distributed::Vector<typename VectorType::value_type>
+        work_result;
+      project_matrix_free_component(mapping,
+                                    dof,
+                                    constraints,
+                                    quadrature,
+                                    function,
+                                    work_result,
+                                    enforce_zero_boundary,
+                                    q_boundary,
+                                    project_to_boundary_first);
+
+      const IndexSet &          locally_owned_dofs = dof.locally_owned_dofs();
+      IndexSet::ElementIterator it                 = locally_owned_dofs.begin();
+      for (; it != locally_owned_dofs.end(); ++it)
+        ::dealii::internal::ElementAccess<VectorType>::set(work_result(*it),
+                                                           *it,
+                                                           vec_result);
+      vec_result.compress(VectorOperation::insert);
+    }
+
+    /**
+     * Return whether the boundary values try to constrain a degree of freedom
+     * that is already constrained to something else
+     */
+    template <typename number>
+    bool
+    constraints_and_b_v_are_compatible(
+      const AffineConstraints<number> &          constraints,
+      std::map<types::global_dof_index, number> &boundary_values)
+    {
+      for (const auto &boundary_value : boundary_values)
+        if (constraints.is_constrained(boundary_value.first))
+          // TODO: This looks wrong -- shouldn't it be ==0 in the first
+          // condition and && ?
+          if (!(constraints.get_constraint_entries(boundary_value.first)
+                    ->size() > 0 ||
+                (constraints.get_inhomogeneity(boundary_value.first) ==
+                 boundary_value.second)))
+            return false;
+
+      return true;
+    }
+
+
+
+    /**
+     * Generic implementation of the project() function
+     */
+    template <int dim,
+              int spacedim,
+              typename VectorType,
+              template <int, int> class DoFHandlerType,
+              template <int, int> class M_or_MC,
+              template <int> class Q_or_QC>
+    void
+    do_project(
+      const M_or_MC<dim, spacedim> &                             mapping,
+      const DoFHandlerType<dim, spacedim> &                      dof,
+      const AffineConstraints<typename VectorType::value_type> & constraints,
+      const Q_or_QC<dim> &                                       quadrature,
+      const Function<spacedim, typename VectorType::value_type> &function,
+      VectorType &                                               vec_result,
+      const bool              enforce_zero_boundary,
+      const Q_or_QC<dim - 1> &q_boundary,
+      const bool              project_to_boundary_first)
+    {
+      using number = typename VectorType::value_type;
+      Assert(dof.get_fe(0).n_components() == function.n_components,
+             ExcDimensionMismatch(dof.get_fe(0).n_components(),
+                                  function.n_components));
+      Assert(vec_result.size() == dof.n_dofs(),
+             ExcDimensionMismatch(vec_result.size(), dof.n_dofs()));
+
+      // make up boundary values
+      std::map<types::global_dof_index, number> boundary_values;
+      project_compute_b_v(mapping,
+                          dof,
+                          function,
+                          enforce_zero_boundary,
+                          q_boundary,
+                          project_to_boundary_first,
+                          boundary_values);
+
+      // check if constraints are compatible (see below)
+      const bool constraints_are_compatible =
+        constraints_and_b_v_are_compatible<number>(constraints,
+                                                   boundary_values);
+
+      // set up mass matrix and right hand side
+      Vector<number>  vec(dof.n_dofs());
+      SparsityPattern sparsity;
+      {
+        DynamicSparsityPattern dsp(dof.n_dofs(), dof.n_dofs());
+        DoFTools::make_sparsity_pattern(dof,
+                                        dsp,
+                                        constraints,
+                                        !constraints_are_compatible);
+
+        sparsity.copy_from(dsp);
+      }
+      SparseMatrix<number> mass_matrix(sparsity);
+      Vector<number>       tmp(mass_matrix.n());
+
+      // If the constraints object does not conflict with the given boundary
+      // values (i.e., it either does not contain boundary values or it contains
+      // the same as boundary_values), we can let it call
+      // distribute_local_to_global straight away, otherwise we need to first
+      // interpolate the boundary values and then condense the matrix and vector
+      if (constraints_are_compatible)
+        {
+          const Function<spacedim, number> *dummy = nullptr;
+          MatrixCreator::create_mass_matrix(mapping,
+                                            dof,
+                                            quadrature,
+                                            mass_matrix,
+                                            function,
+                                            tmp,
+                                            dummy,
+                                            constraints);
+          if (boundary_values.size() > 0)
+            MatrixTools::apply_boundary_values(
+              boundary_values, mass_matrix, vec, tmp, true);
+        }
+      else
+        {
+          // create mass matrix and rhs at once, which is faster.
+          MatrixCreator::create_mass_matrix(
+            mapping, dof, quadrature, mass_matrix, function, tmp);
+          MatrixTools::apply_boundary_values(
+            boundary_values, mass_matrix, vec, tmp, true);
+          constraints.condense(mass_matrix, tmp);
+        }
+
+      // Allow for a maximum of 5*n steps to reduce the residual by 10^-12. n
+      // steps may not be sufficient, since roundoff errors may accumulate for
+      // badly conditioned matrices
+      ReductionControl control(5 * tmp.size(), 0., 1e-12, false, false);
+      GrowingVectorMemory<Vector<number>> memory;
+      SolverCG<Vector<number>>            cg(control, memory);
+
+      PreconditionSSOR<SparseMatrix<number>> prec;
+      prec.initialize(mass_matrix, 1.2);
+
+      cg.solve(mass_matrix, vec, tmp, prec);
+      constraints.distribute(vec);
+
+      // copy vec into vec_result. we can't use vec_result itself above, since
+      // it may be of another type than Vector<double> and that wouldn't
+      // necessarily go together with the matrix and other functions
+      for (unsigned int i = 0; i < vec.size(); ++i)
+        ::dealii::internal::ElementAccess<VectorType>::set(vec(i),
+                                                           i,
+                                                           vec_result);
+    }
+
+    template <int dim, typename VectorType, int spacedim, int fe_degree>
+    void
+    project_parallel(
+      const Mapping<dim, spacedim> &                            mapping,
+      const DoFHandler<dim, spacedim> &                         dof,
+      const AffineConstraints<typename VectorType::value_type> &constraints,
+      const Quadrature<dim> &                                   quadrature,
+      const std::function<typename VectorType::value_type(
+        const typename DoFHandler<dim, spacedim>::active_cell_iterator &,
+        const unsigned int)> &                                  func,
+      VectorType &                                              vec_result)
+    {
+      using Number = typename VectorType::value_type;
+      Assert(dof.get_fe(0).n_components() == 1,
+             ExcDimensionMismatch(dof.get_fe(0).n_components(), 1));
+      Assert(vec_result.size() == dof.n_dofs(),
+             ExcDimensionMismatch(vec_result.size(), dof.n_dofs()));
+      Assert(fe_degree == -1 ||
+               dof.get_fe().degree == static_cast<unsigned int>(fe_degree),
+             ExcDimensionMismatch(fe_degree, dof.get_fe().degree));
+
+      // set up mass matrix and right hand side
+      typename MatrixFree<dim, Number>::AdditionalData additional_data;
+      additional_data.tasks_parallel_scheme =
+        MatrixFree<dim, Number>::AdditionalData::partition_color;
+      additional_data.mapping_update_flags =
+        (update_values | update_JxW_values);
+      std::shared_ptr<MatrixFree<dim, Number>> matrix_free(
+        new MatrixFree<dim, Number>());
+      matrix_free->reinit(mapping,
+                          dof,
+                          constraints,
+                          QGauss<1>(dof.get_fe().degree + 2),
+                          additional_data);
+      using MatrixType = MatrixFreeOperators::MassOperator<
+        dim,
+        fe_degree,
+        fe_degree + 2,
+        1,
+        LinearAlgebra::distributed::Vector<Number>>;
+      MatrixType mass_matrix;
+      mass_matrix.initialize(matrix_free);
+      mass_matrix.compute_diagonal();
+
+      using LocalVectorType = LinearAlgebra::distributed::Vector<Number>;
+      LocalVectorType vec, rhs, inhomogeneities;
+      matrix_free->initialize_dof_vector(vec);
+      matrix_free->initialize_dof_vector(rhs);
+      matrix_free->initialize_dof_vector(inhomogeneities);
+      constraints.distribute(inhomogeneities);
+      inhomogeneities *= -1.;
+
+      // assemble right hand side:
+      {
+        FEValues<dim> fe_values(mapping,
+                                dof.get_fe(),
+                                quadrature,
+                                update_values | update_JxW_values);
+
+        const unsigned int dofs_per_cell = dof.get_fe().dofs_per_cell;
+        const unsigned int n_q_points    = quadrature.size();
+        Vector<Number>     cell_rhs(dofs_per_cell);
+        std::vector<types::global_dof_index> local_dof_indices(dofs_per_cell);
+        typename DoFHandler<dim, spacedim>::active_cell_iterator
+          cell = dof.begin_active(),
+          endc = dof.end();
+        for (; cell != endc; ++cell)
+          if (cell->is_locally_owned())
+            {
+              cell_rhs = 0;
+              fe_values.reinit(cell);
+              for (unsigned int q_point = 0; q_point < n_q_points; ++q_point)
+                {
+                  const double val_q = func(cell, q_point);
+                  for (unsigned int i = 0; i < dofs_per_cell; ++i)
+                    cell_rhs(i) += (fe_values.shape_value(i, q_point) * val_q *
+                                    fe_values.JxW(q_point));
+                }
+
+              cell->get_dof_indices(local_dof_indices);
+              constraints.distribute_local_to_global(cell_rhs,
+                                                     local_dof_indices,
+                                                     rhs);
+            }
+        rhs.compress(VectorOperation::add);
+      }
+
+      mass_matrix.vmult_add(rhs, inhomogeneities);
+
+      // now invert the matrix
+      // Allow for a maximum of 5*n steps to reduce the residual by 10^-12. n
+      // steps may not be sufficient, since roundoff errors may accumulate for
+      // badly conditioned matrices. This behavior can be observed, e.g. for
+      // FE_Q_Hierarchical for degree higher than three.
+      ReductionControl control(5 * rhs.size(), 0., 1e-12, false, false);
+      SolverCG<LinearAlgebra::distributed::Vector<Number>>    cg(control);
+      typename PreconditionJacobi<MatrixType>::AdditionalData data(0.8);
+      PreconditionJacobi<MatrixType>                          preconditioner;
+      preconditioner.initialize(mass_matrix, data);
+      cg.solve(mass_matrix, vec, rhs, preconditioner);
+      vec += inhomogeneities;
+
+      constraints.distribute(vec);
+
+      const IndexSet &          locally_owned_dofs = dof.locally_owned_dofs();
+      IndexSet::ElementIterator it                 = locally_owned_dofs.begin();
+      for (; it != locally_owned_dofs.end(); ++it)
+        ::dealii::internal::ElementAccess<VectorType>::set(vec(*it),
+                                                           *it,
+                                                           vec_result);
+      vec_result.compress(VectorOperation::insert);
+    }
+
+
+
+    template <int dim,
+              typename VectorType,
+              int spacedim,
+              int fe_degree,
+              int n_q_points_1d>
+    void
+    project_parallel(
+      std::shared_ptr<const MatrixFree<dim, typename VectorType::value_type>>
+                                                                matrix_free,
+      const AffineConstraints<typename VectorType::value_type> &constraints,
+      const std::function<VectorizedArray<typename VectorType::value_type>(
+        const unsigned int,
+        const unsigned int)> &                                  func,
+      VectorType &                                              vec_result,
+      const unsigned int                                        fe_component)
+    {
+      const DoFHandler<dim, spacedim> &dof =
+        matrix_free->get_dof_handler(fe_component);
+
+      using Number = typename VectorType::value_type;
+      Assert(dof.get_fe(0).n_components() == 1,
+             ExcDimensionMismatch(dof.get_fe(0).n_components(), 1));
+      Assert(vec_result.size() == dof.n_dofs(),
+             ExcDimensionMismatch(vec_result.size(), dof.n_dofs()));
+      Assert(fe_degree == -1 ||
+               dof.get_fe().degree == static_cast<unsigned int>(fe_degree),
+             ExcDimensionMismatch(fe_degree, dof.get_fe().degree));
+
+      using MatrixType = MatrixFreeOperators::MassOperator<
+        dim,
+        fe_degree,
+        n_q_points_1d,
+        1,
+        LinearAlgebra::distributed::Vector<Number>>;
+      MatrixType mass_matrix;
+      mass_matrix.initialize(matrix_free, {fe_component});
+      mass_matrix.compute_diagonal();
+
+      using LocalVectorType = LinearAlgebra::distributed::Vector<Number>;
+      LocalVectorType vec, rhs, inhomogeneities;
+      matrix_free->initialize_dof_vector(vec, fe_component);
+      matrix_free->initialize_dof_vector(rhs, fe_component);
+      matrix_free->initialize_dof_vector(inhomogeneities, fe_component);
+      constraints.distribute(inhomogeneities);
+      inhomogeneities *= -1.;
+
+      // assemble right hand side:
+      {
+        FEEvaluation<dim, fe_degree, n_q_points_1d, 1, Number> fe_eval(
+          *matrix_free, fe_component);
+        const unsigned int n_cells    = matrix_free->n_macro_cells();
+        const unsigned int n_q_points = fe_eval.n_q_points;
+
+        for (unsigned int cell = 0; cell < n_cells; ++cell)
+          {
+            fe_eval.reinit(cell);
+            for (unsigned int q = 0; q < n_q_points; ++q)
+              fe_eval.submit_value(func(cell, q), q);
+
+            fe_eval.integrate(true, false);
+            fe_eval.distribute_local_to_global(rhs);
+          }
+        rhs.compress(VectorOperation::add);
+      }
+
+      mass_matrix.vmult_add(rhs, inhomogeneities);
+
+      // now invert the matrix
+      // Allow for a maximum of 5*n steps to reduce the residual by 10^-12. n
+      // steps may not be sufficient, since roundoff errors may accumulate for
+      // badly conditioned matrices. This behavior can be observed, e.g. for
+      // FE_Q_Hierarchical for degree higher than three.
+      ReductionControl control(5 * rhs.size(), 0., 1e-12, false, false);
+      SolverCG<LinearAlgebra::distributed::Vector<Number>>    cg(control);
+      typename PreconditionJacobi<MatrixType>::AdditionalData data(0.8);
+      PreconditionJacobi<MatrixType>                          preconditioner;
+      preconditioner.initialize(mass_matrix, data);
+      cg.solve(mass_matrix, vec, rhs, preconditioner);
+      vec += inhomogeneities;
+
+      constraints.distribute(vec);
+
+      const IndexSet &          locally_owned_dofs = dof.locally_owned_dofs();
+      IndexSet::ElementIterator it                 = locally_owned_dofs.begin();
+      for (; it != locally_owned_dofs.end(); ++it)
+        ::dealii::internal::ElementAccess<VectorType>::set(vec(*it),
+                                                           *it,
+                                                           vec_result);
+      vec_result.compress(VectorOperation::insert);
+    }
+
+    /**
+     * Specialization of project() for the case dim==spacedim.
+     * Check if we can use the MatrixFree implementation or need
+     * to use the matrix based one.
+     */
+    template <typename VectorType, int dim>
+    void
+    project(
+      const Mapping<dim> &                                      mapping,
+      const DoFHandler<dim> &                                   dof,
+      const AffineConstraints<typename VectorType::value_type> &constraints,
+      const Quadrature<dim> &                                   quadrature,
+      const Function<dim, typename VectorType::value_type> &    function,
+      VectorType &                                              vec_result,
+      const bool                 enforce_zero_boundary,
+      const Quadrature<dim - 1> &q_boundary,
+      const bool                 project_to_boundary_first)
+    {
+      // If we can, use the matrix-free implementation
+      bool use_matrix_free =
+        MatrixFree<dim, typename VectorType::value_type>::is_supported(
+          dof.get_fe());
+
+      // enforce_zero_boundary and project_to_boundary_first
+      // are not yet supported.
+      // We have explicit instantiations only if
+      // the number of components is not too high.
+      if (enforce_zero_boundary || project_to_boundary_first ||
+          dof.get_fe(0).n_components() > 4)
+        use_matrix_free = false;
+
+      if (use_matrix_free)
+        project_matrix_free_copy_vector(mapping,
+                                        dof,
+                                        constraints,
+                                        quadrature,
+                                        function,
+                                        vec_result,
+                                        enforce_zero_boundary,
+                                        q_boundary,
+                                        project_to_boundary_first);
+      else
+        {
+          Assert((dynamic_cast<const parallel::TriangulationBase<dim> *>(
+                    &(dof.get_triangulation())) == nullptr),
+                 ExcNotImplemented());
+          do_project(mapping,
+                     dof,
+                     constraints,
+                     quadrature,
+                     function,
+                     vec_result,
+                     enforce_zero_boundary,
+                     q_boundary,
+                     project_to_boundary_first);
+        }
+    }
+  } // namespace internal
+
+  template <int dim, typename VectorType, int spacedim>
+  void
+  project(const Mapping<dim, spacedim> &                            mapping,
+          const DoFHandler<dim, spacedim> &                         dof,
+          const AffineConstraints<typename VectorType::value_type> &constraints,
+          const Quadrature<dim> &                                   quadrature,
+          const std::function<typename VectorType::value_type(
+            const typename DoFHandler<dim, spacedim>::active_cell_iterator &,
+            const unsigned int)> &                                  func,
+          VectorType &                                              vec_result)
+  {
+    switch (dof.get_fe().degree)
+      {
+        case 1:
+          internal::project_parallel<dim, VectorType, spacedim, 1>(
+            mapping, dof, constraints, quadrature, func, vec_result);
+          break;
+        case 2:
+          internal::project_parallel<dim, VectorType, spacedim, 2>(
+            mapping, dof, constraints, quadrature, func, vec_result);
+          break;
+        case 3:
+          internal::project_parallel<dim, VectorType, spacedim, 3>(
+            mapping, dof, constraints, quadrature, func, vec_result);
+          break;
+        default:
+          internal::project_parallel<dim, VectorType, spacedim, -1>(
+            mapping, dof, constraints, quadrature, func, vec_result);
+      }
+  }
+
+
+
+  template <int dim, typename VectorType>
+  void
+  project(std::shared_ptr<const MatrixFree<
+            dim,
+            typename VectorType::value_type,
+            VectorizedArray<typename VectorType::value_type>>>      matrix_free,
+          const AffineConstraints<typename VectorType::value_type> &constraints,
+          const unsigned int      n_q_points_1d,
+          const std::function<VectorizedArray<typename VectorType::value_type>(
+            const unsigned int,
+            const unsigned int)> &func,
+          VectorType &            vec_result,
+          const unsigned int      fe_component)
+  {
+    const unsigned int fe_degree =
+      matrix_free->get_dof_handler(fe_component).get_fe().degree;
+
+    if (fe_degree + 1 == n_q_points_1d)
+      switch (fe_degree)
+        {
+          case 1:
+            internal::project_parallel<dim, VectorType, dim, 1, 2>(
+              matrix_free, constraints, func, vec_result, fe_component);
+            break;
+          case 2:
+            internal::project_parallel<dim, VectorType, dim, 2, 3>(
+              matrix_free, constraints, func, vec_result, fe_component);
+            break;
+          case 3:
+            internal::project_parallel<dim, VectorType, dim, 3, 4>(
+              matrix_free, constraints, func, vec_result, fe_component);
+            break;
+          default:
+            internal::project_parallel<dim, VectorType, dim, -1, 0>(
+              matrix_free, constraints, func, vec_result, fe_component);
+        }
+    else
+      internal::project_parallel<dim, VectorType, dim, -1, 0>(
+        matrix_free, constraints, func, vec_result, fe_component);
+  }
+
+
+
+  template <int dim, typename VectorType>
+  void
+  project(std::shared_ptr<const MatrixFree<
+            dim,
+            typename VectorType::value_type,
+            VectorizedArray<typename VectorType::value_type>>>      matrix_free,
+          const AffineConstraints<typename VectorType::value_type> &constraints,
+          const std::function<VectorizedArray<typename VectorType::value_type>(
+            const unsigned int,
+            const unsigned int)> &                                  func,
+          VectorType &                                              vec_result,
+          const unsigned int fe_component)
+  {
+    project(matrix_free,
+            constraints,
+            matrix_free->get_dof_handler(fe_component).get_fe().degree + 1,
+            func,
+            vec_result,
+            fe_component);
+  }
+
+
+
+  template <int dim, typename VectorType, int spacedim>
+  void
+  project(const Mapping<dim, spacedim> &                            mapping,
+          const DoFHandler<dim, spacedim> &                         dof,
+          const AffineConstraints<typename VectorType::value_type> &constraints,
+          const Quadrature<dim> &                                   quadrature,
+          const Function<spacedim, typename VectorType::value_type> &function,
+          VectorType &                                               vec_result,
+          const bool                 enforce_zero_boundary,
+          const Quadrature<dim - 1> &q_boundary,
+          const bool                 project_to_boundary_first)
+  {
+    if (dim == spacedim)
+      {
+        const Mapping<dim> *const mapping_ptr =
+          dynamic_cast<const Mapping<dim> *>(&mapping);
+        const DoFHandler<dim> *const dof_ptr =
+          dynamic_cast<const DoFHandler<dim> *>(&dof);
+        const Function<dim,
+                       typename VectorType::value_type> *const function_ptr =
+          dynamic_cast<const Function<dim, typename VectorType::value_type> *>(
+            &function);
+        Assert(mapping_ptr != nullptr, ExcInternalError());
+        Assert(dof_ptr != nullptr, ExcInternalError());
+        internal::project<VectorType, dim>(*mapping_ptr,
+                                           *dof_ptr,
+                                           constraints,
+                                           quadrature,
+                                           *function_ptr,
+                                           vec_result,
+                                           enforce_zero_boundary,
+                                           q_boundary,
+                                           project_to_boundary_first);
+      }
+    else
+      {
+        Assert(
+          (dynamic_cast<const parallel::TriangulationBase<dim, spacedim> *>(
+             &(dof.get_triangulation())) == nullptr),
+          ExcNotImplemented());
+        internal::do_project(mapping,
+                             dof,
+                             constraints,
+                             quadrature,
+                             function,
+                             vec_result,
+                             enforce_zero_boundary,
+                             q_boundary,
+                             project_to_boundary_first);
+      }
+  }
+
+
+
+  template <int dim, typename VectorType, int spacedim>
+  void
+  project(const DoFHandler<dim, spacedim> &                         dof,
+          const AffineConstraints<typename VectorType::value_type> &constraints,
+          const Quadrature<dim> &                                   quadrature,
+          const Function<spacedim, typename VectorType::value_type> &function,
+          VectorType &                                               vec,
+          const bool                 enforce_zero_boundary,
+          const Quadrature<dim - 1> &q_boundary,
+          const bool                 project_to_boundary_first)
+  {
+#ifdef _MSC_VER
+    Assert(false,
+           ExcMessage("Please specify the mapping explicitly "
+                      "when building with MSVC!"));
+#else
+    project(StaticMappingQ1<dim, spacedim>::mapping,
+            dof,
+            constraints,
+            quadrature,
+            function,
+            vec,
+            enforce_zero_boundary,
+            q_boundary,
+            project_to_boundary_first);
+#endif
+  }
+
+
+
+  template <int dim, typename VectorType, int spacedim>
+  void
+  project(const hp::MappingCollection<dim, spacedim> &              mapping,
+          const hp::DoFHandler<dim, spacedim> &                     dof,
+          const AffineConstraints<typename VectorType::value_type> &constraints,
+          const hp::QCollection<dim> &                              quadrature,
+          const Function<spacedim, typename VectorType::value_type> &function,
+          VectorType &                                               vec_result,
+          const bool                      enforce_zero_boundary,
+          const hp::QCollection<dim - 1> &q_boundary,
+          const bool                      project_to_boundary_first)
+  {
+    Assert((dynamic_cast<const parallel::TriangulationBase<dim, spacedim> *>(
+              &(dof.get_triangulation())) == nullptr),
+           ExcNotImplemented());
+
+    internal::do_project(mapping,
+                         dof,
+                         constraints,
+                         quadrature,
+                         function,
+                         vec_result,
+                         enforce_zero_boundary,
+                         q_boundary,
+                         project_to_boundary_first);
+  }
+
+
+  template <int dim, typename VectorType, int spacedim>
+  void
+  project(const hp::DoFHandler<dim, spacedim> &                     dof,
+          const AffineConstraints<typename VectorType::value_type> &constraints,
+          const hp::QCollection<dim> &                              quadrature,
+          const Function<spacedim, typename VectorType::value_type> &function,
+          VectorType &                                               vec,
+          const bool                      enforce_zero_boundary,
+          const hp::QCollection<dim - 1> &q_boundary,
+          const bool                      project_to_boundary_first)
+  {
+    project(hp::StaticMappingQ1<dim, spacedim>::mapping_collection,
+            dof,
+            constraints,
+            quadrature,
+            function,
+            vec,
+            enforce_zero_boundary,
+            q_boundary,
+            project_to_boundary_first);
+  }
+} // namespace VectorTools
+
+DEAL_II_NAMESPACE_CLOSE
+
+#endif // dealii_vector_tools_project_templates_h
diff --git a/include/deal.II/numerics/vector_tools_rhs.templates.h b/include/deal.II/numerics/vector_tools_rhs.templates.h
new file mode 100644 (file)
index 0000000..1bef5a3
--- /dev/null
@@ -0,0 +1,644 @@
+// ---------------------------------------------------------------------
+//
+// Copyright (C) 1998 - 2018 by the deal.II authors
+//
+// This file is part of the deal.II library.
+//
+// The deal.II library is free software; you can use it, redistribute
+// it, and/or modify it under the terms of the GNU Lesser General
+// Public License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+// The full text of the license can be found in the file LICENSE.md at
+// the top level directory of deal.II.
+//
+// ---------------------------------------------------------------------
+
+#ifndef dealii_vector_tools_rhs_templates_h
+#define dealii_vector_tools_rhs_templates_h
+
+
+#include <deal.II/hp/fe_values.h>
+
+#include <deal.II/lac/block_vector.h>
+#include <deal.II/lac/la_parallel_block_vector.h>
+#include <deal.II/lac/la_parallel_vector.h>
+#include <deal.II/lac/la_vector.h>
+#include <deal.II/lac/petsc_block_vector.h>
+#include <deal.II/lac/petsc_vector.h>
+#include <deal.II/lac/trilinos_parallel_block_vector.h>
+#include <deal.II/lac/trilinos_vector.h>
+
+#include <deal.II/numerics/vector_tools.h>
+
+
+DEAL_II_NAMESPACE_OPEN
+
+namespace VectorTools
+{
+  template <int dim, int spacedim, typename VectorType>
+  void
+  create_boundary_right_hand_side(
+    const Mapping<dim, spacedim> &                             mapping,
+    const DoFHandler<dim, spacedim> &                          dof_handler,
+    const Quadrature<dim - 1> &                                quadrature,
+    const Function<spacedim, typename VectorType::value_type> &rhs_function,
+    VectorType &                                               rhs_vector,
+    const std::set<types::boundary_id> &                       boundary_ids)
+  {
+    const FiniteElement<dim> &fe = dof_handler.get_fe();
+    Assert(fe.n_components() == rhs_function.n_components,
+           ExcDimensionMismatch(fe.n_components(), rhs_function.n_components));
+    Assert(rhs_vector.size() == dof_handler.n_dofs(),
+           ExcDimensionMismatch(rhs_vector.size(), dof_handler.n_dofs()));
+
+    rhs_vector = 0;
+
+    UpdateFlags update_flags =
+      UpdateFlags(update_values | update_quadrature_points | update_JxW_values);
+    FEFaceValues<dim> fe_values(mapping, fe, quadrature, update_flags);
+
+    const unsigned int dofs_per_cell = fe_values.dofs_per_cell,
+                       n_q_points    = fe_values.n_quadrature_points,
+                       n_components  = fe.n_components();
+
+    std::vector<types::global_dof_index> dofs(dofs_per_cell);
+    Vector<double>                       cell_vector(dofs_per_cell);
+
+    typename DoFHandler<dim, spacedim>::active_cell_iterator
+      cell = dof_handler.begin_active(),
+      endc = dof_handler.end();
+
+    if (n_components == 1)
+      {
+        std::vector<double> rhs_values(n_q_points);
+
+        for (; cell != endc; ++cell)
+          for (unsigned int face : GeometryInfo<dim>::face_indices())
+            if (cell->face(face)->at_boundary() &&
+                (boundary_ids.empty() ||
+                 (boundary_ids.find(cell->face(face)->boundary_id()) !=
+                  boundary_ids.end())))
+              {
+                fe_values.reinit(cell, face);
+
+                const std::vector<double> &weights = fe_values.get_JxW_values();
+                rhs_function.value_list(fe_values.get_quadrature_points(),
+                                        rhs_values);
+
+                cell_vector = 0;
+                for (unsigned int point = 0; point < n_q_points; ++point)
+                  for (unsigned int i = 0; i < dofs_per_cell; ++i)
+                    cell_vector(i) += rhs_values[point] *
+                                      fe_values.shape_value(i, point) *
+                                      weights[point];
+
+                cell->get_dof_indices(dofs);
+
+                for (unsigned int i = 0; i < dofs_per_cell; ++i)
+                  rhs_vector(dofs[i]) += cell_vector(i);
+              }
+      }
+    else
+      {
+        std::vector<Vector<double>> rhs_values(n_q_points,
+                                               Vector<double>(n_components));
+
+        for (; cell != endc; ++cell)
+          for (unsigned int face : GeometryInfo<dim>::face_indices())
+            if (cell->face(face)->at_boundary() &&
+                (boundary_ids.empty() ||
+                 (boundary_ids.find(cell->face(face)->boundary_id()) !=
+                  boundary_ids.end())))
+              {
+                fe_values.reinit(cell, face);
+
+                const std::vector<double> &weights = fe_values.get_JxW_values();
+                rhs_function.vector_value_list(
+                  fe_values.get_quadrature_points(), rhs_values);
+
+                cell_vector = 0;
+
+                // Use the faster code if the
+                // FiniteElement is primitive
+                if (fe.is_primitive())
+                  {
+                    for (unsigned int point = 0; point < n_q_points; ++point)
+                      for (unsigned int i = 0; i < dofs_per_cell; ++i)
+                        {
+                          const unsigned int component =
+                            fe.system_to_component_index(i).first;
+
+                          cell_vector(i) += rhs_values[point](component) *
+                                            fe_values.shape_value(i, point) *
+                                            weights[point];
+                        }
+                  }
+                else
+                  {
+                    // And the full featured
+                    // code, if vector valued
+                    // FEs are used
+                    for (unsigned int point = 0; point < n_q_points; ++point)
+                      for (unsigned int i = 0; i < dofs_per_cell; ++i)
+                        for (unsigned int comp_i = 0; comp_i < n_components;
+                             ++comp_i)
+                          if (fe.get_nonzero_components(i)[comp_i])
+                            {
+                              cell_vector(i) +=
+                                rhs_values[point](comp_i) *
+                                fe_values.shape_value_component(i,
+                                                                point,
+                                                                comp_i) *
+                                weights[point];
+                            }
+                  }
+
+                cell->get_dof_indices(dofs);
+
+                for (unsigned int i = 0; i < dofs_per_cell; ++i)
+                  rhs_vector(dofs[i]) += cell_vector(i);
+              }
+      }
+  }
+
+
+
+  template <int dim, int spacedim, typename VectorType>
+  void
+  create_boundary_right_hand_side(
+    const DoFHandler<dim, spacedim> &                          dof_handler,
+    const Quadrature<dim - 1> &                                quadrature,
+    const Function<spacedim, typename VectorType::value_type> &rhs_function,
+    VectorType &                                               rhs_vector,
+    const std::set<types::boundary_id> &                       boundary_ids)
+  {
+    create_boundary_right_hand_side(StaticMappingQ1<dim>::mapping,
+                                    dof_handler,
+                                    quadrature,
+                                    rhs_function,
+                                    rhs_vector,
+                                    boundary_ids);
+  }
+
+
+
+  template <int dim, int spacedim, typename VectorType>
+  void
+  create_boundary_right_hand_side(
+    const hp::MappingCollection<dim, spacedim> &               mapping,
+    const hp::DoFHandler<dim, spacedim> &                      dof_handler,
+    const hp::QCollection<dim - 1> &                           quadrature,
+    const Function<spacedim, typename VectorType::value_type> &rhs_function,
+    VectorType &                                               rhs_vector,
+    const std::set<types::boundary_id> &                       boundary_ids)
+  {
+    const hp::FECollection<dim> &fe = dof_handler.get_fe_collection();
+    Assert(fe.n_components() == rhs_function.n_components,
+           ExcDimensionMismatch(fe.n_components(), rhs_function.n_components));
+    Assert(rhs_vector.size() == dof_handler.n_dofs(),
+           ExcDimensionMismatch(rhs_vector.size(), dof_handler.n_dofs()));
+
+    rhs_vector = 0;
+
+    UpdateFlags update_flags =
+      UpdateFlags(update_values | update_quadrature_points | update_JxW_values);
+    hp::FEFaceValues<dim> x_fe_values(mapping, fe, quadrature, update_flags);
+
+    const unsigned int n_components = fe.n_components();
+
+    std::vector<types::global_dof_index> dofs(fe.max_dofs_per_cell());
+    Vector<double>                       cell_vector(fe.max_dofs_per_cell());
+
+    typename hp::DoFHandler<dim, spacedim>::active_cell_iterator
+      cell = dof_handler.begin_active(),
+      endc = dof_handler.end();
+
+    if (n_components == 1)
+      {
+        std::vector<double> rhs_values;
+
+        for (; cell != endc; ++cell)
+          for (unsigned int face : GeometryInfo<dim>::face_indices())
+            if (cell->face(face)->at_boundary() &&
+                (boundary_ids.empty() ||
+                 (boundary_ids.find(cell->face(face)->boundary_id()) !=
+                  boundary_ids.end())))
+              {
+                x_fe_values.reinit(cell, face);
+
+                const FEFaceValues<dim> &fe_values =
+                  x_fe_values.get_present_fe_values();
+
+                const unsigned int dofs_per_cell = fe_values.dofs_per_cell,
+                                   n_q_points = fe_values.n_quadrature_points;
+                rhs_values.resize(n_q_points);
+
+                const std::vector<double> &weights = fe_values.get_JxW_values();
+                rhs_function.value_list(fe_values.get_quadrature_points(),
+                                        rhs_values);
+
+                cell_vector = 0;
+                for (unsigned int point = 0; point < n_q_points; ++point)
+                  for (unsigned int i = 0; i < dofs_per_cell; ++i)
+                    cell_vector(i) += rhs_values[point] *
+                                      fe_values.shape_value(i, point) *
+                                      weights[point];
+
+                dofs.resize(dofs_per_cell);
+                cell->get_dof_indices(dofs);
+
+                for (unsigned int i = 0; i < dofs_per_cell; ++i)
+                  rhs_vector(dofs[i]) += cell_vector(i);
+              }
+      }
+    else
+      {
+        std::vector<Vector<double>> rhs_values;
+
+        for (; cell != endc; ++cell)
+          for (unsigned int face : GeometryInfo<dim>::face_indices())
+            if (cell->face(face)->at_boundary() &&
+                (boundary_ids.empty() ||
+                 (boundary_ids.find(cell->face(face)->boundary_id()) !=
+                  boundary_ids.end())))
+              {
+                x_fe_values.reinit(cell, face);
+
+                const FEFaceValues<dim> &fe_values =
+                  x_fe_values.get_present_fe_values();
+
+                const unsigned int dofs_per_cell = fe_values.dofs_per_cell,
+                                   n_q_points = fe_values.n_quadrature_points;
+                rhs_values.resize(n_q_points, Vector<double>(n_components));
+
+                const std::vector<double> &weights = fe_values.get_JxW_values();
+                rhs_function.vector_value_list(
+                  fe_values.get_quadrature_points(), rhs_values);
+
+                cell_vector = 0;
+
+                // Use the faster code if the
+                // FiniteElement is primitive
+                if (cell->get_fe().is_primitive())
+                  {
+                    for (unsigned int point = 0; point < n_q_points; ++point)
+                      for (unsigned int i = 0; i < dofs_per_cell; ++i)
+                        {
+                          const unsigned int component =
+                            cell->get_fe().system_to_component_index(i).first;
+
+                          cell_vector(i) += rhs_values[point](component) *
+                                            fe_values.shape_value(i, point) *
+                                            weights[point];
+                        }
+                  }
+                else
+                  {
+                    // And the full featured
+                    // code, if vector valued
+                    // FEs are used
+                    for (unsigned int point = 0; point < n_q_points; ++point)
+                      for (unsigned int i = 0; i < dofs_per_cell; ++i)
+                        for (unsigned int comp_i = 0; comp_i < n_components;
+                             ++comp_i)
+                          if (cell->get_fe().get_nonzero_components(i)[comp_i])
+                            {
+                              cell_vector(i) +=
+                                rhs_values[point](comp_i) *
+                                fe_values.shape_value_component(i,
+                                                                point,
+                                                                comp_i) *
+                                weights[point];
+                            }
+                  }
+                dofs.resize(dofs_per_cell);
+                cell->get_dof_indices(dofs);
+
+                for (unsigned int i = 0; i < dofs_per_cell; ++i)
+                  rhs_vector(dofs[i]) += cell_vector(i);
+              }
+      }
+  }
+
+
+
+  template <int dim, int spacedim, typename VectorType>
+  void
+  create_boundary_right_hand_side(
+    const hp::DoFHandler<dim, spacedim> &                      dof_handler,
+    const hp::QCollection<dim - 1> &                           quadrature,
+    const Function<spacedim, typename VectorType::value_type> &rhs_function,
+    VectorType &                                               rhs_vector,
+    const std::set<types::boundary_id> &                       boundary_ids)
+  {
+    create_boundary_right_hand_side(
+      hp::StaticMappingQ1<dim>::mapping_collection,
+      dof_handler,
+      quadrature,
+      rhs_function,
+      rhs_vector,
+      boundary_ids);
+  }
+
+  template <int dim, int spacedim, typename VectorType>
+  void
+  create_right_hand_side(
+    const Mapping<dim, spacedim> &                             mapping,
+    const DoFHandler<dim, spacedim> &                          dof_handler,
+    const Quadrature<dim> &                                    quadrature,
+    const Function<spacedim, typename VectorType::value_type> &rhs_function,
+    VectorType &                                               rhs_vector,
+    const AffineConstraints<typename VectorType::value_type> & constraints)
+  {
+    using Number = typename VectorType::value_type;
+
+    const FiniteElement<dim, spacedim> &fe = dof_handler.get_fe();
+    Assert(fe.n_components() == rhs_function.n_components,
+           ExcDimensionMismatch(fe.n_components(), rhs_function.n_components));
+    Assert(rhs_vector.size() == dof_handler.n_dofs(),
+           ExcDimensionMismatch(rhs_vector.size(), dof_handler.n_dofs()));
+    rhs_vector = typename VectorType::value_type(0.);
+
+    UpdateFlags update_flags =
+      UpdateFlags(update_values | update_quadrature_points | update_JxW_values);
+    FEValues<dim, spacedim> fe_values(mapping, fe, quadrature, update_flags);
+
+    const unsigned int dofs_per_cell = fe_values.dofs_per_cell,
+                       n_q_points    = fe_values.n_quadrature_points,
+                       n_components  = fe.n_components();
+
+    std::vector<types::global_dof_index> dofs(dofs_per_cell);
+    Vector<Number>                       cell_vector(dofs_per_cell);
+
+    typename DoFHandler<dim, spacedim>::active_cell_iterator
+      cell = dof_handler.begin_active(),
+      endc = dof_handler.end();
+
+    if (n_components == 1)
+      {
+        std::vector<Number> rhs_values(n_q_points);
+
+        for (; cell != endc; ++cell)
+          if (cell->is_locally_owned())
+            {
+              fe_values.reinit(cell);
+
+              const std::vector<double> &weights = fe_values.get_JxW_values();
+              rhs_function.value_list(fe_values.get_quadrature_points(),
+                                      rhs_values);
+
+              cell_vector = 0;
+              for (unsigned int point = 0; point < n_q_points; ++point)
+                for (unsigned int i = 0; i < dofs_per_cell; ++i)
+                  cell_vector(i) += rhs_values[point] *
+                                    fe_values.shape_value(i, point) *
+                                    weights[point];
+
+              cell->get_dof_indices(dofs);
+
+              constraints.distribute_local_to_global(cell_vector,
+                                                     dofs,
+                                                     rhs_vector);
+            }
+      }
+    else
+      {
+        std::vector<Vector<Number>> rhs_values(n_q_points,
+                                               Vector<Number>(n_components));
+
+        for (; cell != endc; ++cell)
+          if (cell->is_locally_owned())
+            {
+              fe_values.reinit(cell);
+
+              const std::vector<double> &weights = fe_values.get_JxW_values();
+              rhs_function.vector_value_list(fe_values.get_quadrature_points(),
+                                             rhs_values);
+
+              cell_vector = 0;
+              // Use the faster code if the
+              // FiniteElement is primitive
+              if (fe.is_primitive())
+                {
+                  for (unsigned int point = 0; point < n_q_points; ++point)
+                    for (unsigned int i = 0; i < dofs_per_cell; ++i)
+                      {
+                        const unsigned int component =
+                          fe.system_to_component_index(i).first;
+
+                        cell_vector(i) += rhs_values[point](component) *
+                                          fe_values.shape_value(i, point) *
+                                          weights[point];
+                      }
+                }
+              else
+                {
+                  // Otherwise do it the way
+                  // proposed for vector valued
+                  // elements
+                  for (unsigned int point = 0; point < n_q_points; ++point)
+                    for (unsigned int i = 0; i < dofs_per_cell; ++i)
+                      for (unsigned int comp_i = 0; comp_i < n_components;
+                           ++comp_i)
+                        if (fe.get_nonzero_components(i)[comp_i])
+                          {
+                            cell_vector(i) +=
+                              rhs_values[point](comp_i) *
+                              fe_values.shape_value_component(i,
+                                                              point,
+                                                              comp_i) *
+                              weights[point];
+                          }
+                }
+              cell->get_dof_indices(dofs);
+
+              constraints.distribute_local_to_global(cell_vector,
+                                                     dofs,
+                                                     rhs_vector);
+            }
+      }
+  }
+
+
+
+  template <int dim, int spacedim, typename VectorType>
+  void
+  create_right_hand_side(
+    const DoFHandler<dim, spacedim> &                          dof_handler,
+    const Quadrature<dim> &                                    quadrature,
+    const Function<spacedim, typename VectorType::value_type> &rhs_function,
+    VectorType &                                               rhs_vector,
+    const AffineConstraints<typename VectorType::value_type> & constraints)
+  {
+    create_right_hand_side(StaticMappingQ1<dim, spacedim>::mapping,
+                           dof_handler,
+                           quadrature,
+                           rhs_function,
+                           rhs_vector,
+                           constraints);
+  }
+
+
+
+  template <int dim, int spacedim, typename VectorType>
+  void
+  create_right_hand_side(
+    const hp::MappingCollection<dim, spacedim> &               mapping,
+    const hp::DoFHandler<dim, spacedim> &                      dof_handler,
+    const hp::QCollection<dim> &                               quadrature,
+    const Function<spacedim, typename VectorType::value_type> &rhs_function,
+    VectorType &                                               rhs_vector,
+    const AffineConstraints<typename VectorType::value_type> & constraints)
+  {
+    using Number = typename VectorType::value_type;
+
+    const hp::FECollection<dim, spacedim> &fe = dof_handler.get_fe_collection();
+    Assert(fe.n_components() == rhs_function.n_components,
+           ExcDimensionMismatch(fe.n_components(), rhs_function.n_components));
+    Assert(rhs_vector.size() == dof_handler.n_dofs(),
+           ExcDimensionMismatch(rhs_vector.size(), dof_handler.n_dofs()));
+    rhs_vector = 0;
+
+    UpdateFlags update_flags =
+      UpdateFlags(update_values | update_quadrature_points | update_JxW_values);
+    hp::FEValues<dim, spacedim> x_fe_values(mapping,
+                                            fe,
+                                            quadrature,
+                                            update_flags);
+
+    const unsigned int n_components = fe.n_components();
+
+    std::vector<types::global_dof_index> dofs(fe.max_dofs_per_cell());
+    Vector<Number>                       cell_vector(fe.max_dofs_per_cell());
+
+    typename hp::DoFHandler<dim, spacedim>::active_cell_iterator
+      cell = dof_handler.begin_active(),
+      endc = dof_handler.end();
+
+    if (n_components == 1)
+      {
+        std::vector<Number> rhs_values;
+
+        for (; cell != endc; ++cell)
+          if (cell->is_locally_owned())
+            {
+              x_fe_values.reinit(cell);
+
+              const FEValues<dim, spacedim> &fe_values =
+                x_fe_values.get_present_fe_values();
+
+              const unsigned int dofs_per_cell = fe_values.dofs_per_cell,
+                                 n_q_points    = fe_values.n_quadrature_points;
+              rhs_values.resize(n_q_points);
+              dofs.resize(dofs_per_cell);
+              cell_vector.reinit(dofs_per_cell);
+
+              const std::vector<Number> &weights = fe_values.get_JxW_values();
+              rhs_function.value_list(fe_values.get_quadrature_points(),
+                                      rhs_values);
+
+              cell_vector = 0;
+              for (unsigned int point = 0; point < n_q_points; ++point)
+                for (unsigned int i = 0; i < dofs_per_cell; ++i)
+                  cell_vector(i) += rhs_values[point] *
+                                    fe_values.shape_value(i, point) *
+                                    weights[point];
+
+              cell->get_dof_indices(dofs);
+
+              constraints.distribute_local_to_global(cell_vector,
+                                                     dofs,
+                                                     rhs_vector);
+            }
+      }
+    else
+      {
+        std::vector<Vector<Number>> rhs_values;
+
+        for (; cell != endc; ++cell)
+          if (cell->is_locally_owned())
+            {
+              x_fe_values.reinit(cell);
+
+              const FEValues<dim, spacedim> &fe_values =
+                x_fe_values.get_present_fe_values();
+
+              const unsigned int dofs_per_cell = fe_values.dofs_per_cell,
+                                 n_q_points    = fe_values.n_quadrature_points;
+              rhs_values.resize(n_q_points, Vector<Number>(n_components));
+              dofs.resize(dofs_per_cell);
+              cell_vector.reinit(dofs_per_cell);
+
+              const std::vector<Number> &weights = fe_values.get_JxW_values();
+              rhs_function.vector_value_list(fe_values.get_quadrature_points(),
+                                             rhs_values);
+
+              cell_vector = 0;
+
+              // Use the faster code if the
+              // FiniteElement is primitive
+              if (cell->get_fe().is_primitive())
+                {
+                  for (unsigned int point = 0; point < n_q_points; ++point)
+                    for (unsigned int i = 0; i < dofs_per_cell; ++i)
+                      {
+                        const unsigned int component =
+                          cell->get_fe().system_to_component_index(i).first;
+
+                        cell_vector(i) += rhs_values[point](component) *
+                                          fe_values.shape_value(i, point) *
+                                          weights[point];
+                      }
+                }
+              else
+                {
+                  // Otherwise do it the way proposed
+                  // for vector valued elements
+                  for (unsigned int point = 0; point < n_q_points; ++point)
+                    for (unsigned int i = 0; i < dofs_per_cell; ++i)
+                      for (unsigned int comp_i = 0; comp_i < n_components;
+                           ++comp_i)
+                        if (cell->get_fe().get_nonzero_components(i)[comp_i])
+                          {
+                            cell_vector(i) +=
+                              rhs_values[point](comp_i) *
+                              fe_values.shape_value_component(i,
+                                                              point,
+                                                              comp_i) *
+                              weights[point];
+                          }
+                }
+
+              cell->get_dof_indices(dofs);
+
+              constraints.distribute_local_to_global(cell_vector,
+                                                     dofs,
+                                                     rhs_vector);
+            }
+      }
+  }
+
+
+
+  template <int dim, int spacedim, typename VectorType>
+  void
+  create_right_hand_side(
+    const hp::DoFHandler<dim, spacedim> &                      dof_handler,
+    const hp::QCollection<dim> &                               quadrature,
+    const Function<spacedim, typename VectorType::value_type> &rhs_function,
+    VectorType &                                               rhs_vector,
+    const AffineConstraints<typename VectorType::value_type> & constraints)
+  {
+    create_right_hand_side(
+      hp::StaticMappingQ1<dim, spacedim>::mapping_collection,
+      dof_handler,
+      quadrature,
+      rhs_function,
+      rhs_vector,
+      constraints);
+  }
+} // namespace VectorTools
+
+DEAL_II_NAMESPACE_CLOSE
+
+#endif // dealii_vector_tools_rhs_templates_h
index e3a12974feb827f3353ce0ea1ec7f6ee5d3fb5e0..a07eb0ae6385ece6219461cd975b5c3e313eb1b2 100644 (file)
@@ -14,7 +14,7 @@
 // ---------------------------------------------------------------------
 
 
-#include <deal.II/numerics/vector_tools.templates.h>
+#include <deal.II/numerics/vector_tools_boundary.templates.h>
 
 DEAL_II_NAMESPACE_OPEN
 
index 5fc9d7bf1cc25e3b138447c94e0bbbe204ea1334..85779dcfcd4e58e09d54c59fb04a937254dced88 100644 (file)
@@ -14,7 +14,7 @@
 // ---------------------------------------------------------------------
 
 
-#include <deal.II/numerics/vector_tools.templates.h>
+#include <deal.II/numerics/vector_tools_constraints.templates.h>
 
 DEAL_II_NAMESPACE_OPEN
 
index 3427c3455c718fcf62ff07f43c8a135e08769980..2e5494bd213eb794e91900010f9db7b1b5617fc4 100644 (file)
@@ -14,7 +14,7 @@
 // ---------------------------------------------------------------------
 
 
-#include <deal.II/numerics/vector_tools.templates.h>
+#include <deal.II/numerics/vector_tools_integrate_difference.templates.h>
 
 DEAL_II_NAMESPACE_OPEN
 
index 7897902fc0c41449eba3638f5cf98cb62fd97376..ba7e51cd5e01f124f513da9ed5517e78a0ca83c5 100644 (file)
@@ -14,7 +14,7 @@
 // ---------------------------------------------------------------------
 
 
-#include <deal.II/numerics/vector_tools.templates.h>
+#include <deal.II/numerics/vector_tools_interpolate.templates.h>
 
 DEAL_II_NAMESPACE_OPEN
 
index f1d3229d2999c134ac19d4de5af3240ef714f1d2..b4c1f15f29aea133f30bdfe6cf93a304451e284e 100644 (file)
@@ -14,7 +14,7 @@
 // ---------------------------------------------------------------------
 
 
-#include <deal.II/numerics/vector_tools.templates.h>
+#include <deal.II/numerics/vector_tools_mean_value.templates.h>
 
 DEAL_II_NAMESPACE_OPEN
 
index a0ef0f7994210167fb8f0b8dfef06b56ba9606d4..436b8667ea3b8e9486395ca05276298c0a1a1695 100644 (file)
@@ -14,7 +14,7 @@
 // ---------------------------------------------------------------------
 
 
-#include <deal.II/numerics/vector_tools.templates.h>
+#include <deal.II/numerics/vector_tools_point_gradient.templates.h>
 
 DEAL_II_NAMESPACE_OPEN
 
index 3efaa572fe67527711ec814b90a6b39b221a6022..98fcb9308da187ef68975e14c45310c7a35dd918 100644 (file)
@@ -14,7 +14,7 @@
 // ---------------------------------------------------------------------
 
 
-#include <deal.II/numerics/vector_tools.templates.h>
+#include <deal.II/numerics/vector_tools_point_value.templates.h>
 
 DEAL_II_NAMESPACE_OPEN
 
index 42b2a4984a1438ce20a18bf47c95408a088d744f..d96f198b7b6f964e40c1c6985221d4d91d27d45e 100644 (file)
@@ -14,7 +14,7 @@
 // ---------------------------------------------------------------------
 
 
-#include <deal.II/numerics/vector_tools.templates.h>
+#include <deal.II/numerics/vector_tools_project.templates.h>
 
 DEAL_II_NAMESPACE_OPEN
 
index b2a7641ec9c2ee068f5d1301336d1c2f81551f3f..52da94895f7a79a0147ea03e4c73a2b1944b36cd 100644 (file)
@@ -14,7 +14,7 @@
 // ---------------------------------------------------------------------
 
 
-#include <deal.II/numerics/vector_tools.templates.h>
+#include <deal.II/numerics/vector_tools_project.templates.h>
 
 DEAL_II_NAMESPACE_OPEN
 
index 1eee77e5945d0bf873bf239f73786ccfd28e47c0..e3588cbb2cc78dda83a9b1b59a312342466c8af3 100644 (file)
@@ -14,7 +14,8 @@
 // ---------------------------------------------------------------------
 
 
-#include <deal.II/numerics/vector_tools.templates.h>
+#include <deal.II/numerics/vector_tools_project.templates.h>
+
 
 DEAL_II_NAMESPACE_OPEN
 
index f19bc7a3e3e97d00298414bc961a7800c71bf9b0..2afe935448c8e4054b60d242010caedb8b30904d 100644 (file)
@@ -14,7 +14,8 @@
 // ---------------------------------------------------------------------
 
 
-#include <deal.II/numerics/vector_tools.templates.h>
+#include <deal.II/numerics/vector_tools_project.templates.h>
+
 
 DEAL_II_NAMESPACE_OPEN
 
index 8fd28bd00eecf623ddb1423168b0082a1b872de7..cebb158350c094996e74deec5e5d7b3cc4424f67 100644 (file)
@@ -14,7 +14,7 @@
 // ---------------------------------------------------------------------
 
 
-#include <deal.II/numerics/vector_tools.templates.h>
+#include <deal.II/numerics/vector_tools_project.templates.h>
 
 DEAL_II_NAMESPACE_OPEN
 
index 74944558a0657250df0b386ff333a4c98a1e6ba0..42030ada21f8f424362c3b10f15eb849c35016c5 100644 (file)
@@ -14,7 +14,7 @@
 // ---------------------------------------------------------------------
 
 
-#include <deal.II/numerics/vector_tools.templates.h>
+#include <deal.II/numerics/vector_tools_rhs.templates.h>
 
 DEAL_II_NAMESPACE_OPEN
 

In the beginning the Universe was created. This has made a lot of people very angry and has been widely regarded as a bad move.

Douglas Adams


Typeset in Trocchi and Trocchi Bold Sans Serif.