/* ---------------------------------------------------------------------
 *
 * Copyright (C) 2002 - 2021 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.
 *
 * ---------------------------------------------------------------------

 *
 * Author: Wolfgang Bangerth, ETH Zurich, 2002
 */


// Start out with well known things...
#include <deal.II/base/quadrature_lib.h>
#include <deal.II/base/function.h>
#include <deal.II/base/logstream.h>
#include <deal.II/base/thread_management.h>
#include <deal.II/base/work_stream.h>
#include <deal.II/lac/vector.h>
#include <deal.II/lac/full_matrix.h>
#include <deal.II/lac/sparse_matrix.h>
#include <deal.II/lac/dynamic_sparsity_pattern.h>
#include <deal.II/lac/solver_cg.h>
#include <deal.II/lac/precondition.h>
#include <deal.II/lac/affine_constraints.h>
#include <deal.II/grid/tria.h>
#include <deal.II/grid/grid_generator.h>
#include <deal.II/grid/grid_out.h>
#include <deal.II/grid/grid_refinement.h>
#include <deal.II/dofs/dof_handler.h>
#include <deal.II/dofs/dof_tools.h>
#include <deal.II/fe/fe_q.h>
#include <deal.II/fe/fe_values.h>
#include <deal.II/fe/fe_tools.h>
#include <deal.II/numerics/vector_tools.h>
#include <deal.II/numerics/matrix_tools.h>
#include <deal.II/numerics/data_out.h>
#include <deal.II/numerics/error_estimator.h>

#include <algorithm>
#include <fstream>
#include <iostream>
#include <list>
#include <memory>
#include <numeric>

// The last step is as in all previous programs:
namespace Step14
{
  using namespace dealii;

  // @sect3{Evaluating the solution}

  // As mentioned in the introduction, significant parts of the program have
  // simply been taken over from the step-13 example program. We therefore
  // only comment on those things that are new.
  //
  // First, the framework for evaluation of solutions is unchanged, i.e. the
  // base class is the same, and the class to evaluate the solution at a grid
  // point is unchanged:
  namespace Evaluation
  {
    // @sect4{The EvaluationBase class}
    template <int dim>
    class EvaluationBase
    {
    public:
      virtual ~EvaluationBase() = default;

      void set_refinement_cycle(const unsigned int refinement_cycle);

      virtual void operator()(const DoFHandler<dim> &dof_handler,
                              const Vector<double> & solution) const = 0;

    protected:
      unsigned int refinement_cycle;
    };



    template <int dim>
    void EvaluationBase<dim>::set_refinement_cycle(const unsigned int step)
    {
      refinement_cycle = step;
    }


    // @sect4{The PointValueEvaluation class}
    template <int dim>
    class PointValueEvaluation : public EvaluationBase<dim>
    {
    public:
      PointValueEvaluation(const Point<dim> &evaluation_point);

      virtual void operator()(const DoFHandler<dim> &dof_handler,
                              const Vector<double> & solution) const override;

      DeclException1(
        ExcEvaluationPointNotFound,
        Point<dim>,
        << "The evaluation point " << arg1
        << " was not found among the vertices of the present grid.");

    private:
      const Point<dim> evaluation_point;
    };


    template <int dim>
    PointValueEvaluation<dim>::PointValueEvaluation(
      const Point<dim> &evaluation_point)
      : evaluation_point(evaluation_point)
    {}



    template <int dim>
    void
    PointValueEvaluation<dim>::operator()(const DoFHandler<dim> &dof_handler,
                                          const Vector<double> & solution) const
    {
      double point_value = 1e20;

      bool evaluation_point_found = false;
      for (const auto &cell : dof_handler.active_cell_iterators())
        if (!evaluation_point_found)
          for (const auto vertex : cell->vertex_indices())
            if (cell->vertex(vertex).distance(evaluation_point) <
                cell->diameter() * 1e-8)
              {
                point_value = solution(cell->vertex_dof_index(vertex, 0));

                evaluation_point_found = true;
                break;
              }

      AssertThrow(evaluation_point_found,
                  ExcEvaluationPointNotFound(evaluation_point));

      std::cout << "   Point value=" << point_value << std::endl;
    }


    // @sect4{The PointXDerivativeEvaluation class}

    // Besides the class implementing the evaluation of the solution at one
    // point, we here provide one which evaluates the gradient at a grid
    // point. Since in general the gradient of a finite element function is
    // not continuous at a vertex, we have to be a little bit more careful
    // here. What we do is to loop over all cells, even if we have found the
    // point already on one cell, and use the mean value of the gradient at
    // the vertex taken from all adjacent cells.
    //
    // Given the interface of the <code>PointValueEvaluation</code> class, the
    // declaration of this class provides little surprise, and neither does
    // the constructor:
    template <int dim>
    class PointXDerivativeEvaluation : public EvaluationBase<dim>
    {
    public:
      PointXDerivativeEvaluation(const Point<dim> &evaluation_point);

      virtual void operator()(const DoFHandler<dim> &dof_handler,
                              const Vector<double> & solution) const;

      DeclException1(
        ExcEvaluationPointNotFound,
        Point<dim>,
        << "The evaluation point " << arg1
        << " was not found among the vertices of the present grid.");

    private:
      const Point<dim> evaluation_point;
    };


    template <int dim>
    PointXDerivativeEvaluation<dim>::PointXDerivativeEvaluation(
      const Point<dim> &evaluation_point)
      : evaluation_point(evaluation_point)
    {}


    // The more interesting things happen inside the function doing the actual
    // evaluation:
    template <int dim>
    void PointXDerivativeEvaluation<dim>::operator()(
      const DoFHandler<dim> &dof_handler,
      const Vector<double> & solution) const
    {
      // This time initialize the return value with something useful, since we
      // will have to add up a number of contributions and take the mean value
      // afterwards...
      double point_derivative = 0;

      // ...then have some objects of which the meaning will become clear
      // below...
      QTrapezoid<dim>             vertex_quadrature;
      FEValues<dim>               fe_values(dof_handler.get_fe(),
                              vertex_quadrature,
                              update_gradients | update_quadrature_points);
      std::vector<Tensor<1, dim>> solution_gradients(vertex_quadrature.size());

      // ...and next loop over all cells and their vertices, and count how
      // often the vertex has been found:
      unsigned int evaluation_point_hits = 0;
      for (const auto &cell : dof_handler.active_cell_iterators())
        for (const auto vertex : cell->vertex_indices())
          if (cell->vertex(vertex) == evaluation_point)
            {
              // Things are now no more as simple, since we can't get the
              // gradient of the finite element field as before, where we
              // simply had to pick one degree of freedom at a vertex.
              //
              // Rather, we have to evaluate the finite element field on this
              // cell, and at a certain point. As you know, evaluating finite
              // element fields at certain points is done through the
              // <code>FEValues</code> class, so we use that. The question is:
              // the <code>FEValues</code> object needs to be a given a
              // quadrature formula and can then compute the values of finite
              // element quantities at the quadrature points. Here, we don't
              // want to do quadrature, we simply want to specify some points!
              //
              // Nevertheless, the same way is chosen: use a special
              // quadrature rule with points at the vertices, since these are
              // what we are interested in. The appropriate rule is the
              // trapezoidal rule, so that is the reason why we used that one
              // above.
              //
              // Thus: initialize the <code>FEValues</code> object on this
              // cell,
              fe_values.reinit(cell);
              // and extract the gradients of the solution vector at the
              // vertices:
              fe_values.get_function_gradients(solution, solution_gradients);

              // Now we have the gradients at all vertices, so pick out that
              // one which belongs to the evaluation point (note that the
              // order of vertices is not necessarily the same as that of the
              // quadrature points):
              unsigned int q_point = 0;
              for (; q_point < solution_gradients.size(); ++q_point)
                if (fe_values.quadrature_point(q_point) == evaluation_point)
                  break;

              // Check that the evaluation point was indeed found,
              Assert(q_point < solution_gradients.size(), ExcInternalError());
              // and if so take the x-derivative of the gradient there as the
              // value which we are interested in, and increase the counter
              // indicating how often we have added to that variable:
              point_derivative += solution_gradients[q_point][0];
              ++evaluation_point_hits;

              // Finally break out of the innermost loop iterating over the
              // vertices of the present cell, since if we have found the
              // evaluation point at one vertex it cannot be at a following
              // vertex as well:
              break;
            }

      // Now we have looped over all cells and vertices, so check whether the
      // point was found:
      AssertThrow(evaluation_point_hits > 0,
                  ExcEvaluationPointNotFound(evaluation_point));

      // We have simply summed up the contributions of all adjacent cells, so
      // we still have to compute the mean value. Once this is done, report
      // the status:
      point_derivative /= evaluation_point_hits;
      std::cout << "   Point x-derivative=" << point_derivative << std::endl;
    }



    // @sect4{The GridOutput class}

    // Since this program has a more difficult structure (it computed a dual
    // solution in addition to a primal one), writing out the solution is no
    // more done by an evaluation object since we want to write both solutions
    // at once into one file, and that requires some more information than
    // available to the evaluation classes.
    //
    // However, we also want to look at the grids generated. This again can be
    // done with one such class. Its structure is analog to the
    // <code>SolutionOutput</code> class of the previous example program, so
    // we do not discuss it here in more detail. Furthermore, everything that
    // is used here has already been used in previous example programs.
    template <int dim>
    class GridOutput : public EvaluationBase<dim>
    {
    public:
      GridOutput(const std::string &output_name_base);

      virtual void operator()(const DoFHandler<dim> &dof_handler,
                              const Vector<double> & solution) const override;

    private:
      const std::string output_name_base;
    };


    template <int dim>
    GridOutput<dim>::GridOutput(const std::string &output_name_base)
      : output_name_base(output_name_base)
    {}


    template <int dim>
    void GridOutput<dim>::operator()(const DoFHandler<dim> &dof_handler,
                                     const Vector<double> & /*solution*/) const
    {
      std::ofstream out(output_name_base + "-" +
                        std::to_string(this->refinement_cycle) + ".svg");
      GridOut().write_svg(dof_handler.get_triangulation(), out);
    }
  } // namespace Evaluation


  // @sect3{The Laplace solver classes}

  // Next are the actual solver classes. Again, we discuss only the
  // differences to the previous program.
  namespace LaplaceSolver
  {
    // @sect4{The Laplace solver base class}

    // This class is almost unchanged, with the exception that it declares two
    // more functions: <code>output_solution</code> will be used to generate
    // output files from the actual solutions computed by derived classes, and
    // the <code>set_refinement_cycle</code> function by which the testing
    // framework sets the number of the refinement cycle to a local variable
    // in this class; this number is later used to generate filenames for the
    // solution output.
    template <int dim>
    class Base
    {
    public:
      Base(Triangulation<dim> &coarse_grid);
      virtual ~Base() = default;

      virtual void solve_problem() = 0;
      virtual void postprocess(
        const Evaluation::EvaluationBase<dim> &postprocessor) const = 0;
      virtual void         refine_grid()                            = 0;
      virtual unsigned int n_dofs() const                           = 0;

      virtual void set_refinement_cycle(const unsigned int cycle);

      virtual void output_solution() const = 0;

    protected:
      const SmartPointer<Triangulation<dim>> triangulation;

      unsigned int refinement_cycle;
    };


    template <int dim>
    Base<dim>::Base(Triangulation<dim> &coarse_grid)
      : triangulation(&coarse_grid)
      , refinement_cycle(numbers::invalid_unsigned_int)
    {}



    template <int dim>
    void Base<dim>::set_refinement_cycle(const unsigned int cycle)
    {
      refinement_cycle = cycle;
    }


    // @sect4{The Laplace Solver class}

    // Likewise, the <code>Solver</code> class is entirely unchanged and will
    // thus not be discussed.
    template <int dim>
    class Solver : public virtual Base<dim>
    {
    public:
      Solver(Triangulation<dim> &       triangulation,
             const FiniteElement<dim> & fe,
             const Quadrature<dim> &    quadrature,
             const Quadrature<dim - 1> &face_quadrature,
             const Function<dim> &      boundary_values);
      virtual ~Solver() override;

      virtual void solve_problem() override;

      virtual void postprocess(
        const Evaluation::EvaluationBase<dim> &postprocessor) const override;

      virtual unsigned int n_dofs() const override;

    protected:
      const SmartPointer<const FiniteElement<dim>>  fe;
      const SmartPointer<const Quadrature<dim>>     quadrature;
      const SmartPointer<const Quadrature<dim - 1>> face_quadrature;
      DoFHandler<dim>                               dof_handler;
      Vector<double>                                solution;
      const SmartPointer<const Function<dim>>       boundary_values;

      virtual void assemble_rhs(Vector<double> &rhs) const = 0;

    private:
      struct LinearSystem
      {
        LinearSystem(const DoFHandler<dim> &dof_handler);

        void solve(Vector<double> &solution) const;

        AffineConstraints<double> hanging_node_constraints;
        SparsityPattern           sparsity_pattern;
        SparseMatrix<double>      matrix;
        Vector<double>            rhs;
      };


      // The remainder of the class is essentially a copy of step-13
      // as well, including the data structures and functions
      // necessary to compute the linear system in parallel using the
      // WorkStream framework:
      struct AssemblyScratchData
      {
        AssemblyScratchData(const FiniteElement<dim> &fe,
                            const Quadrature<dim> &   quadrature);
        AssemblyScratchData(const AssemblyScratchData &scratch_data);

        FEValues<dim> fe_values;
      };

      struct AssemblyCopyData
      {
        FullMatrix<double>                   cell_matrix;
        std::vector<types::global_dof_index> local_dof_indices;
      };


      void assemble_linear_system(LinearSystem &linear_system);

      void local_assemble_matrix(
        const typename DoFHandler<dim>::active_cell_iterator &cell,
        AssemblyScratchData &                                 scratch_data,
        AssemblyCopyData &                                    copy_data) const;


      void copy_local_to_global(const AssemblyCopyData &copy_data,
                                LinearSystem &          linear_system) const;
    };



    template <int dim>
    Solver<dim>::Solver(Triangulation<dim> &       triangulation,
                        const FiniteElement<dim> & fe,
                        const Quadrature<dim> &    quadrature,
                        const Quadrature<dim - 1> &face_quadrature,
                        const Function<dim> &      boundary_values)
      : Base<dim>(triangulation)
      , fe(&fe)
      , quadrature(&quadrature)
      , face_quadrature(&face_quadrature)
      , dof_handler(triangulation)
      , boundary_values(&boundary_values)
    {}


    template <int dim>
    Solver<dim>::~Solver()
    {
      dof_handler.clear();
    }


    template <int dim>
    void Solver<dim>::solve_problem()
    {
      dof_handler.distribute_dofs(*fe);
      solution.reinit(dof_handler.n_dofs());

      LinearSystem linear_system(dof_handler);
      assemble_linear_system(linear_system);
      linear_system.solve(solution);
    }


    template <int dim>
    void Solver<dim>::postprocess(
      const Evaluation::EvaluationBase<dim> &postprocessor) const
    {
      postprocessor(dof_handler, solution);
    }


    template <int dim>
    unsigned int Solver<dim>::n_dofs() const
    {
      return dof_handler.n_dofs();
    }


    // The following few functions and constructors are verbatim
    // copies taken from step-13:
    template <int dim>
    void Solver<dim>::assemble_linear_system(LinearSystem &linear_system)
    {
      Threads::Task<void> rhs_task =
        Threads::new_task(&Solver<dim>::assemble_rhs, *this, linear_system.rhs);

      auto worker =
        [this](const typename DoFHandler<dim>::active_cell_iterator &cell,
               AssemblyScratchData &scratch_data,
               AssemblyCopyData &   copy_data) {
          this->local_assemble_matrix(cell, scratch_data, copy_data);
        };

      auto copier = [this, &linear_system](const AssemblyCopyData &copy_data) {
        this->copy_local_to_global(copy_data, linear_system);
      };

      WorkStream::run(dof_handler.begin_active(),
                      dof_handler.end(),
                      worker,
                      copier,
                      AssemblyScratchData(*fe, *quadrature),
                      AssemblyCopyData());
      linear_system.hanging_node_constraints.condense(linear_system.matrix);

      std::map<types::global_dof_index, double> boundary_value_map;
      VectorTools::interpolate_boundary_values(dof_handler,
                                               0,
                                               *boundary_values,
                                               boundary_value_map);

      rhs_task.join();
      linear_system.hanging_node_constraints.condense(linear_system.rhs);

      MatrixTools::apply_boundary_values(boundary_value_map,
                                         linear_system.matrix,
                                         solution,
                                         linear_system.rhs);
    }


    template <int dim>
    Solver<dim>::AssemblyScratchData::AssemblyScratchData(
      const FiniteElement<dim> &fe,
      const Quadrature<dim> &   quadrature)
      : fe_values(fe, quadrature, update_gradients | update_JxW_values)
    {}


    template <int dim>
    Solver<dim>::AssemblyScratchData::AssemblyScratchData(
      const AssemblyScratchData &scratch_data)
      : fe_values(scratch_data.fe_values.get_fe(),
                  scratch_data.fe_values.get_quadrature(),
                  update_gradients | update_JxW_values)
    {}


    template <int dim>
    void Solver<dim>::local_assemble_matrix(
      const typename DoFHandler<dim>::active_cell_iterator &cell,
      AssemblyScratchData &                                 scratch_data,
      AssemblyCopyData &                                    copy_data) const
    {
      const unsigned int dofs_per_cell = fe->n_dofs_per_cell();
      const unsigned int n_q_points    = quadrature->size();

      copy_data.cell_matrix.reinit(dofs_per_cell, dofs_per_cell);

      copy_data.local_dof_indices.resize(dofs_per_cell);

      scratch_data.fe_values.reinit(cell);

      for (unsigned int q_point = 0; q_point < n_q_points; ++q_point)
        for (unsigned int i = 0; i < dofs_per_cell; ++i)
          for (unsigned int j = 0; j < dofs_per_cell; ++j)
            copy_data.cell_matrix(i, j) +=
              (scratch_data.fe_values.shape_grad(i, q_point) *
               scratch_data.fe_values.shape_grad(j, q_point) *
               scratch_data.fe_values.JxW(q_point));

      cell->get_dof_indices(copy_data.local_dof_indices);
    }



    template <int dim>
    void Solver<dim>::copy_local_to_global(const AssemblyCopyData &copy_data,
                                           LinearSystem &linear_system) const
    {
      for (unsigned int i = 0; i < copy_data.local_dof_indices.size(); ++i)
        for (unsigned int j = 0; j < copy_data.local_dof_indices.size(); ++j)
          linear_system.matrix.add(copy_data.local_dof_indices[i],
                                   copy_data.local_dof_indices[j],
                                   copy_data.cell_matrix(i, j));
    }


    // Now for the functions that implement actions in the linear
    // system class. First, the constructor initializes all data
    // elements to their correct sizes, and sets up a number of
    // additional data structures, such as constraints due to hanging
    // nodes. Since setting up the hanging nodes and finding out about
    // the nonzero elements of the matrix is independent, we do that
    // in parallel (if the library was configured to use concurrency,
    // at least; otherwise, the actions are performed
    // sequentially). Note that we start only one thread, and do the
    // second action in the main thread. Since only one thread is
    // generated, we don't use the <code>Threads::TaskGroup</code>
    // class here, but rather use the one created task object
    // directly to wait for this particular task's exit. The
    // approach is generally the same as the one we have used in
    // <code>Solver::assemble_linear_system()</code> above.
    //
    // Note that taking the address of the
    // <code>DoFTools::make_hanging_node_constraints</code> function
    // is a little tricky, since there are actually three functions of
    // this name, one for each supported space dimension. Taking
    // addresses of overloaded functions is somewhat complicated in
    // C++, since the address-of operator <code>&</code> in that case
    // returns a set of values (the addresses of all
    // functions with that name), and selecting the right one is then
    // the next step. If the context dictates which one to take (for
    // example by assigning to a function pointer of known type), then
    // the compiler can do that by itself, but if this set of pointers
    // shall be given as the argument to a function that takes a
    // template, the compiler could choose all without having a
    // preference for one. We therefore have to make it clear to the
    // compiler which one we would like to have; for this, we could
    // use a cast, but for more clarity, we assign it to a temporary
    // <code>mhnc_p</code> (short for <code>pointer to
    // make_hanging_node_constraints</code>) with the right type, and
    // using this pointer instead.
    template <int dim>
    Solver<dim>::LinearSystem::LinearSystem(const DoFHandler<dim> &dof_handler)
    {
      hanging_node_constraints.clear();

      void (*mhnc_p)(const DoFHandler<dim> &, AffineConstraints<double> &) =
        &DoFTools::make_hanging_node_constraints;

      // Start a side task then continue on the main thread
      Threads::Task<void> side_task =
        Threads::new_task(mhnc_p, dof_handler, hanging_node_constraints);

      DynamicSparsityPattern dsp(dof_handler.n_dofs(), dof_handler.n_dofs());
      DoFTools::make_sparsity_pattern(dof_handler, dsp);



      // Wait for the side task to be done before going further
      side_task.join();

      hanging_node_constraints.close();
      hanging_node_constraints.condense(dsp);
      sparsity_pattern.copy_from(dsp);

      matrix.reinit(sparsity_pattern);
      rhs.reinit(dof_handler.n_dofs());
    }



    template <int dim>
    void Solver<dim>::LinearSystem::solve(Vector<double> &solution) const
    {
      SolverControl            solver_control(5000, 1e-12);
      SolverCG<Vector<double>> cg(solver_control);

      PreconditionSSOR<SparseMatrix<double>> preconditioner;
      preconditioner.initialize(matrix, 1.2);

      cg.solve(matrix, solution, rhs, preconditioner);

      hanging_node_constraints.distribute(solution);
    }



    // @sect4{The PrimalSolver class}

    // The <code>PrimalSolver</code> class is also mostly unchanged except for
    // implementing the <code>output_solution</code> function. We keep the
    // <code>GlobalRefinement</code> and <code>RefinementKelly</code> classes
    // in this program, and they can then rely on the default implementation
    // of this function which simply outputs the primal solution. The class
    // implementing dual weighted error estimators will overload this function
    // itself, to also output the dual solution.
    template <int dim>
    class PrimalSolver : public Solver<dim>
    {
    public:
      PrimalSolver(Triangulation<dim> &       triangulation,
                   const FiniteElement<dim> & fe,
                   const Quadrature<dim> &    quadrature,
                   const Quadrature<dim - 1> &face_quadrature,
                   const Function<dim> &      rhs_function,
                   const Function<dim> &      boundary_values);

      virtual void output_solution() const override;

    protected:
      const SmartPointer<const Function<dim>> rhs_function;
      virtual void assemble_rhs(Vector<double> &rhs) const override;
    };


    template <int dim>
    PrimalSolver<dim>::PrimalSolver(Triangulation<dim> &       triangulation,
                                    const FiniteElement<dim> & fe,
                                    const Quadrature<dim> &    quadrature,
                                    const Quadrature<dim - 1> &face_quadrature,
                                    const Function<dim> &      rhs_function,
                                    const Function<dim> &      boundary_values)
      : Base<dim>(triangulation)
      , Solver<dim>(triangulation,
                    fe,
                    quadrature,
                    face_quadrature,
                    boundary_values)
      , rhs_function(&rhs_function)
    {}



    template <int dim>
    void PrimalSolver<dim>::output_solution() const
    {
      DataOut<dim> data_out;
      data_out.attach_dof_handler(this->dof_handler);
      data_out.add_data_vector(this->solution, "solution");
      data_out.build_patches();

      std::ofstream out("solution-" + std::to_string(this->refinement_cycle) +
                        ".vtu");
      data_out.write(out, DataOutBase::vtu);
    }



    template <int dim>
    void PrimalSolver<dim>::assemble_rhs(Vector<double> &rhs) const
    {
      FEValues<dim> fe_values(*this->fe,
                              *this->quadrature,
                              update_values | update_quadrature_points |
                                update_JxW_values);

      const unsigned int dofs_per_cell = this->fe->n_dofs_per_cell();
      const unsigned int n_q_points    = this->quadrature->size();

      Vector<double>                       cell_rhs(dofs_per_cell);
      std::vector<double>                  rhs_values(n_q_points);
      std::vector<types::global_dof_index> local_dof_indices(dofs_per_cell);

      for (const auto &cell : this->dof_handler.active_cell_iterators())
        {
          cell_rhs = 0;

          fe_values.reinit(cell);

          rhs_function->value_list(fe_values.get_quadrature_points(),
                                   rhs_values);

          for (unsigned int q_point = 0; q_point < n_q_points; ++q_point)
            for (unsigned int i = 0; i < dofs_per_cell; ++i)
              cell_rhs(i) += (fe_values.shape_value(i, q_point) * // phi_i(x_q)
                              rhs_values[q_point] *               // f((x_q)
                              fe_values.JxW(q_point));            // dx

          cell->get_dof_indices(local_dof_indices);
          for (unsigned int i = 0; i < dofs_per_cell; ++i)
            rhs(local_dof_indices[i]) += cell_rhs(i);
        }
    }


    // @sect4{The RefinementGlobal and RefinementKelly classes}

    // For the following two classes, the same applies as for most of the
    // above: the class is taken from the previous example as-is:
    template <int dim>
    class RefinementGlobal : public PrimalSolver<dim>
    {
    public:
      RefinementGlobal(Triangulation<dim> &       coarse_grid,
                       const FiniteElement<dim> & fe,
                       const Quadrature<dim> &    quadrature,
                       const Quadrature<dim - 1> &face_quadrature,
                       const Function<dim> &      rhs_function,
                       const Function<dim> &      boundary_values);

      virtual void refine_grid() override;
    };



    template <int dim>
    RefinementGlobal<dim>::RefinementGlobal(
      Triangulation<dim> &       coarse_grid,
      const FiniteElement<dim> & fe,
      const Quadrature<dim> &    quadrature,
      const Quadrature<dim - 1> &face_quadrature,
      const Function<dim> &      rhs_function,
      const Function<dim> &      boundary_values)
      : Base<dim>(coarse_grid)
      , PrimalSolver<dim>(coarse_grid,
                          fe,
                          quadrature,
                          face_quadrature,
                          rhs_function,
                          boundary_values)
    {}



    template <int dim>
    void RefinementGlobal<dim>::refine_grid()
    {
      this->triangulation->refine_global(1);
    }



    template <int dim>
    class RefinementKelly : public PrimalSolver<dim>
    {
    public:
      RefinementKelly(Triangulation<dim> &       coarse_grid,
                      const FiniteElement<dim> & fe,
                      const Quadrature<dim> &    quadrature,
                      const Quadrature<dim - 1> &face_quadrature,
                      const Function<dim> &      rhs_function,
                      const Function<dim> &      boundary_values);

      virtual void refine_grid() override;
    };



    template <int dim>
    RefinementKelly<dim>::RefinementKelly(
      Triangulation<dim> &       coarse_grid,
      const FiniteElement<dim> & fe,
      const Quadrature<dim> &    quadrature,
      const Quadrature<dim - 1> &face_quadrature,
      const Function<dim> &      rhs_function,
      const Function<dim> &      boundary_values)
      : Base<dim>(coarse_grid)
      , PrimalSolver<dim>(coarse_grid,
                          fe,
                          quadrature,
                          face_quadrature,
                          rhs_function,
                          boundary_values)
    {}



    template <int dim>
    void RefinementKelly<dim>::refine_grid()
    {
      Vector<float> estimated_error_per_cell(
        this->triangulation->n_active_cells());
      KellyErrorEstimator<dim>::estimate(
        this->dof_handler,
        QGauss<dim - 1>(this->fe->degree + 1),
        std::map<types::boundary_id, const Function<dim> *>(),
        this->solution,
        estimated_error_per_cell);
      GridRefinement::refine_and_coarsen_fixed_number(*this->triangulation,
                                                      estimated_error_per_cell,
                                                      0.3,
                                                      0.03);
      this->triangulation->execute_coarsening_and_refinement();
    }



    // @sect4{The RefinementWeightedKelly class}

    // This class is a variant of the previous one, in that it allows to
    // weight the refinement indicators we get from the library's Kelly
    // indicator by some function. We include this class since the goal of
    // this example program is to demonstrate automatic refinement criteria
    // even for complex output quantities such as point values or stresses. If
    // we did not solve a dual problem and compute the weights thereof, we
    // would probably be tempted to give a hand-crafted weighting to the
    // indicators to account for the fact that we are going to evaluate these
    // quantities. This class accepts such a weighting function as argument to
    // its constructor:
    template <int dim>
    class RefinementWeightedKelly : public PrimalSolver<dim>
    {
    public:
      RefinementWeightedKelly(Triangulation<dim> &       coarse_grid,
                              const FiniteElement<dim> & fe,
                              const Quadrature<dim> &    quadrature,
                              const Quadrature<dim - 1> &face_quadrature,
                              const Function<dim> &      rhs_function,
                              const Function<dim> &      boundary_values,
                              const Function<dim> &      weighting_function);

      virtual void refine_grid() override;

    private:
      const SmartPointer<const Function<dim>> weighting_function;
    };



    template <int dim>
    RefinementWeightedKelly<dim>::RefinementWeightedKelly(
      Triangulation<dim> &       coarse_grid,
      const FiniteElement<dim> & fe,
      const Quadrature<dim> &    quadrature,
      const Quadrature<dim - 1> &face_quadrature,
      const Function<dim> &      rhs_function,
      const Function<dim> &      boundary_values,
      const Function<dim> &      weighting_function)
      : Base<dim>(coarse_grid)
      , PrimalSolver<dim>(coarse_grid,
                          fe,
                          quadrature,
                          face_quadrature,
                          rhs_function,
                          boundary_values)
      , weighting_function(&weighting_function)
    {}



    // Now, here comes the main function, including the weighting:
    template <int dim>
    void RefinementWeightedKelly<dim>::refine_grid()
    {
      // First compute some residual based error indicators for all cells by a
      // method already implemented in the library. What exactly we compute
      // here is described in more detail in the documentation of that class.
      Vector<float> estimated_error_per_cell(
        this->triangulation->n_active_cells());
      std::map<types::boundary_id, const Function<dim> *> dummy_function_map;
      KellyErrorEstimator<dim>::estimate(this->dof_handler,
                                         *this->face_quadrature,
                                         dummy_function_map,
                                         this->solution,
                                         estimated_error_per_cell);

      // Next weigh each entry in the vector of indicators by the value of the
      // function given to the constructor, evaluated at the cell center. We
      // need to write the result into the vector entry that corresponds to the
      // current cell, which we can obtain by asking the cell what its index
      // among all active cells is using CellAccessor::active_cell_index(). (In
      // reality, this index is zero for the first cell we handle in the loop,
      // one for the second cell, etc., and we could as well just keep track of
      // this index using an integer counter; but using
      // CellAccessor::active_cell_index() makes this more explicit.)
      for (const auto &cell : this->dof_handler.active_cell_iterators())
        estimated_error_per_cell(cell->active_cell_index()) *=
          weighting_function->value(cell->center());

      GridRefinement::refine_and_coarsen_fixed_number(*this->triangulation,
                                                      estimated_error_per_cell,
                                                      0.3,
                                                      0.03);
      this->triangulation->execute_coarsening_and_refinement();
    }

  } // namespace LaplaceSolver


  // @sect3{Equation data}
  //
  // In this example program, we work with the same data sets as in the
  // previous one, but as it may so happen that someone wants to run the
  // program with different boundary values and right hand side functions, or
  // on a different grid, we show a simple technique to do exactly that. For
  // more clarity, we furthermore pack everything that has to do with equation
  // data into a namespace of its own.
  //
  // The underlying assumption is that this is a research program, and that
  // there we often have a number of test cases that consist of a domain, a
  // right hand side, boundary values, possibly a specified coefficient, and a
  // number of other parameters. They often vary all at the same time when
  // shifting from one example to another. To make handling such sets of
  // problem description parameters simple is the goal of the following.
  //
  // Basically, the idea is this: let us have a structure for each set of
  // data, in which we pack everything that describes a test case: here, these
  // are two subclasses, one called <code>BoundaryValues</code> for the
  // boundary values of the exact solution, and one called
  // <code>RightHandSide</code>, and then a way to generate the coarse
  // grid. Since the solution of the previous example program looked like
  // curved ridges, we use this name here for the enclosing class. Note that
  // the names of the two inner classes have to be the same for all enclosing
  // test case classes, and also that we have attached the dimension template
  // argument to the enclosing class rather than to the inner ones, to make
  // further processing simpler.  (From a language viewpoint, a namespace
  // would be better to encapsulate these inner classes, rather than a
  // structure. However, namespaces cannot be given as template arguments, so
  // we use a structure to allow a second object to select from within its
  // given argument. The enclosing structure, of course, has no member
  // variables apart from the classes it declares, and a static function to
  // generate the coarse mesh; it will in general never be instantiated.)
  //
  // The idea is then the following (this is the right time to also take a
  // brief look at the code below): we can generate objects for boundary
  // values and right hand side by simply giving the name of the outer class
  // as a template argument to a class which we call here
  // <code>Data::SetUp</code>, and it then creates objects for the inner
  // classes. In this case, to get all that characterizes the curved ridge
  // solution, we would simply generate an instance of
  // <code>Data::SetUp@<Data::CurvedRidge@></code>, and everything we need to
  // know about the solution would be static member variables and functions of
  // that object.
  //
  // This approach might seem like overkill in this case, but will become very
  // handy once a certain set up is not only characterized by Dirichlet
  // boundary values and a right hand side function, but in addition by
  // material properties, Neumann values, different boundary descriptors,
  // etc. In that case, the <code>SetUp</code> class might consist of a dozen
  // or more objects, and each descriptor class (like the
  // <code>CurvedRidges</code> class below) would have to provide them. Then,
  // you will be happy to be able to change from one set of data to another by
  // only changing the template argument to the <code>SetUp</code> class at
  // one place, rather than at many.
  //
  // With this framework for different test cases, we are almost finished, but
  // one thing remains: by now we can select statically, by changing one
  // template argument, which data set to choose. In order to be able to do
  // that dynamically, i.e. at run time, we need a base class. This we provide
  // in the obvious way, see below, with virtual abstract functions. It forces
  // us to introduce a second template parameter <code>dim</code> which we
  // need for the base class (which could be avoided using some template
  // magic, but we omit that), but that's all.
  //
  // Adding new testcases is now simple, you don't have to touch the framework
  // classes, only a structure like the <code>CurvedRidges</code> one is
  // needed.
  namespace Data
  {
    // @sect4{The SetUpBase and SetUp classes}

    // Based on the above description, the <code>SetUpBase</code> class then
    // looks as follows. To allow using the <code>SmartPointer</code> class
    // with this class, we derived from the <code>Subscriptor</code> class.
    template <int dim>
    struct SetUpBase : public Subscriptor
    {
      virtual const Function<dim> &get_boundary_values() const = 0;

      virtual const Function<dim> &get_right_hand_side() const = 0;

      virtual void
      create_coarse_grid(Triangulation<dim> &coarse_grid) const = 0;
    };


    // And now for the derived class that takes the template argument as
    // explained above.
    //
    // Here we pack the data elements into private variables, and allow access
    // to them through the methods of the base class.
    template <class Traits, int dim>
    struct SetUp : public SetUpBase<dim>
    {
      virtual const Function<dim> &get_boundary_values() const override;

      virtual const Function<dim> &get_right_hand_side() const override;


      virtual void
      create_coarse_grid(Triangulation<dim> &coarse_grid) const override;

    private:
      static const typename Traits::BoundaryValues boundary_values;
      static const typename Traits::RightHandSide  right_hand_side;
    };

    // We have to provide definitions for the static member variables of the
    // above class:
    template <class Traits, int dim>
    const typename Traits::BoundaryValues SetUp<Traits, dim>::boundary_values;
    template <class Traits, int dim>
    const typename Traits::RightHandSide SetUp<Traits, dim>::right_hand_side;

    // And definitions of the member functions:
    template <class Traits, int dim>
    const Function<dim> &SetUp<Traits, dim>::get_boundary_values() const
    {
      return boundary_values;
    }


    template <class Traits, int dim>
    const Function<dim> &SetUp<Traits, dim>::get_right_hand_side() const
    {
      return right_hand_side;
    }


    template <class Traits, int dim>
    void SetUp<Traits, dim>::create_coarse_grid(
      Triangulation<dim> &coarse_grid) const
    {
      Traits::create_coarse_grid(coarse_grid);
    }


    // @sect4{The CurvedRidges class}

    // The class that is used to describe the boundary values and right hand
    // side of the <code>curved ridge</code> problem already used in the
    // step-13 example program is then like so:
    template <int dim>
    struct CurvedRidges
    {
      class BoundaryValues : public Function<dim>
      {
      public:
        virtual double value(const Point<dim> & p,
                             const unsigned int component) const;
      };


      class RightHandSide : public Function<dim>
      {
      public:
        virtual double value(const Point<dim> & p,
                             const unsigned int component) const;
      };

      static void create_coarse_grid(Triangulation<dim> &coarse_grid);
    };


    template <int dim>
    double CurvedRidges<dim>::BoundaryValues::value(
      const Point<dim> &p,
      const unsigned int /*component*/) const
    {
      double q = p(0);
      for (unsigned int i = 1; i < dim; ++i)
        q += std::sin(10 * p(i) + 5 * p(0) * p(0));
      const double exponential = std::exp(q);
      return exponential;
    }



    template <int dim>
    double CurvedRidges<dim>::RightHandSide::value(
      const Point<dim> &p,
      const unsigned int /*component*/) const
    {
      double q = p(0);
      for (unsigned int i = 1; i < dim; ++i)
        q += std::sin(10 * p(i) + 5 * p(0) * p(0));
      const double u  = std::exp(q);
      double       t1 = 1, t2 = 0, t3 = 0;
      for (unsigned int i = 1; i < dim; ++i)
        {
          t1 += std::cos(10 * p(i) + 5 * p(0) * p(0)) * 10 * p(0);
          t2 += 10 * std::cos(10 * p(i) + 5 * p(0) * p(0)) -
                100 * std::sin(10 * p(i) + 5 * p(0) * p(0)) * p(0) * p(0);
          t3 += 100 * std::cos(10 * p(i) + 5 * p(0) * p(0)) *
                  std::cos(10 * p(i) + 5 * p(0) * p(0)) -
                100 * std::sin(10 * p(i) + 5 * p(0) * p(0));
        }
      t1 = t1 * t1;

      return -u * (t1 + t2 + t3);
    }


    template <int dim>
    void CurvedRidges<dim>::create_coarse_grid(Triangulation<dim> &coarse_grid)
    {
      GridGenerator::hyper_cube(coarse_grid, -1, 1);
      coarse_grid.refine_global(2);
    }


    // @sect4{The Exercise_2_3 class}

    // This example program was written while giving practical courses for a
    // lecture on adaptive finite element methods and duality based error
    // estimates. For these courses, we had one exercise, which required to
    // solve the Laplace equation with constant right hand side on a square
    // domain with a square hole in the center, and zero boundary
    // values. Since the implementation of the properties of this problem is
    // so particularly simple here, lets do it. As the number of the exercise
    // was 2.3, we take the liberty to retain this name for the class as well.
    template <int dim>
    struct Exercise_2_3
    {
      // We need a class to denote the boundary values of the problem. In this
      // case, this is simple: it's the zero function, so don't even declare a
      // class, just an alias:
      using BoundaryValues = Functions::ZeroFunction<dim>;

      // Second, a class that denotes the right hand side. Since they are
      // constant, just subclass the corresponding class of the library and be
      // done:
      class RightHandSide : public Functions::ConstantFunction<dim>
      {
      public:
        RightHandSide()
          : Functions::ConstantFunction<dim>(1.)
        {}
      };

      // Finally a function to generate the coarse grid. This is somewhat more
      // complicated here, see immediately below.
      static void create_coarse_grid(Triangulation<dim> &coarse_grid);
    };


    // As stated above, the grid for this example is the square [-1,1]^2 with
    // the square [-1/2,1/2]^2 as hole in it. We create the coarse grid as 4
    // times 4 cells with the middle four ones missing. To understand how
    // exactly the mesh is going to look, it may be simplest to just look
    // at the "Results" section of this tutorial program first. In general,
    // if you'd like to understand more about creating meshes either from
    // scratch by hand, as we do here, or using other techniques, you
    // should take a look at step-49.
    //
    // Of course, the example has an extension to 3d, but since this function
    // cannot be written in a dimension independent way we choose not to
    // implement this here, but rather only specialize the template for
    // dim=2. If you compile the program for 3d, you'll get a message from the
    // linker that this function is not implemented for 3d, and needs to be
    // provided.
    //
    // For the creation of this geometry, the library has no predefined
    // method. In this case, the geometry is still simple enough to do the
    // creation by hand, rather than using a mesh generator.
    template <>
    void Exercise_2_3<2>::create_coarse_grid(Triangulation<2> &coarse_grid)
    {
      // We first define the space dimension, to allow those parts of the
      // function that are actually dimension independent to use this
      // variable. That makes it simpler if you later take this as a starting
      // point to implement a 3d version of this mesh. The next step is then
      // to have a list of vertices. Here, they are 24 (5 times 5, with the
      // middle one omitted). It is probably best to draw a sketch here.
      const unsigned int dim = 2;

      const std::vector<Point<2>> vertices = {
        {-1.0, -1.0}, {-0.5, -1.0}, {+0.0, -1.0}, {+0.5, -1.0}, {+1.0, -1.0}, //
        {-1.0, -0.5}, {-0.5, -0.5}, {+0.0, -0.5}, {+0.5, -0.5}, {+1.0, -0.5}, //
        {-1.0, +0.0}, {-0.5, +0.0}, {+0.5, +0.0}, {+1.0, +0.0},               //
        {-1.0, +0.5}, {-0.5, +0.5}, {+0.0, +0.5}, {+0.5, +0.5}, {+1.0, +0.5}, //
        {-1.0, +1.0}, {-0.5, +1.0}, {+0.0, +1.0}, {+0.5, +1.0}, {+1.0, +1.0}};

      // Next, we have to define the cells and the vertices they contain.
      const std::vector<std::array<int, GeometryInfo<dim>::vertices_per_cell>>
        cell_vertices = {{{0, 1, 5, 6}},
                         {{1, 2, 6, 7}},
                         {{2, 3, 7, 8}},
                         {{3, 4, 8, 9}},
                         {{5, 6, 10, 11}},
                         {{8, 9, 12, 13}},
                         {{10, 11, 14, 15}},
                         {{12, 13, 17, 18}},
                         {{14, 15, 19, 20}},
                         {{15, 16, 20, 21}},
                         {{16, 17, 21, 22}},
                         {{17, 18, 22, 23}}};

      const unsigned int n_cells = cell_vertices.size();

      // Again, we generate a C++ vector type from this, but this time by
      // looping over the cells (yes, this is boring). Additionally, we set
      // the material indicator to zero for all the cells:
      std::vector<CellData<dim>> cells(n_cells, CellData<dim>());
      for (unsigned int i = 0; i < n_cells; ++i)
        {
          for (unsigned int j = 0; j < cell_vertices[i].size(); ++j)
            cells[i].vertices[j] = cell_vertices[i][j];
          cells[i].material_id = 0;
        }

      // Finally pass all this information to the library to generate a
      // triangulation. The last parameter may be used to pass information
      // about non-zero boundary indicators at certain faces of the
      // triangulation to the library, but we don't want that here, so we give
      // an empty object:
      coarse_grid.create_triangulation(vertices, cells, SubCellData());

      // And since we want that the evaluation point (3/4,3/4) in this example
      // is a grid point, we refine once globally:
      coarse_grid.refine_global(1);
    }
  } // namespace Data

  // @sect4{Discussion}
  //
  // As you have now read through this framework, you may be wondering why we
  // have not chosen to implement the classes implementing a certain setup
  // (like the <code>CurvedRidges</code> class) directly as classes derived
  // from <code>Data::SetUpBase</code>. Indeed, we could have done very well
  // so. The only reason is that then we would have to have member variables
  // for the solution and right hand side classes in the
  // <code>CurvedRidges</code> class, as well as member functions overloading
  // the abstract functions of the base class giving access to these member
  // variables. The <code>SetUp</code> class has the sole reason to relieve us
  // from the need to reiterate these member variables and functions that
  // would be necessary in all such classes. In some way, the template
  // mechanism here only provides a way to have default implementations for a
  // number of functions that depend on external quantities and can thus not
  // be provided using normal virtual functions, at least not without the help
  // of templates.
  //
  // However, there might be good reasons to actually implement classes
  // derived from <code>Data::SetUpBase</code>, for example if the solution or
  // right hand side classes require constructors that take arguments, which
  // the <code>Data::SetUpBase</code> class cannot provide. In that case,
  // subclassing is a worthwhile strategy. Other possibilities for special
  // cases are to derive from <code>Data::SetUp@<SomeSetUp@></code> where
  // <code>SomeSetUp</code> denotes a class, or even to explicitly specialize
  // <code>Data::SetUp@<SomeSetUp@></code>. The latter allows to transparently
  // use the way the <code>SetUp</code> class is used for other set-ups, but
  // with special actions taken for special arguments.
  //
  // A final observation favoring the approach taken here is the following: we
  // have found numerous times that when starting a project, the number of
  // parameters (usually boundary values, right hand side, coarse grid, just
  // as here) was small, and the number of test cases was small as well. One
  // then starts out by handcoding them into a number of <code>switch</code>
  // statements. Over time, projects grow, and so does the number of test
  // cases. The number of <code>switch</code> statements grows with that, and
  // their length as well, and one starts to find ways to consider impossible
  // examples where domains, boundary values, and right hand sides do not fit
  // together any more, and starts losing the overview over the whole
  // structure. Encapsulating everything belonging to a certain test case into
  // a structure of its own has proven worthwhile for this, as it keeps
  // everything that belongs to one test case in one place. Furthermore, it
  // allows to put these things all in one or more files that are only devoted
  // to test cases and their data, without having to bring their actual
  // implementation into contact with the rest of the program.


  // @sect3{Dual functionals}

  // As with the other components of the program, we put everything we need to
  // describe dual functionals into a namespace of its own, and define an
  // abstract base class that provides the interface the class solving the
  // dual problem needs for its work.
  //
  // We will then implement two such classes, for the evaluation of a point
  // value and of the derivative of the solution at that point. For these
  // functionals we already have the corresponding evaluation objects, so they
  // are complementary.
  namespace DualFunctional
  {
    // @sect4{The DualFunctionalBase class}

    // First start with the base class for dual functionals. Since for linear
    // problems the characteristics of the dual problem play a role only in
    // the right hand side, we only need to provide for a function that
    // assembles the right hand side for a given discretization:
    template <int dim>
    class DualFunctionalBase : public Subscriptor
    {
    public:
      virtual void assemble_rhs(const DoFHandler<dim> &dof_handler,
                                Vector<double> &       rhs) const = 0;
    };


    // @sect4{The dual functional PointValueEvaluation class}

    // As a first application, we consider the functional corresponding to the
    // evaluation of the solution's value at a given point which again we
    // assume to be a vertex. Apart from the constructor that takes and stores
    // the evaluation point, this class consists only of the function that
    // implements assembling the right hand side.
    template <int dim>
    class PointValueEvaluation : public DualFunctionalBase<dim>
    {
    public:
      PointValueEvaluation(const Point<dim> &evaluation_point);

      virtual void assemble_rhs(const DoFHandler<dim> &dof_handler,
                                Vector<double> &       rhs) const override;

      DeclException1(
        ExcEvaluationPointNotFound,
        Point<dim>,
        << "The evaluation point " << arg1
        << " was not found among the vertices of the present grid.");

    protected:
      const Point<dim> evaluation_point;
    };


    template <int dim>
    PointValueEvaluation<dim>::PointValueEvaluation(
      const Point<dim> &evaluation_point)
      : evaluation_point(evaluation_point)
    {}


    // As for doing the main purpose of the class, assembling the right hand
    // side, let us first consider what is necessary: The right hand side of
    // the dual problem is a vector of values J(phi_i), where J is the error
    // functional, and phi_i is the i-th shape function. Here, J is the
    // evaluation at the point x0, i.e. J(phi_i)=phi_i(x0).
    //
    // Now, we have assumed that the evaluation point is a vertex. Thus, for
    // the usual finite elements we might be using in this program, we can
    // take for granted that at such a point exactly one shape function is
    // nonzero, and in particular has the value one. Thus, we set the right
    // hand side vector to all-zeros, then seek for the shape function
    // associated with that point and set the corresponding value of the right
    // hand side vector to one:
    template <int dim>
    void
    PointValueEvaluation<dim>::assemble_rhs(const DoFHandler<dim> &dof_handler,
                                            Vector<double> &       rhs) const
    {
      // So, first set everything to zeros...
      rhs.reinit(dof_handler.n_dofs());

      // ...then loop over cells and find the evaluation point among the
      // vertices (or very close to a vertex, which may happen due to floating
      // point round-off):
      for (const auto &cell : dof_handler.active_cell_iterators())
        for (const auto vertex : cell->vertex_indices())
          if (cell->vertex(vertex).distance(evaluation_point) <
              cell->diameter() * 1e-8)
            {
              // Ok, found, so set corresponding entry, and leave function
              // since we are finished:
              rhs(cell->vertex_dof_index(vertex, 0)) = 1;
              return;
            }

      // Finally, a sanity check: if we somehow got here, then we must have
      // missed the evaluation point, so raise an exception unconditionally:
      AssertThrow(false, ExcEvaluationPointNotFound(evaluation_point));
    }


    // @sect4{The dual functional PointXDerivativeEvaluation class}

    // As second application, we again consider the evaluation of the
    // x-derivative of the solution at one point. Again, the declaration of
    // the class, and the implementation of its constructor is not too
    // interesting:
    template <int dim>
    class PointXDerivativeEvaluation : public DualFunctionalBase<dim>
    {
    public:
      PointXDerivativeEvaluation(const Point<dim> &evaluation_point);

      virtual void assemble_rhs(const DoFHandler<dim> &dof_handler,
                                Vector<double> &       rhs) const;

      DeclException1(
        ExcEvaluationPointNotFound,
        Point<dim>,
        << "The evaluation point " << arg1
        << " was not found among the vertices of the present grid.");

    protected:
      const Point<dim> evaluation_point;
    };


    template <int dim>
    PointXDerivativeEvaluation<dim>::PointXDerivativeEvaluation(
      const Point<dim> &evaluation_point)
      : evaluation_point(evaluation_point)
    {}


    // What is interesting is the implementation of this functional: here,
    // J(phi_i)=d/dx phi_i(x0).
    //
    // We could, as in the implementation of the respective evaluation object
    // take the average of the gradients of each shape function phi_i at this
    // evaluation point. However, we take a slightly different approach: we
    // simply take the average over all cells that surround this point. The
    // question which cells <code>surrounds</code> the evaluation point is
    // made dependent on the mesh width by including those cells for which the
    // distance of the cell's midpoint to the evaluation point is less than
    // the cell's diameter.
    //
    // Taking the average of the gradient over the area/volume of these cells
    // leads to a dual solution which is very close to the one which would
    // result from the point evaluation of the gradient. It is simple to
    // justify theoretically that this does not change the method
    // significantly.
    template <int dim>
    void PointXDerivativeEvaluation<dim>::assemble_rhs(
      const DoFHandler<dim> &dof_handler,
      Vector<double> &       rhs) const
    {
      // Again, first set all entries to zero:
      rhs.reinit(dof_handler.n_dofs());

      // Initialize a <code>FEValues</code> object with a quadrature formula,
      // have abbreviations for the number of quadrature points and shape
      // functions...
      QGauss<dim>        quadrature(dof_handler.get_fe().degree + 1);
      FEValues<dim>      fe_values(dof_handler.get_fe(),
                              quadrature,
                              update_gradients | update_quadrature_points |
                                update_JxW_values);
      const unsigned int n_q_points    = fe_values.n_quadrature_points;
      const unsigned int dofs_per_cell = dof_handler.get_fe().dofs_per_cell;

      // ...and have two objects that are used to store the global indices of
      // the degrees of freedom on a cell, and the values of the gradients of
      // the shape functions at the quadrature points:
      Vector<double>            cell_rhs(dofs_per_cell);
      std::vector<unsigned int> local_dof_indices(dofs_per_cell);

      // Finally have a variable in which we will sum up the area/volume of
      // the cells over which we integrate, by integrating the unit functions
      // on these cells:
      double total_volume = 0;

      // Then start the loop over all cells, and select those cells which are
      // close enough to the evaluation point:
      for (const auto &cell : dof_handler.active_cell_iterators())
        if (cell->center().distance(evaluation_point) <= cell->diameter())
          {
            // If we have found such a cell, then initialize the
            // <code>FEValues</code> object and integrate the x-component of
            // the gradient of each shape function, as well as the unit
            // function for the total area/volume.
            fe_values.reinit(cell);
            cell_rhs = 0;

            for (unsigned int q = 0; q < n_q_points; ++q)
              {
                for (unsigned int i = 0; i < dofs_per_cell; ++i)
                  cell_rhs(i) +=
                    fe_values.shape_grad(i, q)[0] // (d/dx phi_i(x_q))
                    * fe_values.JxW(q);           // * dx
                total_volume += fe_values.JxW(q);
              }

            // If we have the local contributions, distribute them to the
            // global vector:
            cell->get_dof_indices(local_dof_indices);
            for (unsigned int i = 0; i < dofs_per_cell; ++i)
              rhs(local_dof_indices[i]) += cell_rhs(i);
          }

      // After we have looped over all cells, check whether we have found any
      // at all, by making sure that their volume is non-zero. If not, then
      // the results will be botched, as the right hand side should then still
      // be zero, so throw an exception:
      AssertThrow(total_volume > 0,
                  ExcEvaluationPointNotFound(evaluation_point));

      // Finally, we have by now only integrated the gradients of the shape
      // functions, not taking their mean value. We fix this by dividing by
      // the measure of the volume over which we have integrated:
      rhs /= total_volume;
    }


  } // namespace DualFunctional


  // @sect3{Extending the LaplaceSolver namespace}
  namespace LaplaceSolver
  {
    // @sect4{The DualSolver class}

    // In the same way as the <code>PrimalSolver</code> class above, we now
    // implement a <code>DualSolver</code>. It has all the same features, the
    // only difference is that it does not take a function object denoting a
    // right hand side object, but now takes a <code>DualFunctionalBase</code>
    // object that will assemble the right hand side vector of the dual
    // problem. The rest of the class is rather trivial.
    //
    // Since both primal and dual solver will use the same triangulation, but
    // different discretizations, it now becomes clear why we have made the
    // <code>Base</code> class a virtual one: since the final class will be
    // derived from both <code>PrimalSolver</code> as well as
    // <code>DualSolver</code>, it would have two <code>Base</code> instances,
    // would we not have marked the inheritance as virtual. Since in many
    // applications the base class would store much more information than just
    // the triangulation which needs to be shared between primal and dual
    // solvers, we do not usually want to use two such base classes.
    template <int dim>
    class DualSolver : public Solver<dim>
    {
    public:
      DualSolver(
        Triangulation<dim> &                           triangulation,
        const FiniteElement<dim> &                     fe,
        const Quadrature<dim> &                        quadrature,
        const Quadrature<dim - 1> &                    face_quadrature,
        const DualFunctional::DualFunctionalBase<dim> &dual_functional);

    protected:
      const SmartPointer<const DualFunctional::DualFunctionalBase<dim>>
                   dual_functional;
      virtual void assemble_rhs(Vector<double> &rhs) const override;

      static const Functions::ZeroFunction<dim> boundary_values;
    };

    template <int dim>
    const Functions::ZeroFunction<dim> DualSolver<dim>::boundary_values;

    template <int dim>
    DualSolver<dim>::DualSolver(
      Triangulation<dim> &                           triangulation,
      const FiniteElement<dim> &                     fe,
      const Quadrature<dim> &                        quadrature,
      const Quadrature<dim - 1> &                    face_quadrature,
      const DualFunctional::DualFunctionalBase<dim> &dual_functional)
      : Base<dim>(triangulation)
      , Solver<dim>(triangulation,
                    fe,
                    quadrature,
                    face_quadrature,
                    boundary_values)
      , dual_functional(&dual_functional)
    {}



    template <int dim>
    void DualSolver<dim>::assemble_rhs(Vector<double> &rhs) const
    {
      dual_functional->assemble_rhs(this->dof_handler, rhs);
    }


    // @sect4{The WeightedResidual class}

    // Here finally comes the main class of this program, the one that
    // implements the dual weighted residual error estimator. It joins the
    // primal and dual solver classes to use them for the computation of
    // primal and dual solutions, and implements the error representation
    // formula for use as error estimate and mesh refinement.
    //
    // The first few of the functions of this class are mostly overriders of
    // the respective functions of the base class:
    template <int dim>
    class WeightedResidual : public PrimalSolver<dim>, public DualSolver<dim>
    {
    public:
      WeightedResidual(
        Triangulation<dim> &                           coarse_grid,
        const FiniteElement<dim> &                     primal_fe,
        const FiniteElement<dim> &                     dual_fe,
        const Quadrature<dim> &                        quadrature,
        const Quadrature<dim - 1> &                    face_quadrature,
        const Function<dim> &                          rhs_function,
        const Function<dim> &                          boundary_values,
        const DualFunctional::DualFunctionalBase<dim> &dual_functional);

      virtual void solve_problem() override;

      virtual void postprocess(
        const Evaluation::EvaluationBase<dim> &postprocessor) const override;

      virtual unsigned int n_dofs() const override;

      virtual void refine_grid() override;

      virtual void output_solution() const override;

    private:
      // In the private section, we have two functions that are used to call
      // the <code>solve_problem</code> functions of the primal and dual base
      // classes. These two functions will be called in parallel by the
      // <code>solve_problem</code> function of this class.
      void solve_primal_problem();
      void solve_dual_problem();
      // Then declare abbreviations for active cell iterators, to avoid that
      // we have to write this lengthy name over and over again:

      using active_cell_iterator =
        typename DoFHandler<dim>::active_cell_iterator;

      // Next, declare a data type that we will us to store the contribution
      // of faces to the error estimator. The idea is that we can compute the
      // face terms from each of the two cells to this face, as they are the
      // same when viewed from both sides. What we will do is to compute them
      // only once, based on some rules explained below which of the two
      // adjacent cells will be in charge to do so. We then store the
      // contribution of each face in a map mapping faces to their values, and
      // only collect the contributions for each cell by looping over the
      // cells a second time and grabbing the values from the map.
      //
      // The data type of this map is declared here:
      using FaceIntegrals =
        typename std::map<typename DoFHandler<dim>::face_iterator, double>;

      // In the computation of the error estimates on cells and faces, we need
      // a number of helper objects, such as <code>FEValues</code> and
      // <code>FEFaceValues</code> functions, but also temporary objects
      // storing the values and gradients of primal and dual solutions, for
      // example. These fields are needed in the three functions that do the
      // integration on cells, and regular and irregular faces, respectively.
      //
      // There are three reasonable ways to provide these fields: first, as
      // local variables in the function that needs them; second, as member
      // variables of this class; third, as arguments passed to that function.
      //
      // These three alternatives all have drawbacks: the third that their
      // number is not negligible and would make calling these functions a
      // lengthy enterprise. The second has the drawback that it disallows
      // parallelization, since the threads that will compute the error
      // estimate have to have their own copies of these variables each, so
      // member variables of the enclosing class will not work. The first
      // approach, although straightforward, has a subtle but important
      // drawback: we will call these functions over and over again, many
      // thousands of times maybe; it now turns out that allocating
      // vectors and other objects that need memory from the heap is an
      // expensive business in terms of run-time, since memory allocation is
      // expensive when several threads are involved. It is thus
      // significantly better to allocate the memory only once, and recycle
      // the objects as often as possible.
      //
      // What to do? Our answer is to use a variant of the third strategy.
      // In fact, this is exactly what the WorkStream concept is supposed to
      // do (we have already introduced it above, but see also @ref threads).
      // To avoid that we have to give these functions a dozen or so
      // arguments, we pack all these variables into two structures, one which
      // is used for the computations on cells, the other doing them on the
      // faces. Both are then joined into the WeightedResidualScratchData class
      // that will serve as the "scratch data" class of the WorkStream concept:
      struct CellData
      {
        FEValues<dim>                           fe_values;
        const SmartPointer<const Function<dim>> right_hand_side;

        std::vector<double> cell_residual;
        std::vector<double> rhs_values;
        std::vector<double> dual_weights;
        std::vector<double> cell_laplacians;
        CellData(const FiniteElement<dim> &fe,
                 const Quadrature<dim> &   quadrature,
                 const Function<dim> &     right_hand_side);
        CellData(const CellData &cell_data);
      };

      struct FaceData
      {
        FEFaceValues<dim>    fe_face_values_cell;
        FEFaceValues<dim>    fe_face_values_neighbor;
        FESubfaceValues<dim> fe_subface_values_cell;

        std::vector<double>                  jump_residual;
        std::vector<double>                  dual_weights;
        typename std::vector<Tensor<1, dim>> cell_grads;
        typename std::vector<Tensor<1, dim>> neighbor_grads;
        FaceData(const FiniteElement<dim> & fe,
                 const Quadrature<dim - 1> &face_quadrature);
        FaceData(const FaceData &face_data);
      };

      struct WeightedResidualScratchData
      {
        WeightedResidualScratchData(
          const FiniteElement<dim> & primal_fe,
          const Quadrature<dim> &    primal_quadrature,
          const Quadrature<dim - 1> &primal_face_quadrature,
          const Function<dim> &      rhs_function,
          const Vector<double> &     primal_solution,
          const Vector<double> &     dual_weights);

        WeightedResidualScratchData(
          const WeightedResidualScratchData &scratch_data);

        CellData       cell_data;
        FaceData       face_data;
        Vector<double> primal_solution;
        Vector<double> dual_weights;
      };


      // WorkStream::run generally wants both a scratch object and a copy
      // object. Here, for reasons similar to what we had in step-9 when
      // discussing the computation of an approximation of the gradient, we
      // don't actually need a "copy data" structure. Since WorkStream insists
      // on having one of these, we just declare an empty structure that does
      // nothing other than being there.
      struct WeightedResidualCopyData
      {};



      // Regarding the evaluation of the error estimator, we have one driver
      // function that uses WorkStream::run() to call the second function on
      // every cell:
      void estimate_error(Vector<float> &error_indicators) const;

      void estimate_on_one_cell(const active_cell_iterator & cell,
                                WeightedResidualScratchData &scratch_data,
                                WeightedResidualCopyData &   copy_data,
                                Vector<float> &              error_indicators,
                                FaceIntegrals &face_integrals) const;

      // Then we have functions that do the actual integration of the error
      // representation formula. They will treat the terms on the cell
      // interiors, on those faces that have no hanging nodes, and on those
      // faces with hanging nodes, respectively:
      void integrate_over_cell(const active_cell_iterator &cell,
                               const Vector<double> &      primal_solution,
                               const Vector<double> &      dual_weights,
                               CellData &                  cell_data,
                               Vector<float> &error_indicators) const;

      void integrate_over_regular_face(const active_cell_iterator &cell,
                                       const unsigned int          face_no,
                                       const Vector<double> &primal_solution,
                                       const Vector<double> &dual_weights,
                                       FaceData &            face_data,
                                       FaceIntegrals &face_integrals) const;
      void integrate_over_irregular_face(const active_cell_iterator &cell,
                                         const unsigned int          face_no,
                                         const Vector<double> &primal_solution,
                                         const Vector<double> &dual_weights,
                                         FaceData &            face_data,
                                         FaceIntegrals &face_integrals) const;
    };



    // In the implementation of this class, we first have the constructors of
    // the <code>CellData</code> and <code>FaceData</code> member classes, and
    // the <code>WeightedResidual</code> constructor. They only initialize
    // fields to their correct lengths, so we do not have to discuss them in
    // too much detail:
    template <int dim>
    WeightedResidual<dim>::CellData::CellData(
      const FiniteElement<dim> &fe,
      const Quadrature<dim> &   quadrature,
      const Function<dim> &     right_hand_side)
      : fe_values(fe,
                  quadrature,
                  update_values | update_hessians | update_quadrature_points |
                    update_JxW_values)
      , right_hand_side(&right_hand_side)
      , cell_residual(quadrature.size())
      , rhs_values(quadrature.size())
      , dual_weights(quadrature.size())
      , cell_laplacians(quadrature.size())
    {}



    template <int dim>
    WeightedResidual<dim>::CellData::CellData(const CellData &cell_data)
      : fe_values(cell_data.fe_values.get_fe(),
                  cell_data.fe_values.get_quadrature(),
                  update_values | update_hessians | update_quadrature_points |
                    update_JxW_values)
      , right_hand_side(cell_data.right_hand_side)
      , cell_residual(cell_data.cell_residual)
      , rhs_values(cell_data.rhs_values)
      , dual_weights(cell_data.dual_weights)
      , cell_laplacians(cell_data.cell_laplacians)
    {}



    template <int dim>
    WeightedResidual<dim>::FaceData::FaceData(
      const FiniteElement<dim> & fe,
      const Quadrature<dim - 1> &face_quadrature)
      : fe_face_values_cell(fe,
                            face_quadrature,
                            update_values | update_gradients |
                              update_JxW_values | update_normal_vectors)
      , fe_face_values_neighbor(fe,
                                face_quadrature,
                                update_values | update_gradients |
                                  update_JxW_values | update_normal_vectors)
      , fe_subface_values_cell(fe, face_quadrature, update_gradients)
    {
      const unsigned int n_face_q_points = face_quadrature.size();

      jump_residual.resize(n_face_q_points);
      dual_weights.resize(n_face_q_points);
      cell_grads.resize(n_face_q_points);
      neighbor_grads.resize(n_face_q_points);
    }



    template <int dim>
    WeightedResidual<dim>::FaceData::FaceData(const FaceData &face_data)
      : fe_face_values_cell(face_data.fe_face_values_cell.get_fe(),
                            face_data.fe_face_values_cell.get_quadrature(),
                            update_values | update_gradients |
                              update_JxW_values | update_normal_vectors)
      , fe_face_values_neighbor(
          face_data.fe_face_values_neighbor.get_fe(),
          face_data.fe_face_values_neighbor.get_quadrature(),
          update_values | update_gradients | update_JxW_values |
            update_normal_vectors)
      , fe_subface_values_cell(
          face_data.fe_subface_values_cell.get_fe(),
          face_data.fe_subface_values_cell.get_quadrature(),
          update_gradients)
      , jump_residual(face_data.jump_residual)
      , dual_weights(face_data.dual_weights)
      , cell_grads(face_data.cell_grads)
      , neighbor_grads(face_data.neighbor_grads)
    {}



    template <int dim>
    WeightedResidual<dim>::WeightedResidualScratchData::
      WeightedResidualScratchData(
        const FiniteElement<dim> & primal_fe,
        const Quadrature<dim> &    primal_quadrature,
        const Quadrature<dim - 1> &primal_face_quadrature,
        const Function<dim> &      rhs_function,
        const Vector<double> &     primal_solution,
        const Vector<double> &     dual_weights)
      : cell_data(primal_fe, primal_quadrature, rhs_function)
      , face_data(primal_fe, primal_face_quadrature)
      , primal_solution(primal_solution)
      , dual_weights(dual_weights)
    {}

    template <int dim>
    WeightedResidual<dim>::WeightedResidualScratchData::
      WeightedResidualScratchData(
        const WeightedResidualScratchData &scratch_data)
      : cell_data(scratch_data.cell_data)
      , face_data(scratch_data.face_data)
      , primal_solution(scratch_data.primal_solution)
      , dual_weights(scratch_data.dual_weights)
    {}



    template <int dim>
    WeightedResidual<dim>::WeightedResidual(
      Triangulation<dim> &                           coarse_grid,
      const FiniteElement<dim> &                     primal_fe,
      const FiniteElement<dim> &                     dual_fe,
      const Quadrature<dim> &                        quadrature,
      const Quadrature<dim - 1> &                    face_quadrature,
      const Function<dim> &                          rhs_function,
      const Function<dim> &                          bv,
      const DualFunctional::DualFunctionalBase<dim> &dual_functional)
      : Base<dim>(coarse_grid)
      , PrimalSolver<dim>(coarse_grid,
                          primal_fe,
                          quadrature,
                          face_quadrature,
                          rhs_function,
                          bv)
      , DualSolver<dim>(coarse_grid,
                        dual_fe,
                        quadrature,
                        face_quadrature,
                        dual_functional)
    {}


    // The next five functions are boring, as they simply relay their work to
    // the base classes. The first calls the primal and dual solvers in
    // parallel, while postprocessing the solution and retrieving the number
    // of degrees of freedom is done by the primal class.
    template <int dim>
    void WeightedResidual<dim>::solve_problem()
    {
      Threads::TaskGroup<void> tasks;
      tasks +=
        Threads::new_task(&WeightedResidual<dim>::solve_primal_problem, *this);
      tasks +=
        Threads::new_task(&WeightedResidual<dim>::solve_dual_problem, *this);
      tasks.join_all();
    }


    template <int dim>
    void WeightedResidual<dim>::solve_primal_problem()
    {
      PrimalSolver<dim>::solve_problem();
    }

    template <int dim>
    void WeightedResidual<dim>::solve_dual_problem()
    {
      DualSolver<dim>::solve_problem();
    }


    template <int dim>
    void WeightedResidual<dim>::postprocess(
      const Evaluation::EvaluationBase<dim> &postprocessor) const
    {
      PrimalSolver<dim>::postprocess(postprocessor);
    }


    template <int dim>
    unsigned int WeightedResidual<dim>::n_dofs() const
    {
      return PrimalSolver<dim>::n_dofs();
    }



    // Now, it is becoming more interesting: the <code>refine_grid()</code>
    // function asks the error estimator to compute the cell-wise error
    // indicators, then uses their absolute values for mesh refinement.
    template <int dim>
    void WeightedResidual<dim>::refine_grid()
    {
      // First call the function that computes the cell-wise and global error:
      Vector<float> error_indicators(this->triangulation->n_active_cells());
      estimate_error(error_indicators);

      // Then note that marking cells for refinement or coarsening only works
      // if all indicators are positive, to allow their comparison. Thus, drop
      // the signs on all these indicators:
      for (float &error_indicator : error_indicators)
        error_indicator = std::fabs(error_indicator);

      // Finally, we can select between different strategies for
      // refinement. The default here is to refine those cells with the
      // largest error indicators that make up for a total of 80 per cent of
      // the error, while we coarsen those with the smallest indicators that
      // make up for the bottom 2 per cent of the error.
      GridRefinement::refine_and_coarsen_fixed_fraction(*this->triangulation,
                                                        error_indicators,
                                                        0.8,
                                                        0.02);
      this->triangulation->execute_coarsening_and_refinement();
    }


    // Since we want to output both the primal and the dual solution, we
    // overload the <code>output_solution</code> function. The only
    // interesting feature of this function is that the primal and dual
    // solutions are defined on different finite element spaces, which is not
    // the format the <code>DataOut</code> class expects. Thus, we have to
    // transfer them to a common finite element space. Since we want the
    // solutions only to see them qualitatively, we contend ourselves with
    // interpolating the dual solution to the (smaller) primal space. For the
    // interpolation, there is a library function, that takes a
    // AffineConstraints object including the hanging node
    // constraints. The rest is standard.
    template <int dim>
    void WeightedResidual<dim>::output_solution() const
    {
      AffineConstraints<double> primal_hanging_node_constraints;
      DoFTools::make_hanging_node_constraints(PrimalSolver<dim>::dof_handler,
                                              primal_hanging_node_constraints);
      primal_hanging_node_constraints.close();
      Vector<double> dual_solution(PrimalSolver<dim>::dof_handler.n_dofs());
      FETools::interpolate(DualSolver<dim>::dof_handler,
                           DualSolver<dim>::solution,
                           PrimalSolver<dim>::dof_handler,
                           primal_hanging_node_constraints,
                           dual_solution);

      DataOut<dim> data_out;
      data_out.attach_dof_handler(PrimalSolver<dim>::dof_handler);

      // Add the data vectors for which we want output. Add them both, the
      // <code>DataOut</code> functions can handle as many data vectors as you
      // wish to write to output:
      data_out.add_data_vector(PrimalSolver<dim>::solution, "primal_solution");
      data_out.add_data_vector(dual_solution, "dual_solution");

      data_out.build_patches();

      std::ofstream out("solution-" + std::to_string(this->refinement_cycle) +
                        ".vtu");
      data_out.write(out, DataOutBase::vtu);
    }


    // @sect3{Estimating errors}

    // @sect4{Error estimation driver functions}
    //
    // As for the actual computation of error estimates, let's start with the
    // function that drives all this, i.e. calls those functions that actually
    // do the work, and finally collects the results.
    template <int dim>
    void
    WeightedResidual<dim>::estimate_error(Vector<float> &error_indicators) const
    {
      // The first task in computing the error is to set up vectors that
      // denote the primal solution, and the weights (z-z_h)=(z-I_hz), both in
      // the finite element space for which we have computed the dual
      // solution. For this, we have to interpolate the primal solution to the
      // dual finite element space, and to subtract the interpolation of the
      // computed dual solution to the primal finite element
      // space. Fortunately, the library provides functions for the
      // interpolation into larger or smaller finite element spaces, so this
      // is mostly obvious.
      //
      // First, let's do that for the primal solution: it is cell-wise
      // interpolated into the finite element space in which we have solved
      // the dual problem: But, again as in the
      // <code>WeightedResidual::output_solution</code> function we first need
      // to create an AffineConstraints object including the hanging node
      // constraints, but this time of the dual finite element space.
      AffineConstraints<double> dual_hanging_node_constraints;
      DoFTools::make_hanging_node_constraints(DualSolver<dim>::dof_handler,
                                              dual_hanging_node_constraints);
      dual_hanging_node_constraints.close();
      Vector<double> primal_solution(DualSolver<dim>::dof_handler.n_dofs());
      FETools::interpolate(PrimalSolver<dim>::dof_handler,
                           PrimalSolver<dim>::solution,
                           DualSolver<dim>::dof_handler,
                           dual_hanging_node_constraints,
                           primal_solution);

      // Then for computing the interpolation of the numerically approximated
      // dual solution z into the finite element space of the primal solution
      // and subtracting it from z: use the
      // <code>interpolate_difference</code> function, that gives (z-I_hz) in
      // the element space of the dual solution.
      AffineConstraints<double> primal_hanging_node_constraints;
      DoFTools::make_hanging_node_constraints(PrimalSolver<dim>::dof_handler,
                                              primal_hanging_node_constraints);
      primal_hanging_node_constraints.close();
      Vector<double> dual_weights(DualSolver<dim>::dof_handler.n_dofs());
      FETools::interpolation_difference(DualSolver<dim>::dof_handler,
                                        dual_hanging_node_constraints,
                                        DualSolver<dim>::solution,
                                        PrimalSolver<dim>::dof_handler,
                                        primal_hanging_node_constraints,
                                        dual_weights);

      // Note that this could probably have been more efficient since those
      // constraints have been used previously when assembling matrix and
      // right hand side for the primal problem and writing out the dual
      // solution. We leave the optimization of the program in this respect as
      // an exercise.

      // Having computed the dual weights we now proceed with computing the
      // cell and face residuals of the primal solution. First we set up a map
      // between face iterators and their jump term contributions of faces to
      // the error estimator. The reason is that we compute the jump terms
      // only once, from one side of the face, and want to collect them only
      // afterwards when looping over all cells a second time.
      //
      // We initialize this map already with a value of -1e20 for all faces,
      // since this value will stand out in the results if something should go
      // wrong and we fail to compute the value for a face for some
      // reason. Secondly, this initialization already makes the std::map
      // object allocate all objects it may possibly need. This is important
      // since we will write into this structure from parallel threads,
      // and doing so would not be thread-safe if the map needed to allocate
      // memory and thereby reshape its data structures. In other words, the
      // initial initialization relieves us from the necessity to synchronize
      // the threads through a mutex each time they write to (and modify the
      // structure of) this map.
      FaceIntegrals face_integrals;
      for (const auto &cell :
           DualSolver<dim>::dof_handler.active_cell_iterators())
        for (const auto &face : cell->face_iterators())
          face_integrals[face] = -1e20;

      auto worker = [this,
                     &error_indicators,
                     &face_integrals](const active_cell_iterator & cell,
                                      WeightedResidualScratchData &scratch_data,
                                      WeightedResidualCopyData &   copy_data) {
        this->estimate_on_one_cell(
          cell, scratch_data, copy_data, error_indicators, face_integrals);
      };

      auto do_nothing_copier =
        std::function<void(const WeightedResidualCopyData &)>();

      // Then hand it all off to WorkStream::run() to compute the
      // estimators for all cells in parallel:
      WorkStream::run(
        DualSolver<dim>::dof_handler.begin_active(),
        DualSolver<dim>::dof_handler.end(),
        worker,
        do_nothing_copier,
        WeightedResidualScratchData(*DualSolver<dim>::fe,
                                    *DualSolver<dim>::quadrature,
                                    *DualSolver<dim>::face_quadrature,
                                    *this->rhs_function,
                                    primal_solution,
                                    dual_weights),
        WeightedResidualCopyData());

      // Once the error contributions are computed, sum them up. For this,
      // note that the cell terms are already set, and that only the edge
      // terms need to be collected. Thus, loop over all cells and their
      // faces, make sure that the contributions of each of the faces are
      // there, and add them up. Only take minus one half of the jump term,
      // since the other half will be taken by the neighboring cell.
      unsigned int present_cell = 0;
      for (const auto &cell :
           DualSolver<dim>::dof_handler.active_cell_iterators())
        {
          for (const auto &face : cell->face_iterators())
            {
              Assert(face_integrals.find(face) != face_integrals.end(),
                     ExcInternalError());
              error_indicators(present_cell) -= 0.5 * face_integrals[face];
            }
          ++present_cell;
        }
      std::cout << "   Estimated error="
                << std::accumulate(error_indicators.begin(),
                                   error_indicators.end(),
                                   0.)
                << std::endl;
    }


    // @sect4{Estimating on a single cell}

    // Next we have the function that is called to estimate the error on a
    // single cell. The function may be called multiple times if the library was
    // configured to use multithreading. Here it goes:
    template <int dim>
    void WeightedResidual<dim>::estimate_on_one_cell(
      const active_cell_iterator & cell,
      WeightedResidualScratchData &scratch_data,
      WeightedResidualCopyData &   copy_data,
      Vector<float> &              error_indicators,
      FaceIntegrals &              face_integrals) const
    {
      // Because of WorkStream, estimate_on_one_cell requires a CopyData object
      // even if it is no used. The next line silences a warning about this
      // unused variable.
      (void)copy_data;

      // First task on each cell is to compute the cell residual
      // contributions of this cell, and put them into the
      // <code>error_indicators</code> variable:
      integrate_over_cell(cell,
                          scratch_data.primal_solution,
                          scratch_data.dual_weights,
                          scratch_data.cell_data,
                          error_indicators);

      // After computing the cell terms, turn to the face terms. For this,
      // loop over all faces of the present cell, and see whether
      // something needs to be computed on it:
      for (const auto face_no : cell->face_indices())
        {
          // First, if this face is part of the boundary, then there is
          // nothing to do. However, to make things easier when summing up
          // the contributions of the faces of cells, we enter this face
          // into the list of faces with a zero contribution to the error.
          if (cell->face(face_no)->at_boundary())
            {
              face_integrals[cell->face(face_no)] = 0;
              continue;
            }

          // Next, note that since we want to compute the jump terms on
          // each face only once although we access it twice (if it is not
          // at the boundary), we have to define some rules who is
          // responsible for computing on a face:
          //
          // First, if the neighboring cell is on the same level as this
          // one, i.e. neither further refined not coarser, then the one
          // with the lower index within this level does the work. In
          // other words: if the other one has a lower index, then skip
          // work on this face:
          if ((cell->neighbor(face_no)->has_children() == false) &&
              (cell->neighbor(face_no)->level() == cell->level()) &&
              (cell->neighbor(face_no)->index() < cell->index()))
            continue;

          // Likewise, we always work from the coarser cell if this and
          // its neighbor differ in refinement. Thus, if the neighboring
          // cell is less refined than the present one, then do nothing
          // since we integrate over the subfaces when we visit the coarse
          // cell.
          if (cell->at_boundary(face_no) == false)
            if (cell->neighbor(face_no)->level() < cell->level())
              continue;


          // Now we know that we are in charge here, so actually compute
          // the face jump terms. If the face is a regular one, i.e.  the
          // other side's cell is neither coarser not finer than this
          // cell, then call one function, and if the cell on the other
          // side is further refined, then use another function. Note that
          // the case that the cell on the other side is coarser cannot
          // happen since we have decided above that we handle this case
          // when we pass over that other cell.
          if (cell->face(face_no)->has_children() == false)
            integrate_over_regular_face(cell,
                                        face_no,
                                        scratch_data.primal_solution,
                                        scratch_data.dual_weights,
                                        scratch_data.face_data,
                                        face_integrals);
          else
            integrate_over_irregular_face(cell,
                                          face_no,
                                          scratch_data.primal_solution,
                                          scratch_data.dual_weights,
                                          scratch_data.face_data,
                                          face_integrals);
        }
    }


    // @sect4{Computing cell term error contributions}

    // As for the actual computation of the error contributions, first turn to
    // the cell terms:
    template <int dim>
    void WeightedResidual<dim>::integrate_over_cell(
      const active_cell_iterator &cell,
      const Vector<double> &      primal_solution,
      const Vector<double> &      dual_weights,
      CellData &                  cell_data,
      Vector<float> &             error_indicators) const
    {
      // The tasks to be done are what appears natural from looking at the
      // error estimation formula: first get the right hand side and Laplacian
      // of the numerical solution at the quadrature points for the cell
      // residual,
      cell_data.fe_values.reinit(cell);
      cell_data.right_hand_side->value_list(
        cell_data.fe_values.get_quadrature_points(), cell_data.rhs_values);
      cell_data.fe_values.get_function_laplacians(primal_solution,
                                                  cell_data.cell_laplacians);

      // ...then get the dual weights...
      cell_data.fe_values.get_function_values(dual_weights,
                                              cell_data.dual_weights);

      // ...and finally build the sum over all quadrature points and store it
      // with the present cell:
      double sum = 0;
      for (unsigned int p = 0; p < cell_data.fe_values.n_quadrature_points; ++p)
        sum += ((cell_data.rhs_values[p] + cell_data.cell_laplacians[p]) *
                cell_data.dual_weights[p] * cell_data.fe_values.JxW(p));
      error_indicators(cell->active_cell_index()) += sum;
    }


    // @sect4{Computing edge term error contributions -- 1}

    // On the other hand, computation of the edge terms for the error estimate
    // is not so simple. First, we have to distinguish between faces with and
    // without hanging nodes. Because it is the simple case, we first consider
    // the case without hanging nodes on a face (let's call this the `regular'
    // case):
    template <int dim>
    void WeightedResidual<dim>::integrate_over_regular_face(
      const active_cell_iterator &cell,
      const unsigned int          face_no,
      const Vector<double> &      primal_solution,
      const Vector<double> &      dual_weights,
      FaceData &                  face_data,
      FaceIntegrals &             face_integrals) const
    {
      const unsigned int n_q_points =
        face_data.fe_face_values_cell.n_quadrature_points;

      // The first step is to get the values of the gradients at the
      // quadrature points of the finite element field on the present
      // cell. For this, initialize the <code>FEFaceValues</code> object
      // corresponding to this side of the face, and extract the gradients
      // using that object.
      face_data.fe_face_values_cell.reinit(cell, face_no);
      face_data.fe_face_values_cell.get_function_gradients(
        primal_solution, face_data.cell_grads);

      // The second step is then to extract the gradients of the finite
      // element solution at the quadrature points on the other side of the
      // face, i.e. from the neighboring cell.
      //
      // For this, do a sanity check before: make sure that the neighbor
      // actually exists (yes, we should not have come here if the neighbor
      // did not exist, but in complicated software there are bugs, so better
      // check this), and if this is not the case throw an error.
      Assert(cell->neighbor(face_no).state() == IteratorState::valid,
             ExcInternalError());
      // If we have that, then we need to find out with which face of the
      // neighboring cell we have to work, i.e. the <code>how-many'th</code> the
      // neighbor the present cell is of the cell behind the present face. For
      // this, there is a function, and we put the result into a variable with
      // the name <code>neighbor_neighbor</code>:
      const unsigned int neighbor_neighbor =
        cell->neighbor_of_neighbor(face_no);
      // Then define an abbreviation for the neighbor cell, initialize the
      // <code>FEFaceValues</code> object on that cell, and extract the
      // gradients on that cell:
      const active_cell_iterator neighbor = cell->neighbor(face_no);
      face_data.fe_face_values_neighbor.reinit(neighbor, neighbor_neighbor);
      face_data.fe_face_values_neighbor.get_function_gradients(
        primal_solution, face_data.neighbor_grads);

      // Now that we have the gradients on this and the neighboring cell,
      // compute the jump residual by multiplying the jump in the gradient
      // with the normal vector:
      for (unsigned int p = 0; p < n_q_points; ++p)
        face_data.jump_residual[p] =
          ((face_data.cell_grads[p] - face_data.neighbor_grads[p]) *
           face_data.fe_face_values_cell.normal_vector(p));

      // Next get the dual weights for this face:
      face_data.fe_face_values_cell.get_function_values(dual_weights,
                                                        face_data.dual_weights);

      // Finally, we have to compute the sum over jump residuals, dual
      // weights, and quadrature weights, to get the result for this face:
      double face_integral = 0;
      for (unsigned int p = 0; p < n_q_points; ++p)
        face_integral +=
          (face_data.jump_residual[p] * face_data.dual_weights[p] *
           face_data.fe_face_values_cell.JxW(p));

      // Double check that the element already exists and that it was not
      // already written to...
      Assert(face_integrals.find(cell->face(face_no)) != face_integrals.end(),
             ExcInternalError());
      Assert(face_integrals[cell->face(face_no)] == -1e20, ExcInternalError());

      // ...then store computed value at assigned location. Note that the
      // stored value does not contain the factor 1/2 that appears in the
      // error representation. The reason is that the term actually does not
      // have this factor if we loop over all faces in the triangulation, but
      // only appears if we write it as a sum over all cells and all faces of
      // each cell; we thus visit the same face twice. We take account of this
      // by using this factor -1/2 later, when we sum up the contributions for
      // each cell individually.
      face_integrals[cell->face(face_no)] = face_integral;
    }


    // @sect4{Computing edge term error contributions -- 2}

    // We are still missing the case of faces with hanging nodes. This is what
    // is covered in this function:
    template <int dim>
    void WeightedResidual<dim>::integrate_over_irregular_face(
      const active_cell_iterator &cell,
      const unsigned int          face_no,
      const Vector<double> &      primal_solution,
      const Vector<double> &      dual_weights,
      FaceData &                  face_data,
      FaceIntegrals &             face_integrals) const
    {
      // First again two abbreviations, and some consistency checks whether
      // the function is called only on faces for which it is supposed to be
      // called:
      const unsigned int n_q_points =
        face_data.fe_face_values_cell.n_quadrature_points;

      const typename DoFHandler<dim>::face_iterator face = cell->face(face_no);
      const typename DoFHandler<dim>::cell_iterator neighbor =
        cell->neighbor(face_no);
      Assert(neighbor.state() == IteratorState::valid, ExcInternalError());
      Assert(neighbor->has_children(), ExcInternalError());
      (void)neighbor;

      // Then find out which neighbor the present cell is of the adjacent
      // cell. Note that we will operate on the children of this adjacent
      // cell, but that their orientation is the same as that of their mother,
      // i.e. the neighbor direction is the same.
      const unsigned int neighbor_neighbor =
        cell->neighbor_of_neighbor(face_no);

      // Then simply do everything we did in the previous function for one
      // face for all the sub-faces now:
      for (unsigned int subface_no = 0; subface_no < face->n_children();
           ++subface_no)
        {
          // Start with some checks again: get an iterator pointing to the
          // cell behind the present subface and check whether its face is a
          // subface of the one we are considering. If that were not the case,
          // then there would be either a bug in the
          // <code>neighbor_neighbor</code> function called above, or -- worse
          // -- some function in the library did not keep to some underlying
          // assumptions about cells, their children, and their faces. In any
          // case, even though this assertion should not be triggered, it does
          // not harm to be cautious, and in optimized mode computations the
          // assertion will be removed anyway.
          const active_cell_iterator neighbor_child =
            cell->neighbor_child_on_subface(face_no, subface_no);
          Assert(neighbor_child->face(neighbor_neighbor) ==
                   cell->face(face_no)->child(subface_no),
                 ExcInternalError());

          // Now start the work by again getting the gradient of the solution
          // first at this side of the interface,
          face_data.fe_subface_values_cell.reinit(cell, face_no, subface_no);
          face_data.fe_subface_values_cell.get_function_gradients(
            primal_solution, face_data.cell_grads);
          // then at the other side,
          face_data.fe_face_values_neighbor.reinit(neighbor_child,
                                                   neighbor_neighbor);
          face_data.fe_face_values_neighbor.get_function_gradients(
            primal_solution, face_data.neighbor_grads);

          // and finally building the jump residuals. Since we take the normal
          // vector from the other cell this time, revert the sign of the
          // first term compared to the other function:
          for (unsigned int p = 0; p < n_q_points; ++p)
            face_data.jump_residual[p] =
              ((face_data.neighbor_grads[p] - face_data.cell_grads[p]) *
               face_data.fe_face_values_neighbor.normal_vector(p));

          // Then get dual weights:
          face_data.fe_face_values_neighbor.get_function_values(
            dual_weights, face_data.dual_weights);

          // At last, sum up the contribution of this sub-face, and set it in
          // the global map:
          double face_integral = 0;
          for (unsigned int p = 0; p < n_q_points; ++p)
            face_integral +=
              (face_data.jump_residual[p] * face_data.dual_weights[p] *
               face_data.fe_face_values_neighbor.JxW(p));
          face_integrals[neighbor_child->face(neighbor_neighbor)] =
            face_integral;
        }

      // Once the contributions of all sub-faces are computed, loop over all
      // sub-faces to collect and store them with the mother face for simple
      // use when later collecting the error terms of cells. Again make safety
      // checks that the entries for the sub-faces have been computed and do
      // not carry an invalid value.
      double sum = 0;
      for (unsigned int subface_no = 0; subface_no < face->n_children();
           ++subface_no)
        {
          Assert(face_integrals.find(face->child(subface_no)) !=
                   face_integrals.end(),
                 ExcInternalError());
          Assert(face_integrals[face->child(subface_no)] != -1e20,
                 ExcInternalError());

          sum += face_integrals[face->child(subface_no)];
        }
      // Finally store the value with the parent face.
      face_integrals[face] = sum;
    }

  } // namespace LaplaceSolver


  // @sect3{A simulation framework}

  // In the previous example program, we have had two functions that were used
  // to drive the process of solving on subsequently finer grids. We extend
  // this here to allow for a number of parameters to be passed to these
  // functions, and put all of that into framework class.
  //
  // You will have noted that this program is built up of a number of small
  // parts (evaluation functions, solver classes implementing various
  // refinement methods, different dual functionals, different problem and
  // data descriptions), which makes the program relatively simple to extend,
  // but also allows to solve a large number of different problems by
  // replacing one part by another. We reflect this flexibility by declaring a
  // structure in the following framework class that holds a number of
  // parameters that may be set to test various combinations of the parts of
  // this program, and which can be used to test it at various problems and
  // discretizations in a simple way.
  template <int dim>
  struct Framework
  {
  public:
    // First, we declare two abbreviations for simple use of the respective
    // data types:
    using Evaluator     = Evaluation::EvaluationBase<dim>;
    using EvaluatorList = std::list<Evaluator *>;


    // Then we have the structure which declares all the parameters that may
    // be set. In the default constructor of the structure, these values are
    // all set to default values, for simple use.
    struct ProblemDescription
    {
      // First allow for the degrees of the piecewise polynomials by which the
      // primal and dual problems will be discretized. They default to (bi-,
      // tri-)linear ansatz functions for the primal, and (bi-, tri-)quadratic
      // ones for the dual problem. If a refinement criterion is chosen that
      // does not need the solution of a dual problem, the value of the dual
      // finite element degree is of course ignored.
      unsigned int primal_fe_degree;
      unsigned int dual_fe_degree;

      // Then have an object that describes the problem type, i.e. right hand
      // side, domain, boundary values, etc. The pointer needed here defaults
      // to the Null pointer, i.e. you will have to set it in actual instances
      // of this object to make it useful.
      std::unique_ptr<const Data::SetUpBase<dim>> data;

      // Since we allow to use different refinement criteria (global
      // refinement, refinement by the Kelly error indicator, possibly with a
      // weight, and using the dual estimator), define a number of enumeration
      // values, and subsequently a variable of that type. It will default to
      // <code>dual_weighted_error_estimator</code>.
      enum RefinementCriterion
      {
        dual_weighted_error_estimator,
        global_refinement,
        kelly_indicator,
        weighted_kelly_indicator
      };

      RefinementCriterion refinement_criterion;

      // Next, an object that describes the dual functional. It is only needed
      // if the dual weighted residual refinement is chosen, and also defaults
      // to a Null pointer.
      std::unique_ptr<const DualFunctional::DualFunctionalBase<dim>>
        dual_functional;

      // Then a list of evaluation objects. Its default value is empty,
      // i.e. no evaluation objects.
      EvaluatorList evaluator_list;

      // Next to last, a function that is used as a weight to the
      // <code>RefinementWeightedKelly</code> class. The default value of this
      // pointer is zero, but you have to set it to some other value if you
      // want to use the <code>weighted_kelly_indicator</code> refinement
      // criterion.
      std::unique_ptr<const Function<dim>> kelly_weight;

      // Finally, we have a variable that denotes the maximum number of
      // degrees of freedom we allow for the (primal) discretization. If it is
      // exceeded, we stop the process of solving and intermittent mesh
      // refinement. Its default value is 20,000.
      unsigned int max_degrees_of_freedom;

      // Finally the default constructor of this class:
      ProblemDescription();
    };

    // The driver framework class only has one method which calls solver and
    // mesh refinement intermittently, and does some other small tasks in
    // between. Since it does not need data besides the parameters given to
    // it, we make it static:
    static void run(const ProblemDescription &descriptor);
  };


  // As for the implementation, first the constructor of the parameter object,
  // setting all values to their defaults:
  template <int dim>
  Framework<dim>::ProblemDescription::ProblemDescription()
    : primal_fe_degree(1)
    , dual_fe_degree(2)
    , refinement_criterion(dual_weighted_error_estimator)
    , max_degrees_of_freedom(20000)
  {}



  // Then the function which drives the whole process:
  template <int dim>
  void Framework<dim>::run(const ProblemDescription &descriptor)
  {
    // First create a triangulation from the given data object,
    Triangulation<dim> triangulation(
      Triangulation<dim>::smoothing_on_refinement);
    descriptor.data->create_coarse_grid(triangulation);

    // then a set of finite elements and appropriate quadrature formula:
    const FE_Q<dim>       primal_fe(descriptor.primal_fe_degree);
    const FE_Q<dim>       dual_fe(descriptor.dual_fe_degree);
    const QGauss<dim>     quadrature(descriptor.dual_fe_degree + 1);
    const QGauss<dim - 1> face_quadrature(descriptor.dual_fe_degree + 1);

    // Next, select one of the classes implementing different refinement
    // criteria.
    std::unique_ptr<LaplaceSolver::Base<dim>> solver;
    switch (descriptor.refinement_criterion)
      {
        case ProblemDescription::dual_weighted_error_estimator:
          {
            solver = std::make_unique<LaplaceSolver::WeightedResidual<dim>>(
              triangulation,
              primal_fe,
              dual_fe,
              quadrature,
              face_quadrature,
              descriptor.data->get_right_hand_side(),
              descriptor.data->get_boundary_values(),
              *descriptor.dual_functional);
            break;
          }

        case ProblemDescription::global_refinement:
          {
            solver = std::make_unique<LaplaceSolver::RefinementGlobal<dim>>(
              triangulation,
              primal_fe,
              quadrature,
              face_quadrature,
              descriptor.data->get_right_hand_side(),
              descriptor.data->get_boundary_values());
            break;
          }

        case ProblemDescription::kelly_indicator:
          {
            solver = std::make_unique<LaplaceSolver::RefinementKelly<dim>>(
              triangulation,
              primal_fe,
              quadrature,
              face_quadrature,
              descriptor.data->get_right_hand_side(),
              descriptor.data->get_boundary_values());
            break;
          }

        case ProblemDescription::weighted_kelly_indicator:
          {
            solver =
              std::make_unique<LaplaceSolver::RefinementWeightedKelly<dim>>(
                triangulation,
                primal_fe,
                quadrature,
                face_quadrature,
                descriptor.data->get_right_hand_side(),
                descriptor.data->get_boundary_values(),
                *descriptor.kelly_weight);
            break;
          }

        default:
          AssertThrow(false, ExcInternalError());
      }

    // Now that all objects are in place, run the main loop. The stopping
    // criterion is implemented at the bottom of the loop.
    //
    // In the loop, first set the new cycle number, then solve the problem,
    // output its solution(s), apply the evaluation objects to it, then decide
    // whether we want to refine the mesh further and solve again on this
    // mesh, or jump out of the loop.
    for (unsigned int step = 0; true; ++step)
      {
        std::cout << "Refinement cycle: " << step << std::endl;

        solver->set_refinement_cycle(step);
        solver->solve_problem();
        solver->output_solution();

        std::cout << "   Number of degrees of freedom=" << solver->n_dofs()
                  << std::endl;

        for (const auto &evaluator : descriptor.evaluator_list)
          {
            evaluator->set_refinement_cycle(step);
            solver->postprocess(*evaluator);
          }


        if (solver->n_dofs() < descriptor.max_degrees_of_freedom)
          solver->refine_grid();
        else
          break;
      }

    // Clean up the screen after the loop has run:
    std::cout << std::endl;
  }

} // namespace Step14



// @sect3{The main function}

// Here finally comes the main function. It drives the whole process by
// specifying a set of parameters to be used for the simulation (polynomial
// degrees, evaluation and dual functionals, etc), and passes them packed into
// a structure to the frame work class above.
int main()
{
  try
    {
      using namespace Step14;

      // Describe the problem we want to solve here by passing a descriptor
      // object to the function doing the rest of the work:
      const unsigned int                 dim = 2;
      Framework<dim>::ProblemDescription descriptor;

      // First set the refinement criterion we wish to use:
      descriptor.refinement_criterion =
        Framework<dim>::ProblemDescription::dual_weighted_error_estimator;
      // Here, we could as well have used <code>global_refinement</code> or
      // <code>weighted_kelly_indicator</code>. Note that the information
      // given about dual finite elements, dual functional, etc is only
      // important for the given choice of refinement criterion, and is
      // ignored otherwise.

      // Then set the polynomial degrees of primal and dual problem. We choose
      // here bi-linear and bi-quadratic ones:
      descriptor.primal_fe_degree = 1;
      descriptor.dual_fe_degree   = 2;

      // Then set the description of the test case, i.e. domain, boundary
      // values, and right hand side. These are prepackaged in classes. We
      // take here the description of <code>Exercise_2_3</code>, but you can
      // also use <code>CurvedRidges@<dim@></code>:
      descriptor.data =
        std::make_unique<Data::SetUp<Data::Exercise_2_3<dim>, dim>>();

      // Next set first a dual functional, then a list of evaluation
      // objects. We choose as default the evaluation of the value at an
      // evaluation point, represented by the classes
      // <code>PointValueEvaluation</code> in the namespaces of evaluation and
      // dual functional classes. You can also set the
      // <code>PointXDerivativeEvaluation</code> classes for the x-derivative
      // instead of the value at the evaluation point.
      //
      // Note that dual functional and evaluation objects should
      // match. However, you can give as many evaluation functionals as you
      // want, so you can have both point value and derivative evaluated after
      // each step.  One such additional evaluation is to output the grid in
      // each step.
      const Point<dim> evaluation_point(0.75, 0.75);
      descriptor.dual_functional =
        std::make_unique<DualFunctional::PointValueEvaluation<dim>>(
          evaluation_point);

      Evaluation::PointValueEvaluation<dim> postprocessor1(evaluation_point);
      Evaluation::GridOutput<dim>           postprocessor2("grid");

      descriptor.evaluator_list.push_back(&postprocessor1);
      descriptor.evaluator_list.push_back(&postprocessor2);

      // Set the maximal number of degrees of freedom after which we want the
      // program to stop refining the mesh further:
      descriptor.max_degrees_of_freedom = 20000;

      // Finally pass the descriptor object to a function that runs the entire
      // solution with it:
      Framework<dim>::run(descriptor);
    }

  // Catch exceptions to give information about things that failed:
  catch (std::exception &exc)
    {
      std::cerr << std::endl
                << std::endl
                << "----------------------------------------------------"
                << std::endl;
      std::cerr << "Exception on processing: " << std::endl
                << exc.what() << std::endl
                << "Aborting!" << std::endl
                << "----------------------------------------------------"
                << std::endl;
      return 1;
    }
  catch (...)
    {
      std::cerr << std::endl
                << std::endl
                << "----------------------------------------------------"
                << std::endl;
      std::cerr << "Unknown exception!" << std::endl
                << "Aborting!" << std::endl
                << "----------------------------------------------------"
                << std::endl;
      return 1;
    }

  return 0;
}
