/* $Id$ */
/* */
-/* Copyright (C) 2010 by the deal.II authors */
+/* Copyright (C) 2010, 2011 by the deal.II authors */
/* */
/* This file is subject to QPL and may not be distributed */
/* without copyright and license information. Please refer */
#include <deal.II/dofs/dof_tools.h>
#include <deal.II/numerics/data_out.h>
#include <deal.II/fe/mapping_q1.h>
- // Here the discontinuous finite
- // elements are defined. They are
- // used in the same way as all other
- // finite elements, though -- as you
- // have seen in previous tutorial
+ // Here the discontinuous finite elements are
+ // defined. They are used in the same way as
+ // all other finite elements, though -- as
+ // you have seen in previous tutorial
// programs -- there isn't much user
- // interaction with finite element
- // classes at all: the are passed to
- // <code>DoFHandler</code> and <code>FEValues</code>
- // objects, and that is about it.
+ // interaction with finite element classes at
+ // all: the are passed to
+ // <code>DoFHandler</code> and
+ // <code>FEValues</code> objects, and that is
+ // about it.
#include <deal.II/fe/fe_dgq.h>
// We are going to use the simplest
// possible solver, called Richardson
#include <iostream>
#include <fstream>
-using namespace dealii;
-
- // @sect3{Equation data}
- //
- // First, we define a class
- // describing the inhomogeneous
- // boundary data. Since only its
- // values are used, we implement
- // value_list(), but leave all other
- // functions of Function undefined.
-template <int dim>
-class BoundaryValues: public Function<dim>
-{
- public:
- BoundaryValues () {};
- virtual void value_list (const std::vector<Point<dim> > &points,
- std::vector<double> &values,
- const unsigned int component=0) const;
-};
-
- // Given the flow direction, the inflow
- // boundary of the unit square $[0,1]^2$ are
- // the right and the lower boundaries. We
- // prescribe discontinuous boundary values 1
- // and 0 on the x-axis and value 0 on the
- // right boundary. The values of this
- // function on the outflow boundaries will
- // not be used within the DG scheme.
-template <int dim>
-void BoundaryValues<dim>::value_list(const std::vector<Point<dim> > &points,
- std::vector<double> &values,
- const unsigned int) const
-{
- Assert(values.size()==points.size(),
- ExcDimensionMismatch(values.size(),points.size()));
- for (unsigned int i=0; i<values.size(); ++i)
- {
- if (points[i](0)<0.5)
- values[i]=1.;
- else
- values[i]=0.;
- }
-}
- // @sect3{Class: Step12}
- //
- // After this preparations, we
- // proceed with the main class of
- // this program,
- // called Step12. It is basically
- // the main class of step-6. We do
- // not have a ConstraintMatrix,
- // because there are no hanging node
- // constraints in DG discretizations.
-
- // Major differences will only come
- // up in the implementation of the
- // assemble functions, since here, we
- // not only need to cover the flux
- // integrals over faces, we also use
- // the MeshWorker interface to
- // simplify the loops involved.
-template <int dim>
-class Step12
-{
- public:
- Step12 ();
- void run ();
-
- private:
- void setup_system ();
- void assemble_system ();
- void solve (Vector<double> &solution);
- void refine_grid ();
- void output_results (const unsigned int cycle) const;
-
- Triangulation<dim> triangulation;
- const MappingQ1<dim> mapping;
-
- // Furthermore we want to use DG
- // elements of degree 1 (but this
- // is only specified in the
- // constructor). If you want to
- // use a DG method of a different
- // degree the whole program stays
- // the same, only replace 1 in
- // the constructor by the desired
- // polynomial degree.
- FE_DGQ<dim> fe;
- DoFHandler<dim> dof_handler;
-
- // The next four members represent the
- // linear system to be
- // solved. <code>system_matrix</code> and
- // <code>right_hand_side</code> are
- // generated by
- // <code>assemble_system()</code>, the
- // <code>solution</code> is computed in
- // <code>solve()</code>. The
- // <code>sparsity_pattern</code> is used
- // to determine the location of nonzero
- // elements in
- // <code>system_matrix</code>.
- SparsityPattern sparsity_pattern;
- SparseMatrix<double> system_matrix;
-
- Vector<double> solution;
- Vector<double> right_hand_side;
-
- // Finally, we have to provide
- // functions that assemble the
- // cell, boundary, and inner face
- // terms. Within the MeshWorker
- // framework, the loop over all
- // cells and much of the setup of
- // operations will be done
- // outside this class, so all we
- // have to provide are these
- // three operations. They will
- // then work on intermediate
- // objects for which first, we
- // here define typedefs to the
- // info objects handed to the
- // local integration functions in
- // order to make our life easier
- // below.
- typedef MeshWorker::DoFInfo<dim> DoFInfo;
- typedef MeshWorker::IntegrationInfo<dim> CellInfo;
-
- // The following three functions
- // are then the ones that get called
- // inside the generic loop over all
- // cells and faces. They are the
- // ones doing the actual
- // integration.
- //
- // In our code below, these
- // functions do not access member
- // variables of the current
- // class, so we can mark them as
- // <code>static</code> and simply
- // pass pointers to these
- // functions to the MeshWorker
- // framework. If, however, these
- // functions would want to access
- // member variables (or needed
- // additional arguments beyond
- // the ones specified below), we
- // could use the facilities of
- // boost::bind (or std::bind,
- // respectively) to provide the
- // MeshWorker framework with
- // objects that act as if they
- // had the required number and
- // types of arguments, but have
- // in fact other arguments
- // already bound.
- static void integrate_cell_term (DoFInfo& dinfo, CellInfo& info);
- static void integrate_boundary_term (DoFInfo& dinfo, CellInfo& info);
- static void integrate_face_term (DoFInfo& dinfo1, DoFInfo& dinfo2,
- CellInfo& info1, CellInfo& info2);
-};
-
-
- // We start with the constructor. The 1 in
- // the constructor call of <code>fe</code> is
- // the polynomial degree.
-template <int dim>
-Step12<dim>::Step12 ()
- :
- mapping (),
- fe (1),
- dof_handler (triangulation)
-{}
-
-
-template <int dim>
-void Step12<dim>::setup_system ()
+namespace Step12
{
- // In the function that sets up the usual
- // finite element data structures, we first
- // need to distribute the DoFs.
- dof_handler.distribute_dofs (fe);
-
- // We start by generating the sparsity
- // pattern. To this end, we first fill an
- // intermediate object of type
- // CompressedSparsityPattern with the
- // couplings appearing in the system. After
- // building the pattern, this object is
- // copied to <code>sparsity_pattern</code>
- // and can be discarded.
-
- // To build the sparsity pattern for DG
- // discretizations, we can call the
- // function analogue to
- // DoFTools::make_sparsity_pattern, which
- // is called
- // DoFTools::make_flux_sparsity_pattern:
- CompressedSparsityPattern c_sparsity(dof_handler.n_dofs());
- DoFTools::make_flux_sparsity_pattern (dof_handler, c_sparsity);
- sparsity_pattern.copy_from(c_sparsity);
-
- // Finally, we set up the structure
- // of all components of the linear system.
- system_matrix.reinit (sparsity_pattern);
- solution.reinit (dof_handler.n_dofs());
- right_hand_side.reinit (dof_handler.n_dofs());
-}
+ using namespace dealii;
- // @sect4{Function: assemble_system}
-
- // Here we see the major difference to
- // assembling by hand. Instead of writing
- // loops over cells and faces, we leave all
- // this to the MeshWorker framework. In order
- // to do so, we just have to define local
- // integration functions and use one of the
- // classes in namespace MeshWorker::Assembler
- // to build the global system.
-template <int dim>
-void Step12<dim>::assemble_system ()
-{
- // This is the magic object, which
- // knows everything about the data
- // structures and local
- // integration. This is the object
- // doing the work in the function
- // MeshWorker::loop(), which is
- // implicitly called by
- // MeshWorker::integration_loop()
- // below. After the functions to
- // which we provide pointers did
- // the local integration, the
- // MeshWorker::Assembler::SystemSimple
- // object distributes these into
- // the global sparse matrix and the
- // right hand side vector.
- MeshWorker::IntegrationInfoBox<dim> info_box;
-
- // First, we initialize the
- // quadrature formulae and the
- // update flags in the worker base
- // class. For quadrature, we play
- // safe and use a QGauss formula
- // with number of points one higher
- // than the polynomial degree
- // used. Since the quadratures for
- // cells, boundary and interior
- // faces can be selected
- // independently, we have to hand
- // over this value three times.
- const unsigned int n_gauss_points = dof_handler.get_fe().degree+1;
- info_box.initialize_gauss_quadrature(n_gauss_points,
- n_gauss_points,
- n_gauss_points);
-
- // These are the types of values we
- // need for integrating our
- // system. They are added to the
- // flags used on cells, boundary
- // and interior faces, as well as
- // interior neighbor faces, which is
- // forced by the four @p true
- // values.
- info_box.initialize_update_flags();
- UpdateFlags update_flags = update_quadrature_points |
- update_values |
- update_gradients;
- info_box.add_update_flags(update_flags, true, true, true, true);
-
- // After preparing all data in
- // <tt>info_box</tt>, we initialize
- // the FEValus objects in there.
- info_box.initialize(fe, mapping);
-
- // The object created so far helps
- // us do the local integration on
- // each cell and face. Now, we need
- // an object which receives the
- // integrated (local) data and
- // forwards them to the assembler.
- MeshWorker::DoFInfo<dim> dof_info(dof_handler);
-
- // Now, we have to create the
- // assembler object and tell it,
- // where to put the local
- // data. These will be our system
- // matrix and the right hand side.
- MeshWorker::Assembler::SystemSimple<SparseMatrix<double>, Vector<double> >
- assembler;
- assembler.initialize(system_matrix, right_hand_side);
-
- // Finally, the integration loop
- // over all active cells
- // (determined by the first
- // argument, which is an active
- // iterator).
+ // @sect3{Equation data}
//
- // As noted in the discussion when
- // declaring the local integration
- // functions in the class
- // declaration, the arguments
- // expected by the assembling
- // integrator class are not
- // actually function
- // pointers. Rather, they are
- // objects that can be called like
- // functions with a certain number
- // of arguments. Consequently, we
- // could also pass objects with
- // appropriate operator()
- // implementations here, or the
- // result of std::bind if the local
- // integrators were, for example,
- // non-static member functions.
- MeshWorker::integration_loop<dim, dim>
- (dof_handler.begin_active(), dof_handler.end(),
- dof_info, info_box,
- &Step12<dim>::integrate_cell_term,
- &Step12<dim>::integrate_boundary_term,
- &Step12<dim>::integrate_face_term,
- assembler, true);
-}
-
-
- // @sect4{The local integrators}
-
- // These functions are analogous to
- // step-12 and differ only in the
- // data structures. Instead of
- // providing the local matrices
- // explicitly in the argument list,
- // they are part of the info object.
-
- // Note that here we still have the
- // local integration loop inside the
- // following functions. The program
- // would be even shorter, if we used
- // pre-made operators from the
- // Operators namespace (which will be
- // added soon).
-
-template <int dim>
-void Step12<dim>::integrate_cell_term (DoFInfo& dinfo, CellInfo& info)
-{
- // First, let us retrieve some of
- // the objects used here from
- // @p info. Note that these objects
- // can handle much more complex
- // structures, thus the access here
- // looks more complicated than
- // might seem necessary.
- const FEValuesBase<dim>& fe_v = info.fe_values();
- FullMatrix<double>& local_matrix = dinfo.matrix(0).matrix;
- const std::vector<double> &JxW = fe_v.get_JxW_values ();
-
- // With these objects, we continue
- // local integration like
- // always. First, we loop over the
- // quadrature points and compute
- // the advection vector in the
- // current point.
- for (unsigned int point=0; point<fe_v.n_quadrature_points; ++point)
- {
- Point<dim> beta;
- beta(0) = -fe_v.quadrature_point(point)(1);
- beta(1) = fe_v.quadrature_point(point)(0);
- beta /= beta.norm();
-
- // We solve a homogeneous
- // equation, thus no right
- // hand side shows up in
- // the cell term.
- // What's left is
- // integrating the matrix entries.
- for (unsigned int i=0; i<fe_v.dofs_per_cell; ++i)
- for (unsigned int j=0; j<fe_v.dofs_per_cell; ++j)
- local_matrix(i,j) -= beta*fe_v.shape_grad(i,point)*
- fe_v.shape_value(j,point) *
- JxW[point];
- }
-}
-
- // Now the same for the boundary terms. Note
- // that now we use FEValuesBase, the base
- // class for both FEFaceValues and
- // FESubfaceValues, in order to get access to
- // normal vectors.
-template <int dim>
-void Step12<dim>::integrate_boundary_term (DoFInfo& dinfo, CellInfo& info)
-{
- const FEValuesBase<dim>& fe_v = info.fe_values();
- FullMatrix<double>& local_matrix = dinfo.matrix(0).matrix;
- Vector<double>& local_vector = dinfo.vector(0).block(0);
-
- const std::vector<double> &JxW = fe_v.get_JxW_values ();
- const std::vector<Point<dim> > &normals = fe_v.get_normal_vectors ();
-
- std::vector<double> g(fe_v.n_quadrature_points);
-
- static BoundaryValues<dim> boundary_function;
- boundary_function.value_list (fe_v.get_quadrature_points(), g);
-
- for (unsigned int point=0; point<fe_v.n_quadrature_points; ++point)
- {
- Point<dim> beta;
- beta(0) = -fe_v.quadrature_point(point)(1);
- beta(1) = fe_v.quadrature_point(point)(0);
- beta /= beta.norm();
-
- const double beta_n=beta * normals[point];
- if (beta_n>0)
+ // First, we define a class
+ // describing the inhomogeneous
+ // boundary data. Since only its
+ // values are used, we implement
+ // value_list(), but leave all other
+ // functions of Function undefined.
+ template <int dim>
+ class BoundaryValues: public Function<dim>
+ {
+ public:
+ BoundaryValues () {};
+ virtual void value_list (const std::vector<Point<dim> > &points,
+ std::vector<double> &values,
+ const unsigned int component=0) const;
+ };
+
+ // Given the flow direction, the inflow
+ // boundary of the unit square $[0,1]^2$ are
+ // the right and the lower boundaries. We
+ // prescribe discontinuous boundary values 1
+ // and 0 on the x-axis and value 0 on the
+ // right boundary. The values of this
+ // function on the outflow boundaries will
+ // not be used within the DG scheme.
+ template <int dim>
+ void BoundaryValues<dim>::value_list(const std::vector<Point<dim> > &points,
+ std::vector<double> &values,
+ const unsigned int) const
+ {
+ Assert(values.size()==points.size(),
+ ExcDimensionMismatch(values.size(),points.size()));
+
+ for (unsigned int i=0; i<values.size(); ++i)
+ {
+ if (points[i](0)<0.5)
+ values[i]=1.;
+ else
+ values[i]=0.;
+ }
+ }
+ // @sect3{The AdvectionProblem class}
+ //
+ // After this preparations, we
+ // proceed with the main class of
+ // this program,
+ // called AdvectionProblem. It is basically
+ // the main class of step-6. We do
+ // not have a ConstraintMatrix,
+ // because there are no hanging node
+ // constraints in DG discretizations.
+
+ // Major differences will only come
+ // up in the implementation of the
+ // assemble functions, since here, we
+ // not only need to cover the flux
+ // integrals over faces, we also use
+ // the MeshWorker interface to
+ // simplify the loops involved.
+ template <int dim>
+ class AdvectionProblem
+ {
+ public:
+ AdvectionProblem ();
+ void run ();
+
+ private:
+ void setup_system ();
+ void assemble_system ();
+ void solve (Vector<double> &solution);
+ void refine_grid ();
+ void output_results (const unsigned int cycle) const;
+
+ Triangulation<dim> triangulation;
+ const MappingQ1<dim> mapping;
+
+ // Furthermore we want to use DG
+ // elements of degree 1 (but this
+ // is only specified in the
+ // constructor). If you want to
+ // use a DG method of a different
+ // degree the whole program stays
+ // the same, only replace 1 in
+ // the constructor by the desired
+ // polynomial degree.
+ FE_DGQ<dim> fe;
+ DoFHandler<dim> dof_handler;
+
+ // The next four members represent the
+ // linear system to be
+ // solved. <code>system_matrix</code> and
+ // <code>right_hand_side</code> are
+ // generated by
+ // <code>assemble_system()</code>, the
+ // <code>solution</code> is computed in
+ // <code>solve()</code>. The
+ // <code>sparsity_pattern</code> is used
+ // to determine the location of nonzero
+ // elements in
+ // <code>system_matrix</code>.
+ SparsityPattern sparsity_pattern;
+ SparseMatrix<double> system_matrix;
+
+ Vector<double> solution;
+ Vector<double> right_hand_side;
+
+ // Finally, we have to provide
+ // functions that assemble the
+ // cell, boundary, and inner face
+ // terms. Within the MeshWorker
+ // framework, the loop over all
+ // cells and much of the setup of
+ // operations will be done
+ // outside this class, so all we
+ // have to provide are these
+ // three operations. They will
+ // then work on intermediate
+ // objects for which first, we
+ // here define typedefs to the
+ // info objects handed to the
+ // local integration functions in
+ // order to make our life easier
+ // below.
+ typedef MeshWorker::DoFInfo<dim> DoFInfo;
+ typedef MeshWorker::IntegrationInfo<dim> CellInfo;
+
+ // The following three functions
+ // are then the ones that get called
+ // inside the generic loop over all
+ // cells and faces. They are the
+ // ones doing the actual
+ // integration.
+ //
+ // In our code below, these
+ // functions do not access member
+ // variables of the current
+ // class, so we can mark them as
+ // <code>static</code> and simply
+ // pass pointers to these
+ // functions to the MeshWorker
+ // framework. If, however, these
+ // functions would want to access
+ // member variables (or needed
+ // additional arguments beyond
+ // the ones specified below), we
+ // could use the facilities of
+ // boost::bind (or std::bind,
+ // respectively) to provide the
+ // MeshWorker framework with
+ // objects that act as if they
+ // had the required number and
+ // types of arguments, but have
+ // in fact other arguments
+ // already bound.
+ static void integrate_cell_term (DoFInfo& dinfo,
+ CellInfo& info);
+ static void integrate_boundary_term (DoFInfo& dinfo,
+ CellInfo& info);
+ static void integrate_face_term (DoFInfo& dinfo1,
+ DoFInfo& dinfo2,
+ CellInfo& info1,
+ CellInfo& info2);
+ };
+
+
+ // We start with the constructor. The 1 in
+ // the constructor call of <code>fe</code> is
+ // the polynomial degree.
+ template <int dim>
+ AdvectionProblem<dim>::AdvectionProblem ()
+ :
+ mapping (),
+ fe (1),
+ dof_handler (triangulation)
+ {}
+
+
+ template <int dim>
+ void AdvectionProblem<dim>::setup_system ()
+ {
+ // In the function that sets up the usual
+ // finite element data structures, we first
+ // need to distribute the DoFs.
+ dof_handler.distribute_dofs (fe);
+
+ // We start by generating the sparsity
+ // pattern. To this end, we first fill an
+ // intermediate object of type
+ // CompressedSparsityPattern with the
+ // couplings appearing in the system. After
+ // building the pattern, this object is
+ // copied to <code>sparsity_pattern</code>
+ // and can be discarded.
+
+ // To build the sparsity pattern for DG
+ // discretizations, we can call the
+ // function analogue to
+ // DoFTools::make_sparsity_pattern, which
+ // is called
+ // DoFTools::make_flux_sparsity_pattern:
+ CompressedSparsityPattern c_sparsity(dof_handler.n_dofs());
+ DoFTools::make_flux_sparsity_pattern (dof_handler, c_sparsity);
+ sparsity_pattern.copy_from(c_sparsity);
+
+ // Finally, we set up the structure
+ // of all components of the linear system.
+ system_matrix.reinit (sparsity_pattern);
+ solution.reinit (dof_handler.n_dofs());
+ right_hand_side.reinit (dof_handler.n_dofs());
+ }
+
+ // @sect4{The assemble_system function}
+
+ // Here we see the major difference to
+ // assembling by hand. Instead of writing
+ // loops over cells and faces, we leave all
+ // this to the MeshWorker framework. In order
+ // to do so, we just have to define local
+ // integration functions and use one of the
+ // classes in namespace MeshWorker::Assembler
+ // to build the global system.
+ template <int dim>
+ void AdvectionProblem<dim>::assemble_system ()
+ {
+ // This is the magic object, which
+ // knows everything about the data
+ // structures and local
+ // integration. This is the object
+ // doing the work in the function
+ // MeshWorker::loop(), which is
+ // implicitly called by
+ // MeshWorker::integration_loop()
+ // below. After the functions to
+ // which we provide pointers did
+ // the local integration, the
+ // MeshWorker::Assembler::SystemSimple
+ // object distributes these into
+ // the global sparse matrix and the
+ // right hand side vector.
+ MeshWorker::IntegrationInfoBox<dim> info_box;
+
+ // First, we initialize the
+ // quadrature formulae and the
+ // update flags in the worker base
+ // class. For quadrature, we play
+ // safe and use a QGauss formula
+ // with number of points one higher
+ // than the polynomial degree
+ // used. Since the quadratures for
+ // cells, boundary and interior
+ // faces can be selected
+ // independently, we have to hand
+ // over this value three times.
+ const unsigned int n_gauss_points = dof_handler.get_fe().degree+1;
+ info_box.initialize_gauss_quadrature(n_gauss_points,
+ n_gauss_points,
+ n_gauss_points);
+
+ // These are the types of values we
+ // need for integrating our
+ // system. They are added to the
+ // flags used on cells, boundary
+ // and interior faces, as well as
+ // interior neighbor faces, which is
+ // forced by the four @p true
+ // values.
+ info_box.initialize_update_flags();
+ UpdateFlags update_flags = update_quadrature_points |
+ update_values |
+ update_gradients;
+ info_box.add_update_flags(update_flags, true, true, true, true);
+
+ // After preparing all data in
+ // <tt>info_box</tt>, we initialize
+ // the FEValus objects in there.
+ info_box.initialize(fe, mapping);
+
+ // The object created so far helps
+ // us do the local integration on
+ // each cell and face. Now, we need
+ // an object which receives the
+ // integrated (local) data and
+ // forwards them to the assembler.
+ MeshWorker::DoFInfo<dim> dof_info(dof_handler);
+
+ // Now, we have to create the
+ // assembler object and tell it,
+ // where to put the local
+ // data. These will be our system
+ // matrix and the right hand side.
+ MeshWorker::Assembler::SystemSimple<SparseMatrix<double>, Vector<double> >
+ assembler;
+ assembler.initialize(system_matrix, right_hand_side);
+
+ // Finally, the integration loop
+ // over all active cells
+ // (determined by the first
+ // argument, which is an active
+ // iterator).
+ //
+ // As noted in the discussion when
+ // declaring the local integration
+ // functions in the class
+ // declaration, the arguments
+ // expected by the assembling
+ // integrator class are not
+ // actually function
+ // pointers. Rather, they are
+ // objects that can be called like
+ // functions with a certain number
+ // of arguments. Consequently, we
+ // could also pass objects with
+ // appropriate operator()
+ // implementations here, or the
+ // result of std::bind if the local
+ // integrators were, for example,
+ // non-static member functions.
+ MeshWorker::integration_loop<dim, dim>
+ (dof_handler.begin_active(), dof_handler.end(),
+ dof_info, info_box,
+ &AdvectionProblem<dim>::integrate_cell_term,
+ &AdvectionProblem<dim>::integrate_boundary_term,
+ &AdvectionProblem<dim>::integrate_face_term,
+ assembler, true);
+ }
+
+
+ // @sect4{The local integrators}
+
+ // These functions are analogous to
+ // step-12 and differ only in the
+ // data structures. Instead of
+ // providing the local matrices
+ // explicitly in the argument list,
+ // they are part of the info object.
+
+ // Note that here we still have the
+ // local integration loop inside the
+ // following functions. The program
+ // would be even shorter, if we used
+ // pre-made operators from the
+ // Operators namespace (which will be
+ // added soon).
+
+ template <int dim>
+ void AdvectionProblem<dim>::integrate_cell_term (DoFInfo& dinfo,
+ CellInfo& info)
+ {
+ // First, let us retrieve some of
+ // the objects used here from
+ // @p info. Note that these objects
+ // can handle much more complex
+ // structures, thus the access here
+ // looks more complicated than
+ // might seem necessary.
+ const FEValuesBase<dim>& fe_v = info.fe_values();
+ FullMatrix<double>& local_matrix = dinfo.matrix(0).matrix;
+ const std::vector<double> &JxW = fe_v.get_JxW_values ();
+
+ // With these objects, we continue
+ // local integration like
+ // always. First, we loop over the
+ // quadrature points and compute
+ // the advection vector in the
+ // current point.
+ for (unsigned int point=0; point<fe_v.n_quadrature_points; ++point)
+ {
+ Point<dim> beta;
+ beta(0) = -fe_v.quadrature_point(point)(1);
+ beta(1) = fe_v.quadrature_point(point)(0);
+ beta /= beta.norm();
+
+ // We solve a homogeneous
+ // equation, thus no right
+ // hand side shows up in
+ // the cell term.
+ // What's left is
+ // integrating the matrix entries.
for (unsigned int i=0; i<fe_v.dofs_per_cell; ++i)
for (unsigned int j=0; j<fe_v.dofs_per_cell; ++j)
- local_matrix(i,j) += beta_n *
+ local_matrix(i,j) -= beta*fe_v.shape_grad(i,point)*
fe_v.shape_value(j,point) *
- fe_v.shape_value(i,point) *
JxW[point];
- else
- for (unsigned int i=0; i<fe_v.dofs_per_cell; ++i)
- local_vector(i) -= beta_n *
- g[point] *
- fe_v.shape_value(i,point) *
- JxW[point];
- }
-}
-
- // Finally, the interior face
- // terms. The difference here is that
- // we receive two info objects, one
- // for each cell adjacent to the face
- // and we assemble four matrices, one
- // for each cell and two for coupling
- // back and forth.
-template <int dim>
-void Step12<dim>::integrate_face_term (DoFInfo& dinfo1, DoFInfo& dinfo2,
- CellInfo& info1, CellInfo& info2)
-{
- // For quadrature points, weights,
- // etc., we use the
- // FEValuesBase object of the
- // first argument.
- const FEValuesBase<dim>& fe_v = info1.fe_values();
-
- // For additional shape functions,
- // we have to ask the neighbors
- // FEValuesBase.
- const FEValuesBase<dim>& fe_v_neighbor = info2.fe_values();
-
- // Then we get references to the
- // four local matrices. The letters
- // u and v refer to trial and test
- // functions, respectively. The
- // %numbers indicate the cells
- // provided by info1 and info2. By
- // convention, the two matrices in
- // each info object refer to the
- // test functions on the respective
- // cell. The first matrix contains the
- // interior couplings of that cell,
- // while the second contains the
- // couplings between cells.
- FullMatrix<double>& u1_v1_matrix = dinfo1.matrix(0,false).matrix;
- FullMatrix<double>& u2_v1_matrix = dinfo1.matrix(0,true).matrix;
- FullMatrix<double>& u1_v2_matrix = dinfo2.matrix(0,true).matrix;
- FullMatrix<double>& u2_v2_matrix = dinfo2.matrix(0,false).matrix;
-
- // Here, following the previous
- // functions, we would have the
- // local right hand side
- // vectors. Fortunately, the
- // interface terms only involve the
- // solution and the right hand side
- // does not receive any contributions.
-
- const std::vector<double> &JxW = fe_v.get_JxW_values ();
- const std::vector<Point<dim> > &normals = fe_v.get_normal_vectors ();
-
- for (unsigned int point=0; point<fe_v.n_quadrature_points; ++point)
- {
- Point<dim> beta;
- beta(0) = -fe_v.quadrature_point(point)(1);
- beta(1) = fe_v.quadrature_point(point)(0);
- beta /= beta.norm();
-
- const double beta_n=beta * normals[point];
- if (beta_n>0)
- {
- // This term we've already
- // seen:
+ }
+ }
+
+ // Now the same for the boundary terms. Note
+ // that now we use FEValuesBase, the base
+ // class for both FEFaceValues and
+ // FESubfaceValues, in order to get access to
+ // normal vectors.
+ template <int dim>
+ void AdvectionProblem<dim>::integrate_boundary_term (DoFInfo& dinfo,
+ CellInfo& info)
+ {
+ const FEValuesBase<dim>& fe_v = info.fe_values();
+ FullMatrix<double>& local_matrix = dinfo.matrix(0).matrix;
+ Vector<double>& local_vector = dinfo.vector(0).block(0);
+
+ const std::vector<double> &JxW = fe_v.get_JxW_values ();
+ const std::vector<Point<dim> > &normals = fe_v.get_normal_vectors ();
+
+ std::vector<double> g(fe_v.n_quadrature_points);
+
+ static BoundaryValues<dim> boundary_function;
+ boundary_function.value_list (fe_v.get_quadrature_points(), g);
+
+ for (unsigned int point=0; point<fe_v.n_quadrature_points; ++point)
+ {
+ Point<dim> beta;
+ beta(0) = -fe_v.quadrature_point(point)(1);
+ beta(1) = fe_v.quadrature_point(point)(0);
+ beta /= beta.norm();
+
+ const double beta_n=beta * normals[point];
+ if (beta_n>0)
for (unsigned int i=0; i<fe_v.dofs_per_cell; ++i)
for (unsigned int j=0; j<fe_v.dofs_per_cell; ++j)
- u1_v1_matrix(i,j) += beta_n *
- fe_v.shape_value(j,point) *
- fe_v.shape_value(i,point) *
- JxW[point];
-
- // We additionally assemble
- // the term $(\beta\cdot n
- // u,\hat v)_{\partial
- // \kappa_+}$,
- for (unsigned int k=0; k<fe_v_neighbor.dofs_per_cell; ++k)
- for (unsigned int j=0; j<fe_v.dofs_per_cell; ++j)
- u1_v2_matrix(k,j) -= beta_n *
- fe_v.shape_value(j,point) *
- fe_v_neighbor.shape_value(k,point) *
- JxW[point];
- }
- else
- {
- // This one we've already
- // seen, too:
- for (unsigned int i=0; i<fe_v.dofs_per_cell; ++i)
- for (unsigned int l=0; l<fe_v_neighbor.dofs_per_cell; ++l)
- u2_v1_matrix(i,l) += beta_n *
- fe_v_neighbor.shape_value(l,point) *
- fe_v.shape_value(i,point) *
- JxW[point];
-
- // And this is another new
- // one: $(\beta\cdot n \hat
- // u,\hat v)_{\partial
- // \kappa_-}$:
- for (unsigned int k=0; k<fe_v_neighbor.dofs_per_cell; ++k)
- for (unsigned int l=0; l<fe_v_neighbor.dofs_per_cell; ++l)
- u2_v2_matrix(k,l) -= beta_n *
- fe_v_neighbor.shape_value(l,point) *
- fe_v_neighbor.shape_value(k,point) *
+ local_matrix(i,j) += beta_n *
+ fe_v.shape_value(j,point) *
+ fe_v.shape_value(i,point) *
JxW[point];
- }
- }
-}
-
-
- // @sect3{All the rest}
- //
- // For this simple problem we use the
- // simplest possible solver, called
- // Richardson iteration, that represents a
- // simple defect correction. This, in
- // combination with a block SSOR
- // preconditioner, that uses the special
- // block matrix structure of system matrices
- // arising from DG discretizations. The size
- // of these blocks are the number of DoFs per
- // cell. Here, we use a SSOR preconditioning
- // as we have not renumbered the DoFs
- // according to the flow field. If the DoFs
- // are renumbered in the downstream direction
- // of the flow, then a block Gauss-Seidel
- // preconditioner (see the
- // PreconditionBlockSOR class with
- // relaxation=1) does a much better job.
-template <int dim>
-void Step12<dim>::solve (Vector<double> &solution)
-{
- SolverControl solver_control (1000, 1e-12);
- SolverRichardson<> solver (solver_control);
-
- // Here we create the
- // preconditioner,
- PreconditionBlockSSOR<SparseMatrix<double> > preconditioner;
-
- // then assign the matrix to it and
- // set the right block size:
- preconditioner.initialize(system_matrix, fe.dofs_per_cell);
-
- // After these preparations we are
- // ready to start the linear solver.
- solver.solve (system_matrix, solution, right_hand_side,
- preconditioner);
-}
-
-
- // We refine the grid according to a
- // very simple refinement criterion,
- // namely an approximation to the
- // gradient of the solution. As here
- // we consider the DG(1) method
- // (i.e. we use piecewise bilinear
- // shape functions) we could simply
- // compute the gradients on each
- // cell. But we do not want to base
- // our refinement indicator on the
- // gradients on each cell only, but
- // want to base them also on jumps of
- // the discontinuous solution
- // function over faces between
- // neighboring cells. The simplest
- // way of doing that is to compute
- // approximative gradients by
- // difference quotients including the
- // cell under consideration and its
- // neighbors. This is done by the
- // <code>DerivativeApproximation</code> class
- // that computes the approximate
- // gradients in a way similar to the
- // <code>GradientEstimation</code> described
- // in step-9 of this tutorial. In
- // fact, the
- // <code>DerivativeApproximation</code> class
- // was developed following the
- // <code>GradientEstimation</code> class of
- // step-9. Relating to the
- // discussion in step-9, here we
- // consider $h^{1+d/2}|\nabla_h
- // u_h|$. Furthermore we note that we
- // do not consider approximate second
- // derivatives because solutions to
- // the linear advection equation are
- // in general not in $H^2$ but in $H^1$
- // (to be more precise, in $H^1_\beta$)
- // only.
-template <int dim>
-void Step12<dim>::refine_grid ()
-{
- // The <code>DerivativeApproximation</code>
- // class computes the gradients to
- // float precision. This is
- // sufficient as they are
- // approximate and serve as
- // refinement indicators only.
- Vector<float> gradient_indicator (triangulation.n_active_cells());
-
- // Now the approximate gradients
- // are computed
- DerivativeApproximation::approximate_gradient (mapping,
- dof_handler,
- solution,
- gradient_indicator);
-
- // and they are cell-wise scaled by
- // the factor $h^{1+d/2}$
- typename DoFHandler<dim>::active_cell_iterator
- cell = dof_handler.begin_active(),
- endc = dof_handler.end();
- for (unsigned int cell_no=0; cell!=endc; ++cell, ++cell_no)
- gradient_indicator(cell_no)*=std::pow(cell->diameter(), 1+1.0*dim/2);
-
- // Finally they serve as refinement
- // indicator.
- GridRefinement::refine_and_coarsen_fixed_number (triangulation,
- gradient_indicator,
- 0.3, 0.1);
-
- triangulation.execute_coarsening_and_refinement ();
-}
-
-
- // The output of this program
- // consists of eps-files of the
- // adaptively refined grids and the
- // numerical solutions given in
- // gnuplot format. This was covered
- // in previous examples and will not
- // be further commented on.
-template <int dim>
-void Step12<dim>::output_results (const unsigned int cycle) const
-{
- // Write the grid in eps format.
- std::string filename = "grid-";
- filename += ('0' + cycle);
- Assert (cycle < 10, ExcInternalError());
-
- filename += ".eps";
- deallog << "Writing grid to <" << filename << ">" << std::endl;
- std::ofstream eps_output (filename.c_str());
-
- GridOut grid_out;
- grid_out.write_eps (triangulation, eps_output);
-
- // Output of the solution in
- // gnuplot format.
- filename = "sol-";
- filename += ('0' + cycle);
- Assert (cycle < 10, ExcInternalError());
+ else
+ for (unsigned int i=0; i<fe_v.dofs_per_cell; ++i)
+ local_vector(i) -= beta_n *
+ g[point] *
+ fe_v.shape_value(i,point) *
+ JxW[point];
+ }
+ }
+
+ // Finally, the interior face
+ // terms. The difference here is that
+ // we receive two info objects, one
+ // for each cell adjacent to the face
+ // and we assemble four matrices, one
+ // for each cell and two for coupling
+ // back and forth.
+ template <int dim>
+ void AdvectionProblem<dim>::integrate_face_term (DoFInfo& dinfo1,
+ DoFInfo& dinfo2,
+ CellInfo& info1,
+ CellInfo& info2)
+ {
+ // For quadrature points, weights,
+ // etc., we use the
+ // FEValuesBase object of the
+ // first argument.
+ const FEValuesBase<dim>& fe_v = info1.fe_values();
+
+ // For additional shape functions,
+ // we have to ask the neighbors
+ // FEValuesBase.
+ const FEValuesBase<dim>& fe_v_neighbor = info2.fe_values();
+
+ // Then we get references to the
+ // four local matrices. The letters
+ // u and v refer to trial and test
+ // functions, respectively. The
+ // %numbers indicate the cells
+ // provided by info1 and info2. By
+ // convention, the two matrices in
+ // each info object refer to the
+ // test functions on the respective
+ // cell. The first matrix contains the
+ // interior couplings of that cell,
+ // while the second contains the
+ // couplings between cells.
+ FullMatrix<double>& u1_v1_matrix = dinfo1.matrix(0,false).matrix;
+ FullMatrix<double>& u2_v1_matrix = dinfo1.matrix(0,true).matrix;
+ FullMatrix<double>& u1_v2_matrix = dinfo2.matrix(0,true).matrix;
+ FullMatrix<double>& u2_v2_matrix = dinfo2.matrix(0,false).matrix;
+
+ // Here, following the previous
+ // functions, we would have the
+ // local right hand side
+ // vectors. Fortunately, the
+ // interface terms only involve the
+ // solution and the right hand side
+ // does not receive any contributions.
+
+ const std::vector<double> &JxW = fe_v.get_JxW_values ();
+ const std::vector<Point<dim> > &normals = fe_v.get_normal_vectors ();
+
+ for (unsigned int point=0; point<fe_v.n_quadrature_points; ++point)
+ {
+ Point<dim> beta;
+ beta(0) = -fe_v.quadrature_point(point)(1);
+ beta(1) = fe_v.quadrature_point(point)(0);
+ beta /= beta.norm();
+
+ const double beta_n=beta * normals[point];
+ if (beta_n>0)
+ {
+ // This term we've already
+ // seen:
+ for (unsigned int i=0; i<fe_v.dofs_per_cell; ++i)
+ for (unsigned int j=0; j<fe_v.dofs_per_cell; ++j)
+ u1_v1_matrix(i,j) += beta_n *
+ fe_v.shape_value(j,point) *
+ fe_v.shape_value(i,point) *
+ JxW[point];
+
+ // We additionally assemble
+ // the term $(\beta\cdot n
+ // u,\hat v)_{\partial
+ // \kappa_+}$,
+ for (unsigned int k=0; k<fe_v_neighbor.dofs_per_cell; ++k)
+ for (unsigned int j=0; j<fe_v.dofs_per_cell; ++j)
+ u1_v2_matrix(k,j) -= beta_n *
+ fe_v.shape_value(j,point) *
+ fe_v_neighbor.shape_value(k,point) *
+ JxW[point];
+ }
+ else
+ {
+ // This one we've already
+ // seen, too:
+ for (unsigned int i=0; i<fe_v.dofs_per_cell; ++i)
+ for (unsigned int l=0; l<fe_v_neighbor.dofs_per_cell; ++l)
+ u2_v1_matrix(i,l) += beta_n *
+ fe_v_neighbor.shape_value(l,point) *
+ fe_v.shape_value(i,point) *
+ JxW[point];
+
+ // And this is another new
+ // one: $(\beta\cdot n \hat
+ // u,\hat v)_{\partial
+ // \kappa_-}$:
+ for (unsigned int k=0; k<fe_v_neighbor.dofs_per_cell; ++k)
+ for (unsigned int l=0; l<fe_v_neighbor.dofs_per_cell; ++l)
+ u2_v2_matrix(k,l) -= beta_n *
+ fe_v_neighbor.shape_value(l,point) *
+ fe_v_neighbor.shape_value(k,point) *
+ JxW[point];
+ }
+ }
+ }
+
+
+ // @sect3{All the rest}
+ //
+ // For this simple problem we use the
+ // simplest possible solver, called
+ // Richardson iteration, that represents a
+ // simple defect correction. This, in
+ // combination with a block SSOR
+ // preconditioner, that uses the special
+ // block matrix structure of system matrices
+ // arising from DG discretizations. The size
+ // of these blocks are the number of DoFs per
+ // cell. Here, we use a SSOR preconditioning
+ // as we have not renumbered the DoFs
+ // according to the flow field. If the DoFs
+ // are renumbered in the downstream direction
+ // of the flow, then a block Gauss-Seidel
+ // preconditioner (see the
+ // PreconditionBlockSOR class with
+ // relaxation=1) does a much better job.
+ template <int dim>
+ void AdvectionProblem<dim>::solve (Vector<double> &solution)
+ {
+ SolverControl solver_control (1000, 1e-12);
+ SolverRichardson<> solver (solver_control);
+
+ // Here we create the
+ // preconditioner,
+ PreconditionBlockSSOR<SparseMatrix<double> > preconditioner;
+
+ // then assign the matrix to it and
+ // set the right block size:
+ preconditioner.initialize(system_matrix, fe.dofs_per_cell);
+
+ // After these preparations we are
+ // ready to start the linear solver.
+ solver.solve (system_matrix, solution, right_hand_side,
+ preconditioner);
+ }
+
+
+ // We refine the grid according to a
+ // very simple refinement criterion,
+ // namely an approximation to the
+ // gradient of the solution. As here
+ // we consider the DG(1) method
+ // (i.e. we use piecewise bilinear
+ // shape functions) we could simply
+ // compute the gradients on each
+ // cell. But we do not want to base
+ // our refinement indicator on the
+ // gradients on each cell only, but
+ // want to base them also on jumps of
+ // the discontinuous solution
+ // function over faces between
+ // neighboring cells. The simplest
+ // way of doing that is to compute
+ // approximative gradients by
+ // difference quotients including the
+ // cell under consideration and its
+ // neighbors. This is done by the
+ // <code>DerivativeApproximation</code> class
+ // that computes the approximate
+ // gradients in a way similar to the
+ // <code>GradientEstimation</code> described
+ // in step-9 of this tutorial. In
+ // fact, the
+ // <code>DerivativeApproximation</code> class
+ // was developed following the
+ // <code>GradientEstimation</code> class of
+ // step-9. Relating to the
+ // discussion in step-9, here we
+ // consider $h^{1+d/2}|\nabla_h
+ // u_h|$. Furthermore we note that we
+ // do not consider approximate second
+ // derivatives because solutions to
+ // the linear advection equation are
+ // in general not in $H^2$ but in $H^1$
+ // (to be more precise, in $H^1_\beta$)
+ // only.
+ template <int dim>
+ void AdvectionProblem<dim>::refine_grid ()
+ {
+ // The <code>DerivativeApproximation</code>
+ // class computes the gradients to
+ // float precision. This is
+ // sufficient as they are
+ // approximate and serve as
+ // refinement indicators only.
+ Vector<float> gradient_indicator (triangulation.n_active_cells());
+
+ // Now the approximate gradients
+ // are computed
+ DerivativeApproximation::approximate_gradient (mapping,
+ dof_handler,
+ solution,
+ gradient_indicator);
+
+ // and they are cell-wise scaled by
+ // the factor $h^{1+d/2}$
+ typename DoFHandler<dim>::active_cell_iterator
+ cell = dof_handler.begin_active(),
+ endc = dof_handler.end();
+ for (unsigned int cell_no=0; cell!=endc; ++cell, ++cell_no)
+ gradient_indicator(cell_no)*=std::pow(cell->diameter(), 1+1.0*dim/2);
+
+ // Finally they serve as refinement
+ // indicator.
+ GridRefinement::refine_and_coarsen_fixed_number (triangulation,
+ gradient_indicator,
+ 0.3, 0.1);
+
+ triangulation.execute_coarsening_and_refinement ();
+ }
+
+
+ // The output of this program
+ // consists of eps-files of the
+ // adaptively refined grids and the
+ // numerical solutions given in
+ // gnuplot format. This was covered
+ // in previous examples and will not
+ // be further commented on.
+ template <int dim>
+ void AdvectionProblem<dim>::output_results (const unsigned int cycle) const
+ {
+ // Write the grid in eps format.
+ std::string filename = "grid-";
+ filename += ('0' + cycle);
+ Assert (cycle < 10, ExcInternalError());
+
+ filename += ".eps";
+ deallog << "Writing grid to <" << filename << ">" << std::endl;
+ std::ofstream eps_output (filename.c_str());
+
+ GridOut grid_out;
+ grid_out.write_eps (triangulation, eps_output);
+
+ // Output of the solution in
+ // gnuplot format.
+ filename = "sol-";
+ filename += ('0' + cycle);
+ Assert (cycle < 10, ExcInternalError());
+
+ filename += ".gnuplot";
+ deallog << "Writing solution to <" << filename << ">" << std::endl;
+ std::ofstream gnuplot_output (filename.c_str());
+
+ DataOut<dim> data_out;
+ data_out.attach_dof_handler (dof_handler);
+ data_out.add_data_vector (solution, "u");
+
+ data_out.build_patches ();
+
+ data_out.write_gnuplot(gnuplot_output);
+ }
+
+
+ // The following <code>run</code> function is
+ // similar to previous examples.
+ template <int dim>
+ void AdvectionProblem<dim>::run ()
+ {
+ for (unsigned int cycle=0; cycle<6; ++cycle)
+ {
+ deallog << "Cycle " << cycle << std::endl;
+
+ if (cycle == 0)
+ {
+ GridGenerator::hyper_cube (triangulation);
+
+ triangulation.refine_global (3);
+ }
+ else
+ refine_grid ();
+
+
+ deallog << "Number of active cells: "
+ << triangulation.n_active_cells()
+ << std::endl;
- filename += ".gnuplot";
- deallog << "Writing solution to <" << filename << ">" << std::endl;
- std::ofstream gnuplot_output (filename.c_str());
+ setup_system ();
- DataOut<dim> data_out;
- data_out.attach_dof_handler (dof_handler);
- data_out.add_data_vector (solution, "u");
+ deallog << "Number of degrees of freedom: "
+ << dof_handler.n_dofs()
+ << std::endl;
- data_out.build_patches ();
+ assemble_system ();
+ solve (solution);
- data_out.write_gnuplot(gnuplot_output);
+ output_results (cycle);
+ }
+ }
}
- // The following <code>run</code> function is
- // similar to previous examples.
-template <int dim>
-void Step12<dim>::run ()
-{
- for (unsigned int cycle=0; cycle<6; ++cycle)
- {
- deallog << "Cycle " << cycle << std::endl;
-
- if (cycle == 0)
- {
- GridGenerator::hyper_cube (triangulation);
-
- triangulation.refine_global (3);
- }
- else
- refine_grid ();
-
-
- deallog << "Number of active cells: "
- << triangulation.n_active_cells()
- << std::endl;
-
- setup_system ();
-
- deallog << "Number of degrees of freedom: "
- << dof_handler.n_dofs()
- << std::endl;
-
- assemble_system ();
- solve (solution);
-
- output_results (cycle);
- }
-}
-
// The following <code>main</code> function is
// similar to previous examples as well, and
// need not be commented on.
{
try
{
- Step12<2> dgmethod;
+ Step12::AdvectionProblem<2> dgmethod;
dgmethod.run ();
}
catch (std::exception &exc)
/* $Id$ */
/* */
-/* Copyright (C) 2001, 2002, 2003, 2004, 2006, 2007, 2008, 2009 by the deal.II authors */
+/* Copyright (C) 2001, 2002, 2003, 2004, 2006, 2007, 2008, 2009, 2011 by the deal.II authors */
/* */
/* This file is subject to QPL and may not be distributed */
/* without copyright and license information. Please refer */
// The last step is as in all
// previous programs:
-using namespace dealii;
-
- // @sect3{Evaluation of the solution}
-
- // As for the program itself, we
- // first define classes that evaluate
- // the solutions of a Laplace
- // equation. In fact, they can
- // evaluate every kind of solution,
- // as long as it is described by a
- // <code>DoFHandler</code> object, and a
- // solution vector. We define them
- // here first, even before the
- // classes that actually generate the
- // solution to be evaluated, since we
- // need to declare an abstract base
- // class that the solver classes can
- // refer to.
- //
- // From an abstract point of view, we
- // declare a pure base class
- // that provides an evaluation
- // operator() which will
- // do the evaluation of the solution
- // (whatever derived classes might
- // consider an <code>evaluation</code>). Since
- // this is the only real function of
- // this base class (except for some
- // bookkeeping machinery), one
- // usually terms such a class that
- // only has an <code>operator()</code> a
- // <code>functor</code> in C++ terminology,
- // since it is used just like a
- // function object.
- //
- // Objects of this functor type will
- // then later be passed to the solver
- // object, which applies it to the
- // solution just computed. The
- // evaluation objects may then
- // extract any quantity they like
- // from the solution. The advantage
- // of putting these evaluation
- // functions into a separate
- // hierarchy of classes is that by
- // design they cannot use the
- // internals of the solver object and
- // are therefore independent of
- // changes to the way the solver
- // works. Furthermore, it is trivial
- // to write another evaluation class
- // without modifying the solver
- // class, which speeds up programming
- // (not being able to use internals
- // of another class also means that
- // you do not have to worry about
- // them -- programming evaluators is
- // usually a rather quickly done
- // task), as well as compilation (if
- // solver and evaluation classes are
- // put into different files: the
- // solver only needs to see the
- // declaration of the abstract base
- // class, and therefore does not need
- // to be recompiled upon addition of
- // a new evaluation class, or
- // modification of an old one).
- // On a related note, you can reuse
- // the evaluation classes for other
- // projects, solving different
- // equations.
- //
- // In order to improve separation of
- // code into different modules, we
- // put the evaluation classes into a
- // namespace of their own. This makes
- // it easier to actually solve
- // different equations in the same
- // program, by assembling it from
- // existing building blocks. The
- // reason for this is that classes
- // for similar purposes tend to have
- // the same name, although they were
- // developed in different
- // contexts. In order to be able to
- // use them together in one program,
- // it is necessary that they are
- // placed in different
- // namespaces. This we do here:
-namespace Evaluation
+namespace Step13
{
-
- // Now for the abstract base class
- // of evaluation classes: its main
- // purpose is to declare a pure
- // virtual function <code>operator()</code>
- // taking a <code>DoFHandler</code> object,
- // and the solution vector. In
- // order to be able to use pointers
- // to this base class only, it also
- // has to declare a virtual
- // destructor, which however does
- // nothing. Besides this, it only
- // provides for a little bit of
- // bookkeeping: since we usually
- // want to evaluate solutions on
- // subsequent refinement levels, we
- // store the number of the present
- // refinement cycle, and provide a
- // function to change this number.
- template <int dim>
- class EvaluationBase
+ using namespace dealii;
+
+ // @sect3{Evaluation of the solution}
+
+ // As for the program itself, we
+ // first define classes that evaluate
+ // the solutions of a Laplace
+ // equation. In fact, they can
+ // evaluate every kind of solution,
+ // as long as it is described by a
+ // <code>DoFHandler</code> object, and a
+ // solution vector. We define them
+ // here first, even before the
+ // classes that actually generate the
+ // solution to be evaluated, since we
+ // need to declare an abstract base
+ // class that the solver classes can
+ // refer to.
+ //
+ // From an abstract point of view, we
+ // declare a pure base class
+ // that provides an evaluation
+ // operator() which will
+ // do the evaluation of the solution
+ // (whatever derived classes might
+ // consider an <code>evaluation</code>). Since
+ // this is the only real function of
+ // this base class (except for some
+ // bookkeeping machinery), one
+ // usually terms such a class that
+ // only has an <code>operator()</code> a
+ // <code>functor</code> in C++ terminology,
+ // since it is used just like a
+ // function object.
+ //
+ // Objects of this functor type will
+ // then later be passed to the solver
+ // object, which applies it to the
+ // solution just computed. The
+ // evaluation objects may then
+ // extract any quantity they like
+ // from the solution. The advantage
+ // of putting these evaluation
+ // functions into a separate
+ // hierarchy of classes is that by
+ // design they cannot use the
+ // internals of the solver object and
+ // are therefore independent of
+ // changes to the way the solver
+ // works. Furthermore, it is trivial
+ // to write another evaluation class
+ // without modifying the solver
+ // class, which speeds up programming
+ // (not being able to use internals
+ // of another class also means that
+ // you do not have to worry about
+ // them -- programming evaluators is
+ // usually a rather quickly done
+ // task), as well as compilation (if
+ // solver and evaluation classes are
+ // put into different files: the
+ // solver only needs to see the
+ // declaration of the abstract base
+ // class, and therefore does not need
+ // to be recompiled upon addition of
+ // a new evaluation class, or
+ // modification of an old one).
+ // On a related note, you can reuse
+ // the evaluation classes for other
+ // projects, solving different
+ // equations.
+ //
+ // In order to improve separation of
+ // code into different modules, we
+ // put the evaluation classes into a
+ // namespace of their own. This makes
+ // it easier to actually solve
+ // different equations in the same
+ // program, by assembling it from
+ // existing building blocks. The
+ // reason for this is that classes
+ // for similar purposes tend to have
+ // the same name, although they were
+ // developed in different
+ // contexts. In order to be able to
+ // use them together in one program,
+ // it is necessary that they are
+ // placed in different
+ // namespaces. This we do here:
+ namespace Evaluation
{
- public:
- virtual ~EvaluationBase ();
-
- 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;
- };
+ // Now for the abstract base class
+ // of evaluation classes: its main
+ // purpose is to declare a pure
+ // virtual function <code>operator()</code>
+ // taking a <code>DoFHandler</code> object,
+ // and the solution vector. In
+ // order to be able to use pointers
+ // to this base class only, it also
+ // has to declare a virtual
+ // destructor, which however does
+ // nothing. Besides this, it only
+ // provides for a little bit of
+ // bookkeeping: since we usually
+ // want to evaluate solutions on
+ // subsequent refinement levels, we
+ // store the number of the present
+ // refinement cycle, and provide a
+ // function to change this number.
+ template <int dim>
+ class EvaluationBase
+ {
+ public:
+ virtual ~EvaluationBase ();
- // After the declaration has been
- // discussed above, the
- // implementation is rather
- // straightforward:
- template <int dim>
- EvaluationBase<dim>::~EvaluationBase ()
- {}
-
+ void set_refinement_cycle (const unsigned int refinement_cycle);
-
- template <int dim>
- void
- EvaluationBase<dim>::set_refinement_cycle (const unsigned int step)
- {
- refinement_cycle = step;
- }
+ virtual void operator () (const DoFHandler<dim> &dof_handler,
+ const Vector<double> &solution) const = 0;
+ protected:
+ unsigned int refinement_cycle;
+ };
- // @sect4{%Point evaluation}
+ // After the declaration has been
+ // discussed above, the
+ // implementation is rather
+ // straightforward:
+ template <int dim>
+ EvaluationBase<dim>::~EvaluationBase ()
+ {}
- // The next thing is to implement
- // actual evaluation classes. As
- // noted in the introduction, we'd
- // like to extract a point value
- // from the solution, so the first
- // class does this in its
- // <code>operator()</code>. The actual point
- // is given to this class through
- // the constructor, as well as a
- // table object into which it will
- // put its findings.
- //
- // Finding out the value of a
- // finite element field at an
- // arbitrary point is rather
- // difficult, if we cannot rely on
- // knowing the actual finite
- // element used, since then we
- // cannot, for example, interpolate
- // between nodes. For simplicity,
- // we therefore assume here that
- // the point at which we want to
- // evaluate the field is actually a
- // node. If, in the process of
- // evaluating the solution, we find
- // that we did not encounter this
- // point upon looping over all
- // vertices, we then have to throw
- // an exception in order to signal
- // to the calling functions that
- // something has gone wrong, rather
- // than silently ignore this error.
- //
- // In the step-9 example program,
- // we have already seen how such an
- // exception class can be declared,
- // using the <code>DeclExceptionN</code>
- // macros. We use this mechanism
- // here again.
- //
- // From this, the actual
- // declaration of this class should
- // be evident. Note that of course
- // even if we do not list a
- // destructor explicitely, an
- // implicit destructor is generated
- // from the compiler, and it is
- // virtual just as the one of the
- // base class.
- template <int dim>
- class PointValueEvaluation : public EvaluationBase<dim>
- {
- public:
- PointValueEvaluation (const Point<dim> &evaluation_point,
- TableHandler &results_table);
-
- 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;
- TableHandler &results_table;
- };
- // As for the definition, the
- // constructor is trivial, just
- // taking data and storing it in
- // object-local ones:
- template <int dim>
- PointValueEvaluation<dim>::
- PointValueEvaluation (const Point<dim> &evaluation_point,
- TableHandler &results_table)
- :
- evaluation_point (evaluation_point),
- results_table (results_table)
- {}
-
-
-
- // Now for the function that is
- // mainly of interest in this
- // class, the computation of the
- // point value:
- template <int dim>
- void
- PointValueEvaluation<dim>::
- operator () (const DoFHandler<dim> &dof_handler,
- const Vector<double> &solution) const
- {
- // First allocate a variable that
- // will hold the point
- // value. Initialize it with a
- // value that is clearly bogus,
- // so that if we fail to set it
- // to a reasonable value, we will
- // note at once. This may not be
- // necessary in a function as
- // small as this one, since we
- // can easily see all possible
- // paths of execution here, but
- // it proved to be helpful for
- // more complex cases, and so we
- // employ this strategy here as
- // well.
- double point_value = 1e20;
-
- // Then loop over all cells and
- // all their vertices, and check
- // whether a vertex matches the
- // evaluation point. If this is
- // the case, then extract the
- // point value, set a flag that
- // we have found the point of
- // interest, and exit the loop.
- typename DoFHandler<dim>::active_cell_iterator
- cell = dof_handler.begin_active(),
- endc = dof_handler.end();
- bool evaluation_point_found = false;
- for (; (cell!=endc) && !evaluation_point_found; ++cell)
- for (unsigned int vertex=0;
- vertex<GeometryInfo<dim>::vertices_per_cell;
- ++vertex)
- if (cell->vertex(vertex) == evaluation_point)
- {
- // In order to extract
- // the point value from
- // the global solution
- // vector, pick that
- // component that belongs
- // to the vertex of
- // interest, and, in case
- // the solution is
- // vector-valued, take
- // the first component of
- // it:
- point_value = solution(cell->vertex_dof_index(vertex,0));
- // Note that by this we
- // have made an
- // assumption that is not
- // valid always and
- // should be documented
- // in the class
- // declaration if this
- // were code for a real
- // application rather
- // than a tutorial
- // program: we assume
- // that the finite
- // element used for the
- // solution we try to
- // evaluate actually has
- // degrees of freedom
- // associated with
- // vertices. This, for
- // example, does not hold
- // for discontinuous
- // elements, were the
- // support points for the
- // shape functions
- // happen to be located
- // at the vertices, but
- // are not associated
- // with the vertices but
- // rather with the cell
- // interior, since
- // association with
- // vertices would imply
- // continuity there. It
- // would also not hold
- // for edge oriented
- // elements, and the
- // like.
- //
- // Ideally, we would
- // check this at the
- // beginning of the
- // function, for example
- // by a statement like
- // <code>Assert
- // (dof_handler.get_fe().dofs_per_vertex
- // @> 0,
- // ExcNotImplemented())</code>,
- // which should make it
- // quite clear what is
- // going wrong when the
- // exception is
- // triggered. In this
- // case, we omit it
- // (which is indeed bad
- // style), but knowing
- // that that does not
- // hurt here, since the
- // statement
- // <code>cell-@>vertex_dof_index(vertex,0)</code>
- // would fail if we asked
- // it to give us the DoF
- // index of a vertex if
- // there were none.
- //
- // We stress again that
- // this restriction on
- // the allowed finite
- // elements should be
- // stated in the class
- // documentation.
-
- // Since we found the
- // right point, we now
- // set the respective
- // flag and exit the
- // innermost loop. The
- // outer loop will the
- // also be terminated due
- // to the set flag.
- evaluation_point_found = true;
- break;
- };
+ template <int dim>
+ void
+ EvaluationBase<dim>::set_refinement_cycle (const unsigned int step)
+ {
+ refinement_cycle = step;
+ }
- // Finally, we'd like to make
- // sure that we have indeed found
- // the evaluation point, since if
- // that were not so we could not
- // give a reasonable value of the
- // solution there and the rest of
- // the computations were useless
- // anyway. So make sure through
- // the <code>AssertThrow</code> macro
- // already used in the step-9
- // program that we have indeed
- // found this point. If this is
- // not so, the macro throws an
- // exception of the type that is
- // given to it as second
- // argument, but compared to a
- // straightforward <code>throw</code>
- // statement, it fills the
- // exception object with a set of
- // additional information, for
- // example the source file and
- // line number where the
- // exception was generated, and
- // the condition that failed. If
- // you have a <code>catch</code> clause in
- // your main function (as this
- // program has), you will catch
- // all exceptions that are not
- // caught somewhere in between
- // and thus already handled, and
- // this additional information
- // will help you find out what
- // happened and where it went
- // wrong.
- AssertThrow (evaluation_point_found,
- ExcEvaluationPointNotFound(evaluation_point));
- // Note that we have used the
- // <code>Assert</code> macro in other
- // example programs as well. It
- // differed from the
- // <code>AssertThrow</code> macro used
- // here in that it simply aborts
- // the program, rather than
- // throwing an exception, and
- // that it did so only in debug
- // mode. It was the right macro
- // to use to check about the size
- // of vectors passed as arguments
- // to functions, and the like.
+
+ // @sect4{%Point evaluation}
+
+ // The next thing is to implement
+ // actual evaluation classes. As
+ // noted in the introduction, we'd
+ // like to extract a point value
+ // from the solution, so the first
+ // class does this in its
+ // <code>operator()</code>. The actual point
+ // is given to this class through
+ // the constructor, as well as a
+ // table object into which it will
+ // put its findings.
//
- // However, here the situation is
- // different: whether we find the
- // evaluation point or not may
- // change from refinement to
- // refinement (for example, if
- // the four cells around point
- // are coarsened away, then the
- // point may vanish after
- // refinement and
- // coarsening). This is something
- // that cannot be predicted from
- // a few number of runs of the
- // program in debug mode, but
- // should be checked always, also
- // in production runs. Thus the
- // use of the <code>AssertThrow</code>
- // macro here.
-
- // Now, if we are sure that we
- // have found the evaluation
- // point, we can add the results
- // into the table of results:
- results_table.add_value ("DoFs", dof_handler.n_dofs());
- results_table.add_value ("u(x_0)", point_value);
- }
+ // Finding out the value of a
+ // finite element field at an
+ // arbitrary point is rather
+ // difficult, if we cannot rely on
+ // knowing the actual finite
+ // element used, since then we
+ // cannot, for example, interpolate
+ // between nodes. For simplicity,
+ // we therefore assume here that
+ // the point at which we want to
+ // evaluate the field is actually a
+ // node. If, in the process of
+ // evaluating the solution, we find
+ // that we did not encounter this
+ // point upon looping over all
+ // vertices, we then have to throw
+ // an exception in order to signal
+ // to the calling functions that
+ // something has gone wrong, rather
+ // than silently ignore this error.
+ //
+ // In the step-9 example program,
+ // we have already seen how such an
+ // exception class can be declared,
+ // using the <code>DeclExceptionN</code>
+ // macros. We use this mechanism
+ // here again.
+ //
+ // From this, the actual
+ // declaration of this class should
+ // be evident. Note that of course
+ // even if we do not list a
+ // destructor explicitely, an
+ // implicit destructor is generated
+ // from the compiler, and it is
+ // virtual just as the one of the
+ // base class.
+ template <int dim>
+ class PointValueEvaluation : public EvaluationBase<dim>
+ {
+ public:
+ PointValueEvaluation (const Point<dim> &evaluation_point,
+ TableHandler &results_table);
+
+ 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;
+ TableHandler &results_table;
+ };
+ // As for the definition, the
+ // constructor is trivial, just
+ // taking data and storing it in
+ // object-local ones:
+ template <int dim>
+ PointValueEvaluation<dim>::
+ PointValueEvaluation (const Point<dim> &evaluation_point,
+ TableHandler &results_table)
+ :
+ evaluation_point (evaluation_point),
+ results_table (results_table)
+ {}
+
+
+
+ // Now for the function that is
+ // mainly of interest in this
+ // class, the computation of the
+ // point value:
+ template <int dim>
+ void
+ PointValueEvaluation<dim>::
+ operator () (const DoFHandler<dim> &dof_handler,
+ const Vector<double> &solution) const
+ {
+ // First allocate a variable that
+ // will hold the point
+ // value. Initialize it with a
+ // value that is clearly bogus,
+ // so that if we fail to set it
+ // to a reasonable value, we will
+ // note at once. This may not be
+ // necessary in a function as
+ // small as this one, since we
+ // can easily see all possible
+ // paths of execution here, but
+ // it proved to be helpful for
+ // more complex cases, and so we
+ // employ this strategy here as
+ // well.
+ double point_value = 1e20;
+
+ // Then loop over all cells and
+ // all their vertices, and check
+ // whether a vertex matches the
+ // evaluation point. If this is
+ // the case, then extract the
+ // point value, set a flag that
+ // we have found the point of
+ // interest, and exit the loop.
+ typename DoFHandler<dim>::active_cell_iterator
+ cell = dof_handler.begin_active(),
+ endc = dof_handler.end();
+ bool evaluation_point_found = false;
+ for (; (cell!=endc) && !evaluation_point_found; ++cell)
+ for (unsigned int vertex=0;
+ vertex<GeometryInfo<dim>::vertices_per_cell;
+ ++vertex)
+ if (cell->vertex(vertex) == evaluation_point)
+ {
+ // In order to extract
+ // the point value from
+ // the global solution
+ // vector, pick that
+ // component that belongs
+ // to the vertex of
+ // interest, and, in case
+ // the solution is
+ // vector-valued, take
+ // the first component of
+ // it:
+ point_value = solution(cell->vertex_dof_index(vertex,0));
+ // Note that by this we
+ // have made an
+ // assumption that is not
+ // valid always and
+ // should be documented
+ // in the class
+ // declaration if this
+ // were code for a real
+ // application rather
+ // than a tutorial
+ // program: we assume
+ // that the finite
+ // element used for the
+ // solution we try to
+ // evaluate actually has
+ // degrees of freedom
+ // associated with
+ // vertices. This, for
+ // example, does not hold
+ // for discontinuous
+ // elements, were the
+ // support points for the
+ // shape functions
+ // happen to be located
+ // at the vertices, but
+ // are not associated
+ // with the vertices but
+ // rather with the cell
+ // interior, since
+ // association with
+ // vertices would imply
+ // continuity there. It
+ // would also not hold
+ // for edge oriented
+ // elements, and the
+ // like.
+ //
+ // Ideally, we would
+ // check this at the
+ // beginning of the
+ // function, for example
+ // by a statement like
+ // <code>Assert
+ // (dof_handler.get_fe().dofs_per_vertex
+ // @> 0,
+ // ExcNotImplemented())</code>,
+ // which should make it
+ // quite clear what is
+ // going wrong when the
+ // exception is
+ // triggered. In this
+ // case, we omit it
+ // (which is indeed bad
+ // style), but knowing
+ // that that does not
+ // hurt here, since the
+ // statement
+ // <code>cell-@>vertex_dof_index(vertex,0)</code>
+ // would fail if we asked
+ // it to give us the DoF
+ // index of a vertex if
+ // there were none.
+ //
+ // We stress again that
+ // this restriction on
+ // the allowed finite
+ // elements should be
+ // stated in the class
+ // documentation.
+
+ // Since we found the
+ // right point, we now
+ // set the respective
+ // flag and exit the
+ // innermost loop. The
+ // outer loop will the
+ // also be terminated due
+ // to the set flag.
+ evaluation_point_found = true;
+ break;
+ };
+
+ // Finally, we'd like to make
+ // sure that we have indeed found
+ // the evaluation point, since if
+ // that were not so we could not
+ // give a reasonable value of the
+ // solution there and the rest of
+ // the computations were useless
+ // anyway. So make sure through
+ // the <code>AssertThrow</code> macro
+ // already used in the step-9
+ // program that we have indeed
+ // found this point. If this is
+ // not so, the macro throws an
+ // exception of the type that is
+ // given to it as second
+ // argument, but compared to a
+ // straightforward <code>throw</code>
+ // statement, it fills the
+ // exception object with a set of
+ // additional information, for
+ // example the source file and
+ // line number where the
+ // exception was generated, and
+ // the condition that failed. If
+ // you have a <code>catch</code> clause in
+ // your main function (as this
+ // program has), you will catch
+ // all exceptions that are not
+ // caught somewhere in between
+ // and thus already handled, and
+ // this additional information
+ // will help you find out what
+ // happened and where it went
+ // wrong.
+ AssertThrow (evaluation_point_found,
+ ExcEvaluationPointNotFound(evaluation_point));
+ // Note that we have used the
+ // <code>Assert</code> macro in other
+ // example programs as well. It
+ // differed from the
+ // <code>AssertThrow</code> macro used
+ // here in that it simply aborts
+ // the program, rather than
+ // throwing an exception, and
+ // that it did so only in debug
+ // mode. It was the right macro
+ // to use to check about the size
+ // of vectors passed as arguments
+ // to functions, and the like.
+ //
+ // However, here the situation is
+ // different: whether we find the
+ // evaluation point or not may
+ // change from refinement to
+ // refinement (for example, if
+ // the four cells around point
+ // are coarsened away, then the
+ // point may vanish after
+ // refinement and
+ // coarsening). This is something
+ // that cannot be predicted from
+ // a few number of runs of the
+ // program in debug mode, but
+ // should be checked always, also
+ // in production runs. Thus the
+ // use of the <code>AssertThrow</code>
+ // macro here.
+
+ // Now, if we are sure that we
+ // have found the evaluation
+ // point, we can add the results
+ // into the table of results:
+ results_table.add_value ("DoFs", dof_handler.n_dofs());
+ results_table.add_value ("u(x_0)", point_value);
+ }
- // @sect4{Generating output}
-
- // A different, maybe slightly odd
- // kind of <code>evaluation</code> of a
- // solution is to output it to a
- // file in a graphical
- // format. Since in the evaluation
- // functions we are given a
- // <code>DoFHandler</code> object and the
- // solution vector, we have all we
- // need to do this, so we can do it
- // in an evaluation class. The
- // reason for actually doing so
- // instead of putting it into the
- // class that computed the solution
- // is that this way we have more
- // flexibility: if we choose to
- // only output certain aspects of
- // it, or not output it at all. In
- // any case, we do not need to
- // modify the solver class, we just
- // have to modify one of the
- // modules out of which we build
- // this program. This form of
- // encapsulation, as above, helps
- // us to keep each part of the
- // program rather simple as the
- // interfaces are kept simple, and
- // no access to hidden data is
- // possible.
- //
- // Since this class which generates
- // the output is derived from the
- // common <code>EvaluationBase</code> base
- // class, its main interface is the
- // <code>operator()</code>
- // function. Furthermore, it has a
- // constructor taking a string that
- // will be used as the base part of
- // the file name to which output
- // will be sent (we will augment it
- // by a number indicating the
- // number of the refinement cycle
- // -- the base class has this
- // information at hand --, and a
- // suffix), and the constructor
- // also takes a value that
- // indicates which format is
- // requested, i.e. for which
- // graphics program we shall
- // generate output (from this we
- // will then also generate the
- // suffix of the filename to which
- // we write).
- //
- // Regarding the output format, the
- // <code>DataOutInterface</code> class
- // (which is a base class of
- // <code>DataOut</code> through which we
- // will access its fields) provides
- // an enumeration field
- // <code>OutputFormat</code>, which lists
- // names for all supported output
- // formats. At the time of writing
- // of this program, the supported
- // graphics formats are represented
- // by the enum values <code>ucd</code>,
- // <code>gnuplot</code>, <code>povray</code>,
- // <code>eps</code>, <code>gmv</code>, <code>tecplot</code>,
- // <code>tecplot_binary</code>, <code>dx</code>, and
- // <code>vtk</code>, but this list will
- // certainly grow over time. Now,
- // within various functions of that
- // base class, you can use values
- // of this type to get information
- // about these graphics formats
- // (for example the default suffix
- // used for files of each format),
- // and you can call a generic
- // <code>write</code> function, which then
- // branches to the
- // <code>write_gnuplot</code>,
- // <code>write_ucd</code>, etc functions
- // which we have used in previous
- // examples already, based on the
- // value of a second argument given
- // to it denoting the required
- // output format. This mechanism
- // makes it simple to write an
- // extensible program that can
- // decide which output format to
- // use at runtime, and it also
- // makes it rather simple to write
- // the program in a way such that
- // it takes advantage of newly
- // implemented output formats,
- // without the need to change the
- // application program.
- //
- // Of these two fields, the base
- // name and the output format
- // descriptor, the constructor
- // takes values and stores them for
- // later use by the actual
- // evaluation function.
- template <int dim>
- class SolutionOutput : public EvaluationBase<dim>
- {
- public:
- SolutionOutput (const std::string &output_name_base,
- const typename DataOut<dim>::OutputFormat output_format);
-
- virtual void operator () (const DoFHandler<dim> &dof_handler,
- const Vector<double> &solution) const;
- private:
- const std::string output_name_base;
- const typename DataOut<dim>::OutputFormat output_format;
- };
- template <int dim>
- SolutionOutput<dim>::
- SolutionOutput (const std::string &output_name_base,
- const typename DataOut<dim>::OutputFormat output_format)
- :
- output_name_base (output_name_base),
- output_format (output_format)
- {}
-
-
- // After the description above, the
- // function generating the actual
- // output is now relatively
- // straightforward. The only
- // particularly interesting feature
- // over previous example programs
- // is the use of the
- // <code>DataOut::default_suffix</code>
- // function, returning the usual
- // suffix for files of a given
- // format (e.g. ".eps" for
- // encapsulated postscript files,
- // ".gnuplot" for Gnuplot files),
- // and of the generic
- // <code>DataOut::write</code> function with
- // a second argument, which
- // branches to the actual output
- // functions for the different
- // graphics formats, based on the
- // value of the format descriptor
- // passed as second argument.
- //
- // Also note that we have to prefix
- // <code>this-@></code> to access a member
- // variable of the template
- // dependent base class. The reason
- // here, and further down in the
- // program is the same as the one
- // described in the step-7 example
- // program (look for <code>two-stage
- // name lookup</code> there).
- template <int dim>
- void
- SolutionOutput<dim>::operator () (const DoFHandler<dim> &dof_handler,
- const Vector<double> &solution) const
- {
- DataOut<dim> data_out;
- data_out.attach_dof_handler (dof_handler);
- data_out.add_data_vector (solution, "solution");
- data_out.build_patches ();
-
- std::ostringstream filename;
- filename << output_name_base << "-"
- << this->refinement_cycle
- << data_out.default_suffix (output_format)
- << std::ends;
- std::ofstream out (filename.str().c_str());
-
- data_out.write (out, output_format);
- }
+ // @sect4{Generating output}
+
+ // A different, maybe slightly odd
+ // kind of <code>evaluation</code> of a
+ // solution is to output it to a
+ // file in a graphical
+ // format. Since in the evaluation
+ // functions we are given a
+ // <code>DoFHandler</code> object and the
+ // solution vector, we have all we
+ // need to do this, so we can do it
+ // in an evaluation class. The
+ // reason for actually doing so
+ // instead of putting it into the
+ // class that computed the solution
+ // is that this way we have more
+ // flexibility: if we choose to
+ // only output certain aspects of
+ // it, or not output it at all. In
+ // any case, we do not need to
+ // modify the solver class, we just
+ // have to modify one of the
+ // modules out of which we build
+ // this program. This form of
+ // encapsulation, as above, helps
+ // us to keep each part of the
+ // program rather simple as the
+ // interfaces are kept simple, and
+ // no access to hidden data is
+ // possible.
+ //
+ // Since this class which generates
+ // the output is derived from the
+ // common <code>EvaluationBase</code> base
+ // class, its main interface is the
+ // <code>operator()</code>
+ // function. Furthermore, it has a
+ // constructor taking a string that
+ // will be used as the base part of
+ // the file name to which output
+ // will be sent (we will augment it
+ // by a number indicating the
+ // number of the refinement cycle
+ // -- the base class has this
+ // information at hand --, and a
+ // suffix), and the constructor
+ // also takes a value that
+ // indicates which format is
+ // requested, i.e. for which
+ // graphics program we shall
+ // generate output (from this we
+ // will then also generate the
+ // suffix of the filename to which
+ // we write).
+ //
+ // Regarding the output format, the
+ // <code>DataOutInterface</code> class
+ // (which is a base class of
+ // <code>DataOut</code> through which we
+ // will access its fields) provides
+ // an enumeration field
+ // <code>OutputFormat</code>, which lists
+ // names for all supported output
+ // formats. At the time of writing
+ // of this program, the supported
+ // graphics formats are represented
+ // by the enum values <code>ucd</code>,
+ // <code>gnuplot</code>, <code>povray</code>,
+ // <code>eps</code>, <code>gmv</code>, <code>tecplot</code>,
+ // <code>tecplot_binary</code>, <code>dx</code>, and
+ // <code>vtk</code>, but this list will
+ // certainly grow over time. Now,
+ // within various functions of that
+ // base class, you can use values
+ // of this type to get information
+ // about these graphics formats
+ // (for example the default suffix
+ // used for files of each format),
+ // and you can call a generic
+ // <code>write</code> function, which then
+ // branches to the
+ // <code>write_gnuplot</code>,
+ // <code>write_ucd</code>, etc functions
+ // which we have used in previous
+ // examples already, based on the
+ // value of a second argument given
+ // to it denoting the required
+ // output format. This mechanism
+ // makes it simple to write an
+ // extensible program that can
+ // decide which output format to
+ // use at runtime, and it also
+ // makes it rather simple to write
+ // the program in a way such that
+ // it takes advantage of newly
+ // implemented output formats,
+ // without the need to change the
+ // application program.
+ //
+ // Of these two fields, the base
+ // name and the output format
+ // descriptor, the constructor
+ // takes values and stores them for
+ // later use by the actual
+ // evaluation function.
+ template <int dim>
+ class SolutionOutput : public EvaluationBase<dim>
+ {
+ public:
+ SolutionOutput (const std::string &output_name_base,
+ const typename DataOut<dim>::OutputFormat output_format);
+
+ virtual void operator () (const DoFHandler<dim> &dof_handler,
+ const Vector<double> &solution) const;
+ private:
+ const std::string output_name_base;
+ const typename DataOut<dim>::OutputFormat output_format;
+ };
+ template <int dim>
+ SolutionOutput<dim>::
+ SolutionOutput (const std::string &output_name_base,
+ const typename DataOut<dim>::OutputFormat output_format)
+ :
+ output_name_base (output_name_base),
+ output_format (output_format)
+ {}
+
+
+ // After the description above, the
+ // function generating the actual
+ // output is now relatively
+ // straightforward. The only
+ // particularly interesting feature
+ // over previous example programs
+ // is the use of the
+ // <code>DataOut::default_suffix</code>
+ // function, returning the usual
+ // suffix for files of a given
+ // format (e.g. ".eps" for
+ // encapsulated postscript files,
+ // ".gnuplot" for Gnuplot files),
+ // and of the generic
+ // <code>DataOut::write</code> function with
+ // a second argument, which
+ // branches to the actual output
+ // functions for the different
+ // graphics formats, based on the
+ // value of the format descriptor
+ // passed as second argument.
+ //
+ // Also note that we have to prefix
+ // <code>this-@></code> to access a member
+ // variable of the template
+ // dependent base class. The reason
+ // here, and further down in the
+ // program is the same as the one
+ // described in the step-7 example
+ // program (look for <code>two-stage
+ // name lookup</code> there).
+ template <int dim>
+ void
+ SolutionOutput<dim>::operator () (const DoFHandler<dim> &dof_handler,
+ const Vector<double> &solution) const
+ {
+ DataOut<dim> data_out;
+ data_out.attach_dof_handler (dof_handler);
+ data_out.add_data_vector (solution, "solution");
+ data_out.build_patches ();
+
+ std::ostringstream filename;
+ filename << output_name_base << "-"
+ << this->refinement_cycle
+ << data_out.default_suffix (output_format)
+ << std::ends;
+ std::ofstream out (filename.str().c_str());
+
+ data_out.write (out, output_format);
+ }
- // @sect4{Other evaluations}
-
- // In practical applications, one
- // would add here a list of other
- // possible evaluation classes,
- // representing quantities that one
- // may be interested in. For this
- // example, that much shall be
- // sufficient, so we close the
- // namespace.
-}
-
- // @sect3{The Laplace solver classes}
-
- // After defining what we want to
- // know of the solution, we should
- // now care how to get at it. We will
- // pack everything we need into a
- // namespace of its own, for much the
- // same reasons as for the
- // evaluations above.
- //
- // Since we have discussed Laplace
- // solvers already in considerable
- // detail in previous examples, there
- // is not much new stuff
- // following. Rather, we have to a
- // great extent cannibalized previous
- // examples and put them, in slightly
- // different form, into this example
- // program. We will therefore mostly
- // be concerned with discussing the
- // differences to previous examples.
- //
- // Basically, as already said in the
- // introduction, the lack of new
- // stuff in this example is
- // deliberate, as it is more to
- // demonstrate software design
- // practices, rather than
- // mathematics. The emphasis in
- // explanations below will therefore
- // be more on the actual
- // implementation.
-namespace LaplaceSolver
-{
- // @sect4{An abstract base class}
-
- // In defining a Laplace solver, we
- // start out by declaring an
- // abstract base class, that has no
- // functionality itself except for
- // taking and storing a pointer to
- // the triangulation to be used
- // later.
- //
- // This base class is very general,
- // and could as well be used for
- // any other stationary problem. It
- // provides declarations of
- // functions that shall, in derived
- // classes, solve a problem,
- // postprocess the solution with a
- // list of evaluation objects, and
- // refine the grid,
- // respectively. None of these
- // functions actually does
- // something itself in the base
- // class.
- //
- // Due to the lack of actual
- // functionality, the programming
- // style of declaring very abstract
- // base classes reminds of the
- // style used in Smalltalk or Java
- // programs, where all classes are
- // derived from entirely abstract
- // classes <code>Object</code>, even number
- // representations. The author
- // admits that he does not
- // particularly like the use of
- // such a style in C++, as it puts
- // style over reason. Furthermore,
- // it promotes the use of virtual
- // functions for everything (for
- // example, in Java, all functions
- // are virtual per se), which,
- // however, has proven to be rather
- // inefficient in many applications
- // where functions are often only
- // accessing data, not doing
- // computations, and therefore
- // quickly return; the overhead of
- // virtual functions can then be
- // significant. The opinion of the
- // author is to have abstract base
- // classes wherever at least some
- // part of the code of actual
- // implementations can be shared
- // and thus separated into the base
- // class.
- //
- // Besides all these theoretical
- // questions, we here have a good
- // reason, which will become
- // clearer to the reader
- // below. Basically, we want to be
- // able to have a family of
- // different Laplace solvers that
- // differ so much that no larger
- // common subset of functionality
- // could be found. We therefore
- // just declare such an abstract
- // base class, taking a pointer to
- // a triangulation in the
- // constructor and storing it
- // henceforth. Since this
- // triangulation will be used
- // throughout all computations, we
- // have to make sure that the
- // triangulation exists until the
- // destructor exits. We do this by
- // keeping a <code>SmartPointer</code> to
- // this triangulation, which uses a
- // counter in the triangulation
- // class to denote the fact that
- // there is still an object out
- // there using this triangulation,
- // thus leading to an abort in case
- // the triangulation is attempted
- // to be destructed while this
- // object still uses it.
- //
- // Note that while the pointer
- // itself is declared constant
- // (i.e. throughout the lifetime of
- // this object, the pointer points
- // to the same object), it is not
- // declared as a pointer to a
- // constant triangulation. In fact,
- // by this we allow that derived
- // classes refine or coarsen the
- // triangulation within the
- // <code>refine_grid</code> function.
- //
- // Finally, we have a function
- // <code>n_dofs</code> is only a tool for
- // the driver functions to decide
- // whether we want to go on with
- // mesh refinement or not. It
- // returns the number of degrees of
- // freedom the present simulation
- // has.
- template <int dim>
- class Base
- {
- public:
- Base (Triangulation<dim> &coarse_grid);
- virtual ~Base ();
-
- 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;
-
- protected:
- const SmartPointer<Triangulation<dim> > triangulation;
- };
+ // @sect4{Other evaluations}
- // The implementation of the only
- // two non-abstract functions is
- // then rather boring:
- template <int dim>
- Base<dim>::Base (Triangulation<dim> &coarse_grid)
- :
- triangulation (&coarse_grid)
- {}
+ // In practical applications, one
+ // would add here a list of other
+ // possible evaluation classes,
+ // representing quantities that one
+ // may be interested in. For this
+ // example, that much shall be
+ // sufficient, so we close the
+ // namespace.
+ }
- template <int dim>
- Base<dim>::~Base ()
- {}
-
-
- // @sect4{A general solver class}
-
- // Following now the main class
- // that implements assembling the
- // matrix of the linear system,
- // solving it, and calling the
- // postprocessor objects on the
- // solution. It implements the
- // <code>solve_problem</code> and
- // <code>postprocess</code> functions
- // declared in the base class. It
- // does not, however, implement the
- // <code>refine_grid</code> method, as mesh
- // refinement will be implemented
- // in a number of derived classes.
- //
- // It also declares a new abstract
- // virtual function,
- // <code>assemble_rhs</code>, that needs to
- // be overloaded in subclasses. The
- // reason is that we will implement
- // two different classes that will
- // implement different methods to
- // assemble the right hand side
- // vector. This function might also
- // be interesting in cases where
- // the right hand side depends not
- // simply on a continuous function,
- // but on something else as well,
- // for example the solution of
- // another discretized problem,
- // etc. The latter happens
- // frequently in non-linear
- // problems.
- //
- // As we mentioned previously, the
- // actual content of this class is
- // not new, but a mixture of
- // various techniques already used
- // in previous examples. We will
- // therefore not discuss them in
- // detail, but refer the reader to
- // these programs.
- //
- // Basically, in a few words, the
- // constructor of this class takes
- // pointers to a triangulation, a
- // finite element, and a function
- // object representing the boundary
- // values. These are either passed
- // down to the base class's
- // constructor, or are stored and
- // used to generate a
- // <code>DoFHandler</code> object
- // later. Since finite elements and
- // quadrature formula should match,
- // it is also passed a quadrature
- // object.
- //
- // The <code>solve_problem</code> sets up
- // the data structures for the
- // actual solution, calls the
- // functions to assemble the linear
- // system, and solves it.
+ // @sect3{The Laplace solver classes}
+
+ // After defining what we want to
+ // know of the solution, we should
+ // now care how to get at it. We will
+ // pack everything we need into a
+ // namespace of its own, for much the
+ // same reasons as for the
+ // evaluations above.
//
- // The <code>postprocess</code> function
- // finally takes an evaluation
- // object and applies it to the
- // computed solution.
+ // Since we have discussed Laplace
+ // solvers already in considerable
+ // detail in previous examples, there
+ // is not much new stuff
+ // following. Rather, we have to a
+ // great extent cannibalized previous
+ // examples and put them, in slightly
+ // different form, into this example
+ // program. We will therefore mostly
+ // be concerned with discussing the
+ // differences to previous examples.
//
- // The <code>n_dofs</code> function finally
- // implements the pure virtual
- // function of the base class.
- template <int dim>
- class Solver : public virtual Base<dim>
+ // Basically, as already said in the
+ // introduction, the lack of new
+ // stuff in this example is
+ // deliberate, as it is more to
+ // demonstrate software design
+ // practices, rather than
+ // mathematics. The emphasis in
+ // explanations below will therefore
+ // be more on the actual
+ // implementation.
+ namespace LaplaceSolver
{
- public:
- Solver (Triangulation<dim> &triangulation,
- const FiniteElement<dim> &fe,
- const Quadrature<dim> &quadrature,
- const Function<dim> &boundary_values);
- virtual
- ~Solver ();
-
- virtual
- void
- solve_problem ();
-
- virtual
- void
- postprocess (const Evaluation::EvaluationBase<dim> &postprocessor) const;
-
- virtual
- unsigned int
- n_dofs () const;
-
- // In the protected section of
- // this class, we first have a
- // number of member variables,
- // of which the use should be
- // clear from the previous
- // examples:
- protected:
- const SmartPointer<const FiniteElement<dim> > fe;
- const SmartPointer<const Quadrature<dim> > quadrature;
- DoFHandler<dim> dof_handler;
- Vector<double> solution;
- const SmartPointer<const Function<dim> > boundary_values;
-
- // Then we declare an abstract
- // function that will be used
- // to assemble the right hand
- // side. As explained above,
- // there are various cases for
- // which this action differs
- // strongly in what is
- // necessary, so we defer this
- // to derived classes:
- virtual void assemble_rhs (Vector<double> &rhs) const = 0;
-
- // Next, in the private
- // section, we have a small
- // class which represents an
- // entire linear system, i.e. a
- // matrix, a right hand side,
- // and a solution vector, as
- // well as the constraints that
- // are applied to it, such as
- // those due to hanging
- // nodes. Its constructor
- // initializes the various
- // subobjects, and there is a
- // function that implements a
- // conjugate gradient method as
- // solver.
- private:
- struct LinearSystem
- {
- LinearSystem (const DoFHandler<dim> &dof_handler);
-
- void solve (Vector<double> &solution) const;
-
- ConstraintMatrix hanging_node_constraints;
- SparsityPattern sparsity_pattern;
- SparseMatrix<double> matrix;
- Vector<double> rhs;
- };
+ // @sect4{An abstract base class}
+
+ // In defining a Laplace solver, we
+ // start out by declaring an
+ // abstract base class, that has no
+ // functionality itself except for
+ // taking and storing a pointer to
+ // the triangulation to be used
+ // later.
+ //
+ // This base class is very general,
+ // and could as well be used for
+ // any other stationary problem. It
+ // provides declarations of
+ // functions that shall, in derived
+ // classes, solve a problem,
+ // postprocess the solution with a
+ // list of evaluation objects, and
+ // refine the grid,
+ // respectively. None of these
+ // functions actually does
+ // something itself in the base
+ // class.
+ //
+ // Due to the lack of actual
+ // functionality, the programming
+ // style of declaring very abstract
+ // base classes reminds of the
+ // style used in Smalltalk or Java
+ // programs, where all classes are
+ // derived from entirely abstract
+ // classes <code>Object</code>, even number
+ // representations. The author
+ // admits that he does not
+ // particularly like the use of
+ // such a style in C++, as it puts
+ // style over reason. Furthermore,
+ // it promotes the use of virtual
+ // functions for everything (for
+ // example, in Java, all functions
+ // are virtual per se), which,
+ // however, has proven to be rather
+ // inefficient in many applications
+ // where functions are often only
+ // accessing data, not doing
+ // computations, and therefore
+ // quickly return; the overhead of
+ // virtual functions can then be
+ // significant. The opinion of the
+ // author is to have abstract base
+ // classes wherever at least some
+ // part of the code of actual
+ // implementations can be shared
+ // and thus separated into the base
+ // class.
+ //
+ // Besides all these theoretical
+ // questions, we here have a good
+ // reason, which will become
+ // clearer to the reader
+ // below. Basically, we want to be
+ // able to have a family of
+ // different Laplace solvers that
+ // differ so much that no larger
+ // common subset of functionality
+ // could be found. We therefore
+ // just declare such an abstract
+ // base class, taking a pointer to
+ // a triangulation in the
+ // constructor and storing it
+ // henceforth. Since this
+ // triangulation will be used
+ // throughout all computations, we
+ // have to make sure that the
+ // triangulation exists until the
+ // destructor exits. We do this by
+ // keeping a <code>SmartPointer</code> to
+ // this triangulation, which uses a
+ // counter in the triangulation
+ // class to denote the fact that
+ // there is still an object out
+ // there using this triangulation,
+ // thus leading to an abort in case
+ // the triangulation is attempted
+ // to be destructed while this
+ // object still uses it.
+ //
+ // Note that while the pointer
+ // itself is declared constant
+ // (i.e. throughout the lifetime of
+ // this object, the pointer points
+ // to the same object), it is not
+ // declared as a pointer to a
+ // constant triangulation. In fact,
+ // by this we allow that derived
+ // classes refine or coarsen the
+ // triangulation within the
+ // <code>refine_grid</code> function.
+ //
+ // Finally, we have a function
+ // <code>n_dofs</code> is only a tool for
+ // the driver functions to decide
+ // whether we want to go on with
+ // mesh refinement or not. It
+ // returns the number of degrees of
+ // freedom the present simulation
+ // has.
+ template <int dim>
+ class Base
+ {
+ public:
+ Base (Triangulation<dim> &coarse_grid);
+ virtual ~Base ();
- // Finally, there is a pair of
- // functions which will be used
- // to assemble the actual
- // system matrix. It calls the
- // virtual function assembling
- // the right hand side, and
- // installs a number threads
- // each running the second
- // function which assembles
- // part of the system
- // matrix. The mechanism for
- // doing so is the same as in
- // the step-9 example program.
- void
- assemble_linear_system (LinearSystem &linear_system);
-
- void
- assemble_matrix (LinearSystem &linear_system,
- const typename DoFHandler<dim>::active_cell_iterator &begin_cell,
- const typename DoFHandler<dim>::active_cell_iterator &end_cell,
- Threads::ThreadMutex &mutex) const;
- };
+ 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;
+ protected:
+ const SmartPointer<Triangulation<dim> > triangulation;
+ };
- // Now here comes the constructor
- // of the class. It does not do
- // much except store pointers to
- // the objects given, and generate
- // <code>DoFHandler</code> object
- // initialized with the given
- // pointer to a triangulation. This
- // causes the DoF handler to store
- // that pointer, but does not
- // already generate a finite
- // element numbering (we only ask
- // for that in the
- // <code>solve_problem</code> function).
- template <int dim>
- Solver<dim>::Solver (Triangulation<dim> &triangulation,
- const FiniteElement<dim> &fe,
- const Quadrature<dim> &quadrature,
- const Function<dim> &boundary_values)
- :
- Base<dim> (triangulation),
- fe (&fe),
- quadrature (&quadrature),
- dof_handler (triangulation),
- boundary_values (&boundary_values)
- {}
-
-
- // The destructor is simple, it
- // only clears the information
- // stored in the DoF handler object
- // to release the memory.
- template <int dim>
- Solver<dim>::~Solver ()
- {
- dof_handler.clear ();
- }
+ // The implementation of the only
+ // two non-abstract functions is
+ // then rather boring:
+ template <int dim>
+ Base<dim>::Base (Triangulation<dim> &coarse_grid)
+ :
+ triangulation (&coarse_grid)
+ {}
+
+
+ template <int dim>
+ Base<dim>::~Base ()
+ {}
+
+
+ // @sect4{A general solver class}
+
+ // Following now the main class
+ // that implements assembling the
+ // matrix of the linear system,
+ // solving it, and calling the
+ // postprocessor objects on the
+ // solution. It implements the
+ // <code>solve_problem</code> and
+ // <code>postprocess</code> functions
+ // declared in the base class. It
+ // does not, however, implement the
+ // <code>refine_grid</code> method, as mesh
+ // refinement will be implemented
+ // in a number of derived classes.
+ //
+ // It also declares a new abstract
+ // virtual function,
+ // <code>assemble_rhs</code>, that needs to
+ // be overloaded in subclasses. The
+ // reason is that we will implement
+ // two different classes that will
+ // implement different methods to
+ // assemble the right hand side
+ // vector. This function might also
+ // be interesting in cases where
+ // the right hand side depends not
+ // simply on a continuous function,
+ // but on something else as well,
+ // for example the solution of
+ // another discretized problem,
+ // etc. The latter happens
+ // frequently in non-linear
+ // problems.
+ //
+ // As we mentioned previously, the
+ // actual content of this class is
+ // not new, but a mixture of
+ // various techniques already used
+ // in previous examples. We will
+ // therefore not discuss them in
+ // detail, but refer the reader to
+ // these programs.
+ //
+ // Basically, in a few words, the
+ // constructor of this class takes
+ // pointers to a triangulation, a
+ // finite element, and a function
+ // object representing the boundary
+ // values. These are either passed
+ // down to the base class's
+ // constructor, or are stored and
+ // used to generate a
+ // <code>DoFHandler</code> object
+ // later. Since finite elements and
+ // quadrature formula should match,
+ // it is also passed a quadrature
+ // object.
+ //
+ // The <code>solve_problem</code> sets up
+ // the data structures for the
+ // actual solution, calls the
+ // functions to assemble the linear
+ // system, and solves it.
+ //
+ // The <code>postprocess</code> function
+ // finally takes an evaluation
+ // object and applies it to the
+ // computed solution.
+ //
+ // The <code>n_dofs</code> function finally
+ // implements the pure virtual
+ // function of the base class.
+ template <int dim>
+ class Solver : public virtual Base<dim>
+ {
+ public:
+ Solver (Triangulation<dim> &triangulation,
+ const FiniteElement<dim> &fe,
+ const Quadrature<dim> &quadrature,
+ const Function<dim> &boundary_values);
+ virtual
+ ~Solver ();
+
+ virtual
+ void
+ solve_problem ();
+
+ virtual
+ void
+ postprocess (const Evaluation::EvaluationBase<dim> &postprocessor) const;
+
+ virtual
+ unsigned int
+ n_dofs () const;
+
+ // In the protected section of
+ // this class, we first have a
+ // number of member variables,
+ // of which the use should be
+ // clear from the previous
+ // examples:
+ protected:
+ const SmartPointer<const FiniteElement<dim> > fe;
+ const SmartPointer<const Quadrature<dim> > quadrature;
+ DoFHandler<dim> dof_handler;
+ Vector<double> solution;
+ const SmartPointer<const Function<dim> > boundary_values;
+
+ // Then we declare an abstract
+ // function that will be used
+ // to assemble the right hand
+ // side. As explained above,
+ // there are various cases for
+ // which this action differs
+ // strongly in what is
+ // necessary, so we defer this
+ // to derived classes:
+ virtual void assemble_rhs (Vector<double> &rhs) const = 0;
+
+ // Next, in the private
+ // section, we have a small
+ // class which represents an
+ // entire linear system, i.e. a
+ // matrix, a right hand side,
+ // and a solution vector, as
+ // well as the constraints that
+ // are applied to it, such as
+ // those due to hanging
+ // nodes. Its constructor
+ // initializes the various
+ // subobjects, and there is a
+ // function that implements a
+ // conjugate gradient method as
+ // solver.
+ private:
+ struct LinearSystem
+ {
+ LinearSystem (const DoFHandler<dim> &dof_handler);
+ void solve (Vector<double> &solution) const;
- // The next function is the one
- // which delegates the main work in
- // solving the problem: it sets up
- // the DoF handler object with the
- // finite element given to the
- // constructor of this object, the
- // creates an object that denotes
- // the linear system (i.e. the
- // matrix, the right hand side
- // vector, and the solution
- // vector), calls the function to
- // assemble it, and finally solves
- // it:
- template <int dim>
- void
- Solver<dim>::solve_problem ()
- {
- dof_handler.distribute_dofs (*fe);
- solution.reinit (dof_handler.n_dofs());
+ ConstraintMatrix hanging_node_constraints;
+ SparsityPattern sparsity_pattern;
+ SparseMatrix<double> matrix;
+ Vector<double> rhs;
+ };
- LinearSystem linear_system (dof_handler);
- assemble_linear_system (linear_system);
- linear_system.solve (solution);
- }
+ // Finally, there is a pair of
+ // functions which will be used
+ // to assemble the actual
+ // system matrix. It calls the
+ // virtual function assembling
+ // the right hand side, and
+ // installs a number threads
+ // each running the second
+ // function which assembles
+ // part of the system
+ // matrix. The mechanism for
+ // doing so is the same as in
+ // the step-9 example program.
+ void
+ assemble_linear_system (LinearSystem &linear_system);
+
+ void
+ assemble_matrix (LinearSystem &linear_system,
+ const typename DoFHandler<dim>::active_cell_iterator &begin_cell,
+ const typename DoFHandler<dim>::active_cell_iterator &end_cell,
+ Threads::ThreadMutex &mutex) const;
+ };
- // As stated above, the
- // <code>postprocess</code> function takes
- // an evaluation object, and
- // applies it to the computed
- // solution. This function may be
- // called multiply, once for each
- // evaluation of the solution which
- // the user required.
- template <int dim>
- void
- Solver<dim>::
- postprocess (const Evaluation::EvaluationBase<dim> &postprocessor) const
- {
- postprocessor (dof_handler, solution);
- }
+ // Now here comes the constructor
+ // of the class. It does not do
+ // much except store pointers to
+ // the objects given, and generate
+ // <code>DoFHandler</code> object
+ // initialized with the given
+ // pointer to a triangulation. This
+ // causes the DoF handler to store
+ // that pointer, but does not
+ // already generate a finite
+ // element numbering (we only ask
+ // for that in the
+ // <code>solve_problem</code> function).
+ template <int dim>
+ Solver<dim>::Solver (Triangulation<dim> &triangulation,
+ const FiniteElement<dim> &fe,
+ const Quadrature<dim> &quadrature,
+ const Function<dim> &boundary_values)
+ :
+ Base<dim> (triangulation),
+ fe (&fe),
+ quadrature (&quadrature),
+ dof_handler (triangulation),
+ boundary_values (&boundary_values)
+ {}
+
+
+ // The destructor is simple, it
+ // only clears the information
+ // stored in the DoF handler object
+ // to release the memory.
+ template <int dim>
+ Solver<dim>::~Solver ()
+ {
+ dof_handler.clear ();
+ }
- // The <code>n_dofs</code> function should
- // be self-explanatory:
- template <int dim>
- unsigned int
- Solver<dim>::n_dofs () const
- {
- return dof_handler.n_dofs();
- }
-
-
- // The following function assembles matrix
- // and right hand side of the linear system
- // to be solved in each step. It goes along
- // the same lines as used in previous
- // examples, so we explain it only
- // briefly. Note that we do a number of
- // things in parallel, a process described
- // in more detail in the @ref threads
- // module.
- template <int dim>
- void
- Solver<dim>::assemble_linear_system (LinearSystem &linear_system)
- {
- // First define a convenience
- // abbreviation for these lengthy
- // iterator names...
- typedef
- typename DoFHandler<dim>::active_cell_iterator
- active_cell_iterator;
-
- // ... and use it to split up the
- // set of cells into a number of
- // pieces of equal size. The
- // number of blocks is set to the
- // default number of threads to
- // be used, which by default is
- // set to the number of
- // processors found in your
- // computer at startup of the
- // program:
- const unsigned int n_threads = multithread_info.n_default_threads;
- std::vector<std::pair<active_cell_iterator,active_cell_iterator> >
- thread_ranges
- = Threads::split_range<active_cell_iterator> (dof_handler.begin_active (),
- dof_handler.end (),
- n_threads);
-
- // These ranges are then assigned
- // to a number of threads which
- // we create next. Each will
- // assemble the local cell
- // matrices on the assigned
- // cells, and fill the matrix
- // object with it. Since there is
- // need for synchronization when
- // filling the same matrix from
- // different threads, we need a
- // mutex here:
- Threads::ThreadMutex mutex;
- Threads::ThreadGroup<> threads;
- for (unsigned int thread=0; thread<n_threads; ++thread)
- threads += Threads::new_thread (&Solver<dim>::assemble_matrix,
- *this,
- linear_system,
- thread_ranges[thread].first,
- thread_ranges[thread].second,
- mutex);
-
- // While the new threads
- // assemble the system matrix, we
- // can already compute the right
- // hand side vector in the main
- // thread, and condense away the
- // constraints due to hanging
- // nodes:
- assemble_rhs (linear_system.rhs);
- linear_system.hanging_node_constraints.condense (linear_system.rhs);
-
- // And while we're already
- // computing things in parallel,
- // interpolating boundary values
- // is one more thing that can be
- // done independently, so we do
- // it here:
- std::map<unsigned int,double> boundary_value_map;
- VectorTools::interpolate_boundary_values (dof_handler,
- 0,
- *boundary_values,
- boundary_value_map);
-
-
- // If this is done, wait for the
- // matrix assembling threads, and
- // condense the constraints in
- // the matrix as well:
- threads.join_all ();
- linear_system.hanging_node_constraints.condense (linear_system.matrix);
-
- // Now that we have the linear
- // system, we can also treat
- // boundary values, which need to
- // be eliminated from both the
- // matrix and the right hand
- // side:
- MatrixTools::apply_boundary_values (boundary_value_map,
- linear_system.matrix,
- solution,
- linear_system.rhs);
- }
+ // The next function is the one
+ // which delegates the main work in
+ // solving the problem: it sets up
+ // the DoF handler object with the
+ // finite element given to the
+ // constructor of this object, the
+ // creates an object that denotes
+ // the linear system (i.e. the
+ // matrix, the right hand side
+ // vector, and the solution
+ // vector), calls the function to
+ // assemble it, and finally solves
+ // it:
+ 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);
+ }
- // The second of this pair of
- // functions takes a range of cell
- // iterators, and assembles the
- // system matrix on this part of
- // the domain. Since it's actions
- // have all been explained in
- // previous programs, we do not
- // comment on it any more, except
- // for one pointe below.
- template <int dim>
- void
- Solver<dim>::assemble_matrix (LinearSystem &linear_system,
- const typename DoFHandler<dim>::active_cell_iterator &begin_cell,
- const typename DoFHandler<dim>::active_cell_iterator &end_cell,
- Threads::ThreadMutex &mutex) const
- {
- FEValues<dim> fe_values (*fe, *quadrature,
- update_gradients | update_JxW_values);
- const unsigned int dofs_per_cell = fe->dofs_per_cell;
- const unsigned int n_q_points = quadrature->size();
+ // As stated above, the
+ // <code>postprocess</code> function takes
+ // an evaluation object, and
+ // applies it to the computed
+ // solution. This function may be
+ // called multiply, once for each
+ // evaluation of the solution which
+ // the user required.
+ template <int dim>
+ void
+ Solver<dim>::
+ postprocess (const Evaluation::EvaluationBase<dim> &postprocessor) const
+ {
+ postprocessor (dof_handler, solution);
+ }
+
- FullMatrix<double> cell_matrix (dofs_per_cell, dofs_per_cell);
+ // The <code>n_dofs</code> function should
+ // be self-explanatory:
+ template <int dim>
+ unsigned int
+ Solver<dim>::n_dofs () const
+ {
+ return dof_handler.n_dofs();
+ }
- std::vector<unsigned int> local_dof_indices (dofs_per_cell);
- for (typename DoFHandler<dim>::active_cell_iterator cell=begin_cell;
- cell!=end_cell; ++cell)
- {
- cell_matrix = 0;
+ // The following function assembles matrix
+ // and right hand side of the linear system
+ // to be solved in each step. It goes along
+ // the same lines as used in previous
+ // examples, so we explain it only
+ // briefly. Note that we do a number of
+ // things in parallel, a process described
+ // in more detail in the @ref threads
+ // module.
+ template <int dim>
+ void
+ Solver<dim>::assemble_linear_system (LinearSystem &linear_system)
+ {
+ // First define a convenience
+ // abbreviation for these lengthy
+ // iterator names...
+ typedef
+ typename DoFHandler<dim>::active_cell_iterator
+ active_cell_iterator;
+
+ // ... and use it to split up the
+ // set of cells into a number of
+ // pieces of equal size. The
+ // number of blocks is set to the
+ // default number of threads to
+ // be used, which by default is
+ // set to the number of
+ // processors found in your
+ // computer at startup of the
+ // program:
+ const unsigned int n_threads = multithread_info.n_default_threads;
+ std::vector<std::pair<active_cell_iterator,active_cell_iterator> >
+ thread_ranges
+ = Threads::split_range<active_cell_iterator> (dof_handler.begin_active (),
+ dof_handler.end (),
+ n_threads);
+
+ // These ranges are then assigned
+ // to a number of threads which
+ // we create next. Each will
+ // assemble the local cell
+ // matrices on the assigned
+ // cells, and fill the matrix
+ // object with it. Since there is
+ // need for synchronization when
+ // filling the same matrix from
+ // different threads, we need a
+ // mutex here:
+ Threads::ThreadMutex mutex;
+ Threads::ThreadGroup<> threads;
+ for (unsigned int thread=0; thread<n_threads; ++thread)
+ threads += Threads::new_thread (&Solver<dim>::assemble_matrix,
+ *this,
+ linear_system,
+ thread_ranges[thread].first,
+ thread_ranges[thread].second,
+ mutex);
+
+ // While the new threads
+ // assemble the system matrix, we
+ // can already compute the right
+ // hand side vector in the main
+ // thread, and condense away the
+ // constraints due to hanging
+ // nodes:
+ assemble_rhs (linear_system.rhs);
+ linear_system.hanging_node_constraints.condense (linear_system.rhs);
+
+ // And while we're already
+ // computing things in parallel,
+ // interpolating boundary values
+ // is one more thing that can be
+ // done independently, so we do
+ // it here:
+ std::map<unsigned int,double> boundary_value_map;
+ VectorTools::interpolate_boundary_values (dof_handler,
+ 0,
+ *boundary_values,
+ boundary_value_map);
+
+
+ // If this is done, wait for the
+ // matrix assembling threads, and
+ // condense the constraints in
+ // the matrix as well:
+ threads.join_all ();
+ linear_system.hanging_node_constraints.condense (linear_system.matrix);
+
+ // Now that we have the linear
+ // system, we can also treat
+ // boundary values, which need to
+ // be eliminated from both the
+ // matrix and the right hand
+ // side:
+ MatrixTools::apply_boundary_values (boundary_value_map,
+ linear_system.matrix,
+ solution,
+ linear_system.rhs);
- fe_values.reinit (cell);
+ }
+
+
+ // The second of this pair of
+ // functions takes a range of cell
+ // iterators, and assembles the
+ // system matrix on this part of
+ // the domain. Since it's actions
+ // have all been explained in
+ // previous programs, we do not
+ // comment on it any more, except
+ // for one pointe below.
+ template <int dim>
+ void
+ Solver<dim>::assemble_matrix (LinearSystem &linear_system,
+ const typename DoFHandler<dim>::active_cell_iterator &begin_cell,
+ const typename DoFHandler<dim>::active_cell_iterator &end_cell,
+ Threads::ThreadMutex &mutex) const
+ {
+ FEValues<dim> fe_values (*fe, *quadrature,
+ update_gradients | update_JxW_values);
- for (unsigned int q_point=0; q_point<n_q_points; ++q_point)
+ const unsigned int dofs_per_cell = fe->dofs_per_cell;
+ const unsigned int n_q_points = quadrature->size();
+
+ FullMatrix<double> cell_matrix (dofs_per_cell, dofs_per_cell);
+
+ std::vector<unsigned int> local_dof_indices (dofs_per_cell);
+
+ for (typename DoFHandler<dim>::active_cell_iterator cell=begin_cell;
+ cell!=end_cell; ++cell)
+ {
+ cell_matrix = 0;
+
+ 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)
+ cell_matrix(i,j) += (fe_values.shape_grad(i,q_point) *
+ fe_values.shape_grad(j,q_point) *
+ fe_values.JxW(q_point));
+
+
+ cell->get_dof_indices (local_dof_indices);
+
+ // In the step-9 program, we
+ // have shown that you have
+ // to use the mutex to lock
+ // the matrix when copying
+ // the elements from the
+ // local to the global
+ // matrix. This was necessary
+ // to avoid that two threads
+ // access it at the same
+ // time, eventually
+ // overwriting their
+ // respective
+ // work. Previously, we have
+ // used the <code>acquire</code> and
+ // <code>release</code> functions of
+ // the mutex to lock and
+ // unlock the mutex,
+ // respectively. While this
+ // is valid, there is one
+ // possible catch: if between
+ // the locking operation and
+ // the unlocking operation an
+ // exception is thrown, the
+ // mutex remains in the
+ // locked state, and in some
+ // cases this might lead to
+ // deadlocks. A similar
+ // situation arises, when one
+ // changes the code to have a
+ // return statement somewhere
+ // in the middle of the
+ // locked block, and forgets
+ // that before we call
+ // <code>return</code>, we also have
+ // to unlock the mutex. This
+ // all is not be a problem
+ // here, but we want to show
+ // the general technique to
+ // cope with these problems
+ // nevertheless: have an
+ // object that upon
+ // initialization (i.e. in
+ // its constructor) locks the
+ // mutex, and on running the
+ // destructor unlocks it
+ // again. This is called the
+ // <code>scoped lock</code> pattern
+ // (apparently invented by
+ // Doug Schmidt originally),
+ // and it works because
+ // destructors of local
+ // objects are also run when
+ // we exit the function
+ // either through a
+ // <code>return</code> statement, or
+ // when an exception is
+ // raised. Thus, it is
+ // guaranteed that the mutex
+ // will always be unlocked
+ // when we exit this part of
+ // the program, whether the
+ // operation completed
+ // successfully or not,
+ // whether the exit path was
+ // something we implemented
+ // willfully or whether the
+ // function was exited by an
+ // exception that we did not
+ // forsee.
+ //
+ // deal.II implements the
+ // scoped locking pattern in
+ // the
+ // ThreadMutex::ScopedLock
+ // class: it takes the mutex
+ // in the constructor and
+ // locks it; in its
+ // destructor, it unlocks it
+ // again. So here is how it
+ // is used:
+ Threads::ThreadMutex::ScopedLock lock (mutex);
for (unsigned int i=0; i<dofs_per_cell; ++i)
for (unsigned int j=0; j<dofs_per_cell; ++j)
- cell_matrix(i,j) += (fe_values.shape_grad(i,q_point) *
- fe_values.shape_grad(j,q_point) *
- fe_values.JxW(q_point));
-
-
- cell->get_dof_indices (local_dof_indices);
-
- // In the step-9 program, we
- // have shown that you have
- // to use the mutex to lock
- // the matrix when copying
- // the elements from the
- // local to the global
- // matrix. This was necessary
- // to avoid that two threads
- // access it at the same
- // time, eventually
- // overwriting their
- // respective
- // work. Previously, we have
- // used the <code>acquire</code> and
- // <code>release</code> functions of
- // the mutex to lock and
- // unlock the mutex,
- // respectively. While this
- // is valid, there is one
- // possible catch: if between
- // the locking operation and
- // the unlocking operation an
- // exception is thrown, the
- // mutex remains in the
- // locked state, and in some
- // cases this might lead to
- // deadlocks. A similar
- // situation arises, when one
- // changes the code to have a
- // return statement somewhere
- // in the middle of the
- // locked block, and forgets
- // that before we call
- // <code>return</code>, we also have
- // to unlock the mutex. This
- // all is not be a problem
- // here, but we want to show
- // the general technique to
- // cope with these problems
- // nevertheless: have an
- // object that upon
- // initialization (i.e. in
- // its constructor) locks the
- // mutex, and on running the
- // destructor unlocks it
- // again. This is called the
- // <code>scoped lock</code> pattern
- // (apparently invented by
- // Doug Schmidt originally),
- // and it works because
- // destructors of local
- // objects are also run when
- // we exit the function
- // either through a
- // <code>return</code> statement, or
- // when an exception is
- // raised. Thus, it is
- // guaranteed that the mutex
- // will always be unlocked
- // when we exit this part of
- // the program, whether the
- // operation completed
- // successfully or not,
- // whether the exit path was
- // something we implemented
- // willfully or whether the
- // function was exited by an
- // exception that we did not
- // forsee.
- //
- // deal.II implements the
- // scoped locking pattern in
- // the
- // ThreadMutex::ScopedLock
- // class: it takes the mutex
- // in the constructor and
- // locks it; in its
- // destructor, it unlocks it
- // again. So here is how it
- // is used:
- Threads::ThreadMutex::ScopedLock lock (mutex);
- for (unsigned int i=0; i<dofs_per_cell; ++i)
- for (unsigned int j=0; j<dofs_per_cell; ++j)
- linear_system.matrix.add (local_dof_indices[i],
- local_dof_indices[j],
- cell_matrix(i,j));
- // Here, at the brace, the
- // current scope ends, so the
- // <code>lock</code> variable goes out
- // of existence and its
- // destructor the mutex is
- // unlocked.
- };
- }
+ linear_system.matrix.add (local_dof_indices[i],
+ local_dof_indices[j],
+ cell_matrix(i,j));
+ // Here, at the brace, the
+ // current scope ends, so the
+ // <code>lock</code> variable goes out
+ // of existence and its
+ // destructor the mutex is
+ // unlocked.
+ };
+ }
- // 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::ThreadGroup</code>
- // class here, but rather use the
- // one created thread object
- // directly to wait for this
- // particular thread's exit.
- //
- // Note that taking up the address
- // of the
- // <code>DoFTools::make_hanging_node_constraints</code>
- // function is a little tricky,
- // since there are actually three
- // of them, 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 more like 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> &,
- ConstraintMatrix &)
- = &DoFTools::make_hanging_node_constraints;
-
- Threads::Thread<>
- mhnc_thread = Threads::new_thread (mhnc_p,
- dof_handler,
- hanging_node_constraints);
-
- sparsity_pattern.reinit (dof_handler.n_dofs(),
- dof_handler.n_dofs(),
- dof_handler.max_couplings_between_dofs());
- DoFTools::make_sparsity_pattern (dof_handler, sparsity_pattern);
-
- // Wait until the
- // <code>hanging_node_constraints</code>
- // object is fully set up, then
- // close it and use it to
- // condense the sparsity pattern:
- mhnc_thread.join ();
- hanging_node_constraints.close ();
- hanging_node_constraints.condense (sparsity_pattern);
-
- // Finally, close the sparsity
- // pattern, initialize the
- // matrix, and set the right hand
- // side vector to the right size.
- sparsity_pattern.compress();
- matrix.reinit (sparsity_pattern);
- rhs.reinit (dof_handler.n_dofs());
- }
+ // 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::ThreadGroup</code>
+ // class here, but rather use the
+ // one created thread object
+ // directly to wait for this
+ // particular thread's exit.
+ //
+ // Note that taking up the address
+ // of the
+ // <code>DoFTools::make_hanging_node_constraints</code>
+ // function is a little tricky,
+ // since there are actually three
+ // of them, 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 more like 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> &,
+ ConstraintMatrix &)
+ = &DoFTools::make_hanging_node_constraints;
+
+ Threads::Thread<>
+ mhnc_thread = Threads::new_thread (mhnc_p,
+ dof_handler,
+ hanging_node_constraints);
+
+ sparsity_pattern.reinit (dof_handler.n_dofs(),
+ dof_handler.n_dofs(),
+ dof_handler.max_couplings_between_dofs());
+ DoFTools::make_sparsity_pattern (dof_handler, sparsity_pattern);
+
+ // Wait until the
+ // <code>hanging_node_constraints</code>
+ // object is fully set up, then
+ // close it and use it to
+ // condense the sparsity pattern:
+ mhnc_thread.join ();
+ hanging_node_constraints.close ();
+ hanging_node_constraints.condense (sparsity_pattern);
+
+ // Finally, close the sparsity
+ // pattern, initialize the
+ // matrix, and set the right hand
+ // side vector to the right size.
+ sparsity_pattern.compress();
+ matrix.reinit (sparsity_pattern);
+ rhs.reinit (dof_handler.n_dofs());
+ }
- // The second function of this
- // class simply solves the linear
- // system by a preconditioned
- // conjugate gradient method. This
- // has been extensively discussed
- // before, so we don't dwell into
- // it any more.
- template <int dim>
- void
- Solver<dim>::LinearSystem::solve (Vector<double> &solution) const
- {
- SolverControl solver_control (1000, 1e-12);
- SolverCG<> cg (solver_control);
+ // The second function of this
+ // class simply solves the linear
+ // system by a preconditioned
+ // conjugate gradient method. This
+ // has been extensively discussed
+ // before, so we don't dwell into
+ // it any more.
+ template <int dim>
+ void
+ Solver<dim>::LinearSystem::solve (Vector<double> &solution) const
+ {
+ SolverControl solver_control (1000, 1e-12);
+ SolverCG<> cg (solver_control);
- PreconditionSSOR<> preconditioner;
- preconditioner.initialize(matrix, 1.2);
+ PreconditionSSOR<> preconditioner;
+ preconditioner.initialize(matrix, 1.2);
- cg.solve (matrix, solution, rhs, preconditioner);
+ cg.solve (matrix, solution, rhs, preconditioner);
- hanging_node_constraints.distribute (solution);
- }
+ hanging_node_constraints.distribute (solution);
+ }
- // @sect4{A primal solver}
+ // @sect4{A primal solver}
- // In the previous section, a base
- // class for Laplace solvers was
- // implemented, that lacked the
- // functionality to assemble the
- // right hand side vector, however,
- // for reasons that were explained
- // there. Now we implement a
- // corresponding class that can do
- // this for the case that the right
- // hand side of a problem is given
- // as a function object.
- //
- // The actions of the class are
- // rather what you have seen
- // already in previous examples
- // already, so a brief explanation
- // should suffice: the constructor
- // takes the same data as does that
- // of the underlying class (to
- // which it passes all information)
- // except for one function object
- // that denotes the right hand side
- // of the problem. A pointer to
- // this object is stored (again as
- // a <code>SmartPointer</code>, in order to
- // make sure that the function
- // object is not deleted as long as
- // it is still used by this class).
- //
- // The only functional part of this
- // class is the <code>assemble_rhs</code>
- // method that does what its name
- // suggests.
- template <int dim>
- class PrimalSolver : public Solver<dim>
- {
- public:
- PrimalSolver (Triangulation<dim> &triangulation,
- const FiniteElement<dim> &fe,
- const Quadrature<dim> &quadrature,
- const Function<dim> &rhs_function,
- const Function<dim> &boundary_values);
- protected:
- const SmartPointer<const Function<dim> > rhs_function;
- virtual void assemble_rhs (Vector<double> &rhs) const;
- };
+ // In the previous section, a base
+ // class for Laplace solvers was
+ // implemented, that lacked the
+ // functionality to assemble the
+ // right hand side vector, however,
+ // for reasons that were explained
+ // there. Now we implement a
+ // corresponding class that can do
+ // this for the case that the right
+ // hand side of a problem is given
+ // as a function object.
+ //
+ // The actions of the class are
+ // rather what you have seen
+ // already in previous examples
+ // already, so a brief explanation
+ // should suffice: the constructor
+ // takes the same data as does that
+ // of the underlying class (to
+ // which it passes all information)
+ // except for one function object
+ // that denotes the right hand side
+ // of the problem. A pointer to
+ // this object is stored (again as
+ // a <code>SmartPointer</code>, in order to
+ // make sure that the function
+ // object is not deleted as long as
+ // it is still used by this class).
+ //
+ // The only functional part of this
+ // class is the <code>assemble_rhs</code>
+ // method that does what its name
+ // suggests.
+ template <int dim>
+ class PrimalSolver : public Solver<dim>
+ {
+ public:
+ PrimalSolver (Triangulation<dim> &triangulation,
+ const FiniteElement<dim> &fe,
+ const Quadrature<dim> &quadrature,
+ const Function<dim> &rhs_function,
+ const Function<dim> &boundary_values);
+ protected:
+ const SmartPointer<const Function<dim> > rhs_function;
+ virtual void assemble_rhs (Vector<double> &rhs) const;
+ };
- // The constructor of this class
- // basically does what it is
- // announced to do above...
- template <int dim>
- PrimalSolver<dim>::
- PrimalSolver (Triangulation<dim> &triangulation,
- const FiniteElement<dim> &fe,
- const Quadrature<dim> &quadrature,
- const Function<dim> &rhs_function,
- const Function<dim> &boundary_values)
- :
- Base<dim> (triangulation),
- Solver<dim> (triangulation, fe,
- quadrature, boundary_values),
- rhs_function (&rhs_function)
- {}
-
-
-
- // ... as does the <code>assemble_rhs</code>
- // function. Since this is
- // explained in several of the
- // previous example programs, we
- // leave it at that.
- 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);
+ // The constructor of this class
+ // basically does what it is
+ // announced to do above...
+ template <int dim>
+ PrimalSolver<dim>::
+ PrimalSolver (Triangulation<dim> &triangulation,
+ const FiniteElement<dim> &fe,
+ const Quadrature<dim> &quadrature,
+ const Function<dim> &rhs_function,
+ const Function<dim> &boundary_values)
+ :
+ Base<dim> (triangulation),
+ Solver<dim> (triangulation, fe,
+ quadrature, boundary_values),
+ rhs_function (&rhs_function)
+ {}
+
+
+
+ // ... as does the <code>assemble_rhs</code>
+ // function. Since this is
+ // explained in several of the
+ // previous example programs, we
+ // leave it at that.
+ 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->dofs_per_cell;
- const unsigned int n_q_points = this->quadrature->size();
+ const unsigned int dofs_per_cell = this->fe->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<unsigned int> local_dof_indices (dofs_per_cell);
+ Vector<double> cell_rhs (dofs_per_cell);
+ std::vector<double> rhs_values (n_q_points);
+ std::vector<unsigned int> local_dof_indices (dofs_per_cell);
- typename DoFHandler<dim>::active_cell_iterator
- cell = this->dof_handler.begin_active(),
- endc = this->dof_handler.end();
- for (; cell!=endc; ++cell)
- {
- 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)
+ typename DoFHandler<dim>::active_cell_iterator
+ cell = this->dof_handler.begin_active(),
+ endc = this->dof_handler.end();
+ for (; cell!=endc; ++cell)
+ {
+ 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) *
+ rhs_values[q_point] *
+ fe_values.JxW(q_point));
+
+ cell->get_dof_indices (local_dof_indices);
for (unsigned int i=0; i<dofs_per_cell; ++i)
- cell_rhs(i) += (fe_values.shape_value(i,q_point) *
- rhs_values[q_point] *
- fe_values.JxW(q_point));
+ rhs(local_dof_indices[i]) += cell_rhs(i);
+ };
+ }
- 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{Global refinement}
- // @sect4{Global refinement}
+ // By now, all functions of the
+ // abstract base class except for
+ // the <code>refine_grid</code> function
+ // have been implemented. We will
+ // now have two classes that
+ // implement this function for the
+ // <code>PrimalSolver</code> class, one
+ // doing global refinement, one a
+ // form of local refinement.
+ //
+ // The first, doing global
+ // refinement, is rather simple:
+ // its main function just calls
+ // <code>triangulation-@>refine_global
+ // (1);</code>, which does all the work.
+ //
+ // Note that since the <code>Base</code>
+ // base class of the <code>Solver</code>
+ // class is virtual, we have to
+ // declare a constructor that
+ // initializes the immediate base
+ // class as well as the abstract
+ // virtual one.
+ //
+ // Apart from this technical
+ // complication, the class is
+ // probably simple enough to be
+ // left without further comments.
+ template <int dim>
+ class RefinementGlobal : public PrimalSolver<dim>
+ {
+ public:
+ RefinementGlobal (Triangulation<dim> &coarse_grid,
+ const FiniteElement<dim> &fe,
+ const Quadrature<dim> &quadrature,
+ const Function<dim> &rhs_function,
+ const Function<dim> &boundary_values);
+
+ virtual void refine_grid ();
+ };
- // By now, all functions of the
- // abstract base class except for
- // the <code>refine_grid</code> function
- // have been implemented. We will
- // now have two classes that
- // implement this function for the
- // <code>PrimalSolver</code> class, one
- // doing global refinement, one a
- // form of local refinement.
- //
- // The first, doing global
- // refinement, is rather simple:
- // its main function just calls
- // <code>triangulation-@>refine_global
- // (1);</code>, which does all the work.
- //
- // Note that since the <code>Base</code>
- // base class of the <code>Solver</code>
- // class is virtual, we have to
- // declare a constructor that
- // initializes the immediate base
- // class as well as the abstract
- // virtual one.
- //
- // Apart from this technical
- // complication, the class is
- // probably simple enough to be
- // left without further comments.
- template <int dim>
- class RefinementGlobal : public PrimalSolver<dim>
- {
- public:
- RefinementGlobal (Triangulation<dim> &coarse_grid,
- const FiniteElement<dim> &fe,
- const Quadrature<dim> &quadrature,
- const Function<dim> &rhs_function,
- const Function<dim> &boundary_values);
- virtual void refine_grid ();
- };
+ template <int dim>
+ RefinementGlobal<dim>::
+ RefinementGlobal (Triangulation<dim> &coarse_grid,
+ const FiniteElement<dim> &fe,
+ const Quadrature<dim> &quadrature,
+ const Function<dim> &rhs_function,
+ const Function<dim> &boundary_values)
+ :
+ Base<dim> (coarse_grid),
+ PrimalSolver<dim> (coarse_grid, fe, quadrature,
+ rhs_function, boundary_values)
+ {}
- template <int dim>
- RefinementGlobal<dim>::
- RefinementGlobal (Triangulation<dim> &coarse_grid,
- const FiniteElement<dim> &fe,
- const Quadrature<dim> &quadrature,
- const Function<dim> &rhs_function,
- const Function<dim> &boundary_values)
- :
- Base<dim> (coarse_grid),
- PrimalSolver<dim> (coarse_grid, fe, quadrature,
- rhs_function, boundary_values)
- {}
+ template <int dim>
+ void
+ RefinementGlobal<dim>::refine_grid ()
+ {
+ this->triangulation->refine_global (1);
+ }
- template <int dim>
- void
- RefinementGlobal<dim>::refine_grid ()
- {
- this->triangulation->refine_global (1);
+ // @sect4{Local refinement by the Kelly error indicator}
+
+ // The second class implementing
+ // refinement strategies uses the
+ // Kelly refinemet indicator used
+ // in various example programs
+ // before. Since this indicator is
+ // already implemented in a class
+ // of its own inside the deal.II
+ // library, there is not much t do
+ // here except cal the function
+ // computing the indicator, then
+ // using it to select a number of
+ // cells for refinement and
+ // coarsening, and refinement the
+ // mesh accordingly.
+ //
+ // Again, this should now be
+ // sufficiently standard to allow
+ // the omission of further
+ // comments.
+ template <int dim>
+ class RefinementKelly : public PrimalSolver<dim>
+ {
+ public:
+ RefinementKelly (Triangulation<dim> &coarse_grid,
+ const FiniteElement<dim> &fe,
+ const Quadrature<dim> &quadrature,
+ const Function<dim> &rhs_function,
+ const Function<dim> &boundary_values);
+
+ virtual void refine_grid ();
+ };
+
+
+
+ template <int dim>
+ RefinementKelly<dim>::
+ RefinementKelly (Triangulation<dim> &coarse_grid,
+ const FiniteElement<dim> &fe,
+ const Quadrature<dim> &quadrature,
+ const Function<dim> &rhs_function,
+ const Function<dim> &boundary_values)
+ :
+ Base<dim> (coarse_grid),
+ PrimalSolver<dim> (coarse_grid, fe, 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>(3),
+ typename FunctionMap<dim>::type(),
+ 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{Local refinement by the Kelly error indicator}
-
- // The second class implementing
- // refinement strategies uses the
- // Kelly refinemet indicator used
- // in various example programs
- // before. Since this indicator is
- // already implemented in a class
- // of its own inside the deal.II
- // library, there is not much t do
- // here except cal the function
- // computing the indicator, then
- // using it to select a number of
- // cells for refinement and
- // coarsening, and refinement the
- // mesh accordingly.
+
+
+ // @sect3{Equation data}
+
+ // As this is one more academic
+ // example, we'd like to compare
+ // exact and computed solution
+ // against each other. For this, we
+ // need to declare function classes
+ // representing the exact solution
+ // (for comparison and for the
+ // Dirichlet boundary values), as
+ // well as a class that denotes the
+ // right hand side of the equation
+ // (this is simply the Laplace
+ // operator applied to the exact
+ // solution we'd like to recover).
//
- // Again, this should now be
- // sufficiently standard to allow
- // the omission of further
- // comments.
+ // For this example, let us choose as
+ // exact solution the function
+ // $u(x,y)=exp(x+sin(10y+5x^2))$. In more
+ // than two dimensions, simply repeat
+ // the sine-factor with <code>y</code>
+ // replaced by <code>z</code> and so on. Given
+ // this, the following two classes
+ // are probably straightforward from
+ // the previous examples.
+ //
+ // As in previous examples, the C++
+ // language forces us to declare and
+ // define a constructor to the
+ // following classes even though they
+ // are empty. This is due to the fact
+ // that the base class has no default
+ // constructor (i.e. one without
+ // arguments), even though it has a
+ // constructor which has default
+ // values for all arguments.
template <int dim>
- class RefinementKelly : public PrimalSolver<dim>
+ class Solution : public Function<dim>
{
public:
- RefinementKelly (Triangulation<dim> &coarse_grid,
- const FiniteElement<dim> &fe,
- const Quadrature<dim> &quadrature,
- const Function<dim> &rhs_function,
- const Function<dim> &boundary_values);
+ Solution () : Function<dim> () {}
- virtual void refine_grid ();
+ virtual double value (const Point<dim> &p,
+ const unsigned int component) const;
};
-
- template <int dim>
- RefinementKelly<dim>::
- RefinementKelly (Triangulation<dim> &coarse_grid,
- const FiniteElement<dim> &fe,
- const Quadrature<dim> &quadrature,
- const Function<dim> &rhs_function,
- const Function<dim> &boundary_values)
- :
- Base<dim> (coarse_grid),
- PrimalSolver<dim> (coarse_grid, fe, quadrature,
- rhs_function, boundary_values)
- {}
-
-
-
template <int dim>
- void
- RefinementKelly<dim>::refine_grid ()
+ double
+ Solution<dim>::value (const Point<dim> &p,
+ const unsigned int /*component*/) const
{
- Vector<float> estimated_error_per_cell (this->triangulation->n_active_cells());
- KellyErrorEstimator<dim>::estimate (this->dof_handler,
- QGauss<dim-1>(3),
- typename FunctionMap<dim>::type(),
- 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 ();
+ 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>
+ class RightHandSide : public Function<dim>
+ {
+ public:
+ RightHandSide () : Function<dim> () {}
+ virtual double value (const Point<dim> &p,
+ const unsigned int component) const;
+ };
- // @sect3{Equation data}
-
- // As this is one more academic
- // example, we'd like to compare
- // exact and computed solution
- // against each other. For this, we
- // need to declare function classes
- // representing the exact solution
- // (for comparison and for the
- // Dirichlet boundary values), as
- // well as a class that denotes the
- // right hand side of the equation
- // (this is simply the Laplace
- // operator applied to the exact
- // solution we'd like to recover).
- //
- // For this example, let us choose as
- // exact solution the function
- // $u(x,y)=exp(x+sin(10y+5x^2))$. In more
- // than two dimensions, simply repeat
- // the sine-factor with <code>y</code>
- // replaced by <code>z</code> and so on. Given
- // this, the following two classes
- // are probably straightforward from
- // the previous examples.
- //
- // As in previous examples, the C++
- // language forces us to declare and
- // define a constructor to the
- // following classes even though they
- // are empty. This is due to the fact
- // that the base class has no default
- // constructor (i.e. one without
- // arguments), even though it has a
- // constructor which has default
- // values for all arguments.
-template <int dim>
-class Solution : public Function<dim>
-{
- public:
- Solution () : Function<dim> () {}
-
- virtual double value (const Point<dim> &p,
- const unsigned int component) const;
-};
-
-
-template <int dim>
-double
-Solution<dim>::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
+ RightHandSide<dim>::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>
-class RightHandSide : public Function<dim>
-{
- public:
- RightHandSide () : Function<dim> () {}
-
- virtual double value (const Point<dim> &p,
- const unsigned int component) const;
-};
-
-
-template <int dim>
-double
-RightHandSide<dim>::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);
-}
+ // @sect3{The driver routines}
- // @sect3{The driver routines}
-
- // What is now missing are only the
- // functions that actually select the
- // various options, and run the
- // simulation on successively finer
- // grids to monitor the progress as
- // the mesh is refined.
- //
- // This we do in the following
- // function: it takes a solver
- // object, and a list of
- // postprocessing (evaluation)
- // objects, and runs them with
- // intermittent mesh refinement:
-template <int dim>
-void
-run_simulation (LaplaceSolver::Base<dim> &solver,
- const std::list<Evaluation::EvaluationBase<dim> *> &postprocessor_list)
-{
- // We will give an indicator of the
- // step we are presently computing,
- // in order to keep the user
- // informed that something is still
- // happening, and that the program
- // is not in an endless loop. This
- // is the head of this status line:
- std::cout << "Refinement cycle: ";
-
- // Then start a loop which only
- // terminates once the number of
- // degrees of freedom is larger
- // than 20,000 (you may of course
- // change this limit, if you need
- // more -- or less -- accuracy from
- // your program).
- for (unsigned int step=0; true; ++step)
- {
- // Then give the <code>alive</code>
- // indication for this
- // iteration. Note that the
- // <code>std::flush</code> is needed to
- // have the text actually
- // appear on the screen, rather
- // than only in some buffer
- // that is only flushed the
- // next time we issue an
- // end-line.
- std::cout << step << " " << std::flush;
-
- // Now solve the problem on the
- // present grid, and run the
- // evaluators on it. The long
- // type name of iterators into
- // the list is a little
- // annoying, but could be
- // shortened by a typedef, if
- // so desired.
- solver.solve_problem ();
-
- for (typename std::list<Evaluation::EvaluationBase<dim> *>::const_iterator
- i = postprocessor_list.begin();
- i != postprocessor_list.end(); ++i)
- {
- (*i)->set_refinement_cycle (step);
- solver.postprocess (**i);
- };
+ // What is now missing are only the
+ // functions that actually select the
+ // various options, and run the
+ // simulation on successively finer
+ // grids to monitor the progress as
+ // the mesh is refined.
+ //
+ // This we do in the following
+ // function: it takes a solver
+ // object, and a list of
+ // postprocessing (evaluation)
+ // objects, and runs them with
+ // intermittent mesh refinement:
+ template <int dim>
+ void
+ run_simulation (LaplaceSolver::Base<dim> &solver,
+ const std::list<Evaluation::EvaluationBase<dim> *> &postprocessor_list)
+ {
+ // We will give an indicator of the
+ // step we are presently computing,
+ // in order to keep the user
+ // informed that something is still
+ // happening, and that the program
+ // is not in an endless loop. This
+ // is the head of this status line:
+ std::cout << "Refinement cycle: ";
+
+ // Then start a loop which only
+ // terminates once the number of
+ // degrees of freedom is larger
+ // than 20,000 (you may of course
+ // change this limit, if you need
+ // more -- or less -- accuracy from
+ // your program).
+ for (unsigned int step=0; true; ++step)
+ {
+ // Then give the <code>alive</code>
+ // indication for this
+ // iteration. Note that the
+ // <code>std::flush</code> is needed to
+ // have the text actually
+ // appear on the screen, rather
+ // than only in some buffer
+ // that is only flushed the
+ // next time we issue an
+ // end-line.
+ std::cout << step << " " << std::flush;
+
+ // Now solve the problem on the
+ // present grid, and run the
+ // evaluators on it. The long
+ // type name of iterators into
+ // the list is a little
+ // annoying, but could be
+ // shortened by a typedef, if
+ // so desired.
+ solver.solve_problem ();
+
+ for (typename std::list<Evaluation::EvaluationBase<dim> *>::const_iterator
+ i = postprocessor_list.begin();
+ i != postprocessor_list.end(); ++i)
+ {
+ (*i)->set_refinement_cycle (step);
+ solver.postprocess (**i);
+ };
- // Now check whether more
- // iterations are required, or
- // whether the loop shall be
- // ended:
- if (solver.n_dofs() < 20000)
- solver.refine_grid ();
- else
- break;
- };
+ // Now check whether more
+ // iterations are required, or
+ // whether the loop shall be
+ // ended:
+ if (solver.n_dofs() < 20000)
+ solver.refine_grid ();
+ else
+ break;
+ };
- // Finally end the line in which we
- // displayed status reports:
- std::cout << std::endl;
-}
+ // Finally end the line in which we
+ // displayed status reports:
+ std::cout << std::endl;
+ }
- // The final function is one which
- // takes the name of a solver
- // (presently "kelly" and "global"
- // are allowed), creates a solver
- // object out of it using a coarse
- // grid (in this case the ubiquitous
- // unit square) and a finite element
- // object (here the likewise
- // ubiquitous bilinear one), and uses
- // that solver to ask for the
- // solution of the problem on a
- // sequence of successively refined
- // grids.
- //
- // The function also sets up two of
- // evaluation functions, one
- // evaluating the solution at the
- // point (0.5,0.5), the other writing
- // out the solution to a file.
-template <int dim>
-void solve_problem (const std::string &solver_name)
-{
- // First minor task: tell the user
- // what is going to happen. Thus
- // write a header line, and a line
- // with all '-' characters of the
- // same length as the first one
- // right below.
- const std::string header = "Running tests with \"" + solver_name +
- "\" refinement criterion:";
- std::cout << header << std::endl
- << std::string (header.size(), '-') << std::endl;
-
- // Then set up triangulation,
- // finite element, etc.
- Triangulation<dim> triangulation;
- GridGenerator::hyper_cube (triangulation, -1, 1);
- triangulation.refine_global (2);
- const FE_Q<dim> fe(1);
- const QGauss<dim> quadrature(4);
- const RightHandSide<dim> rhs_function;
- const Solution<dim> boundary_values;
-
- // Create a solver object of the
- // kind indicated by the argument
- // to this function. If the name is
- // not recognized, throw an
- // exception!
- LaplaceSolver::Base<dim> * solver = 0;
- if (solver_name == "global")
- solver = new LaplaceSolver::RefinementGlobal<dim> (triangulation, fe,
- quadrature,
- rhs_function,
- boundary_values);
- else if (solver_name == "kelly")
- solver = new LaplaceSolver::RefinementKelly<dim> (triangulation, fe,
- quadrature,
- rhs_function,
- boundary_values);
- else
- AssertThrow (false, ExcNotImplemented());
-
- // Next create a table object in
- // which the values of the
- // numerical solution at the point
- // (0.5,0.5) will be stored, and
- // create a respective evaluation
- // object:
- TableHandler results_table;
- Evaluation::PointValueEvaluation<dim>
- postprocessor1 (Point<dim>(0.5,0.5), results_table);
-
- // Also generate an evaluator which
- // writes out the solution:
- Evaluation::SolutionOutput<dim>
- postprocessor2 (std::string("solution-")+solver_name,
- DataOut<dim>::gnuplot);
-
- // Take these two evaluation
- // objects and put them in a
- // list...
- std::list<Evaluation::EvaluationBase<dim> *> postprocessor_list;
- postprocessor_list.push_back (&postprocessor1);
- postprocessor_list.push_back (&postprocessor2);
-
- // ... which we can then pass on to
- // the function that actually runs
- // the simulation on successively
- // refined grids:
- run_simulation (*solver, postprocessor_list);
-
- // When this all is done, write out
- // the results of the point
- // evaluations, and finally delete
- // the solver object:
- results_table.write_text (std::cout);
- delete solver;
-
- // And one blank line after all
- // results:
- std::cout << std::endl;
+ // The final function is one which
+ // takes the name of a solver
+ // (presently "kelly" and "global"
+ // are allowed), creates a solver
+ // object out of it using a coarse
+ // grid (in this case the ubiquitous
+ // unit square) and a finite element
+ // object (here the likewise
+ // ubiquitous bilinear one), and uses
+ // that solver to ask for the
+ // solution of the problem on a
+ // sequence of successively refined
+ // grids.
+ //
+ // The function also sets up two of
+ // evaluation functions, one
+ // evaluating the solution at the
+ // point (0.5,0.5), the other writing
+ // out the solution to a file.
+ template <int dim>
+ void solve_problem (const std::string &solver_name)
+ {
+ // First minor task: tell the user
+ // what is going to happen. Thus
+ // write a header line, and a line
+ // with all '-' characters of the
+ // same length as the first one
+ // right below.
+ const std::string header = "Running tests with \"" + solver_name +
+ "\" refinement criterion:";
+ std::cout << header << std::endl
+ << std::string (header.size(), '-') << std::endl;
+
+ // Then set up triangulation,
+ // finite element, etc.
+ Triangulation<dim> triangulation;
+ GridGenerator::hyper_cube (triangulation, -1, 1);
+ triangulation.refine_global (2);
+ const FE_Q<dim> fe(1);
+ const QGauss<dim> quadrature(4);
+ const RightHandSide<dim> rhs_function;
+ const Solution<dim> boundary_values;
+
+ // Create a solver object of the
+ // kind indicated by the argument
+ // to this function. If the name is
+ // not recognized, throw an
+ // exception!
+ LaplaceSolver::Base<dim> * solver = 0;
+ if (solver_name == "global")
+ solver = new LaplaceSolver::RefinementGlobal<dim> (triangulation, fe,
+ quadrature,
+ rhs_function,
+ boundary_values);
+ else if (solver_name == "kelly")
+ solver = new LaplaceSolver::RefinementKelly<dim> (triangulation, fe,
+ quadrature,
+ rhs_function,
+ boundary_values);
+ else
+ AssertThrow (false, ExcNotImplemented());
+
+ // Next create a table object in
+ // which the values of the
+ // numerical solution at the point
+ // (0.5,0.5) will be stored, and
+ // create a respective evaluation
+ // object:
+ TableHandler results_table;
+ Evaluation::PointValueEvaluation<dim>
+ postprocessor1 (Point<dim>(0.5,0.5), results_table);
+
+ // Also generate an evaluator which
+ // writes out the solution:
+ Evaluation::SolutionOutput<dim>
+ postprocessor2 (std::string("solution-")+solver_name,
+ DataOut<dim>::gnuplot);
+
+ // Take these two evaluation
+ // objects and put them in a
+ // list...
+ std::list<Evaluation::EvaluationBase<dim> *> postprocessor_list;
+ postprocessor_list.push_back (&postprocessor1);
+ postprocessor_list.push_back (&postprocessor2);
+
+ // ... which we can then pass on to
+ // the function that actually runs
+ // the simulation on successively
+ // refined grids:
+ run_simulation (*solver, postprocessor_list);
+
+ // When this all is done, write out
+ // the results of the point
+ // evaluations, and finally delete
+ // the solver object:
+ results_table.write_text (std::cout);
+ delete solver;
+
+ // And one blank line after all
+ // results:
+ std::cout << std::endl;
+ }
}
// as much information as possible if
// we should get some. The rest is
// self-explanatory.
-int main ()
+int main ()
{
try
{
- deallog.depth_console (0);
+ dealii::deallog.depth_console (0);
- solve_problem<2> ("global");
- solve_problem<2> ("kelly");
+ Step13::solve_problem<2> ("global");
+ Step13::solve_problem<2> ("kelly");
}
catch (std::exception &exc)
{
<< std::endl;
return 1;
}
- catch (...)
+ catch (...)
{
std::cerr << std::endl << std::endl
<< "----------------------------------------------------"
/* $Id$ */
/* */
-/* Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 by the deal.II authors */
+/* Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 by the deal.II authors */
/* */
/* This file is subject to QPL and may not be distributed */
/* without copyright and license information. Please refer */
// The last step is as in all
// previous programs:
-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
+namespace Step14
{
- // @sect4{The EvaluationBase class}
- template <int dim>
- class EvaluationBase
- {
- public:
- virtual ~EvaluationBase ();
-
- 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;
- };
+ using namespace dealii;
+ // @sect3{Evaluating the solution}
- template <int dim>
- EvaluationBase<dim>::~EvaluationBase ()
- {}
-
+ // 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 ();
-
- template <int dim>
- void
- EvaluationBase<dim>::set_refinement_cycle (const unsigned int step)
- {
- refinement_cycle = step;
- }
+ 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;
+ };
- // @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;
-
- 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>
+ EvaluationBase<dim>::~EvaluationBase ()
+ {}
- 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;
-
- typename DoFHandler<dim>::active_cell_iterator
- cell = dof_handler.begin_active(),
- endc = dof_handler.end();
- bool evaluation_point_found = false;
- for (; (cell!=endc) && !evaluation_point_found; ++cell)
- for (unsigned int vertex=0;
- vertex<GeometryInfo<dim>::vertices_per_cell;
- ++vertex)
- if (cell->vertex(vertex).distance (evaluation_point)
- <
- cell->diameter() * 1e-8)
- {
- point_value = solution(cell->vertex_dof_index(vertex,0));
+ template <int dim>
+ void
+ EvaluationBase<dim>::set_refinement_cycle (const unsigned int step)
+ {
+ refinement_cycle = step;
+ }
- evaluation_point_found = true;
- break;
- }
- AssertThrow (evaluation_point_found,
- ExcEvaluationPointNotFound(evaluation_point));
+ // @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;
+
+ DeclException1 (ExcEvaluationPointNotFound,
+ Point<dim>,
+ << "The evaluation point " << arg1
+ << " was not found among the vertices of the present grid.");
+ private:
+ const Point<dim> evaluation_point;
+ };
- std::cout << " Point value=" << point_value
- << std::endl;
- }
+ template <int dim>
+ PointValueEvaluation<dim>::
+ PointValueEvaluation (const Point<dim> &evaluation_point)
+ :
+ evaluation_point (evaluation_point)
+ {}
- // @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)
- {}
-
+ template <int dim>
+ void
+ PointValueEvaluation<dim>::
+ operator () (const DoFHandler<dim> &dof_handler,
+ const Vector<double> &solution) const
+ {
+ double point_value = 1e20;
- // 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 wil become
- // clear below...
- QTrapez<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:
- typename DoFHandler<dim>::active_cell_iterator
- cell = dof_handler.begin_active(),
- endc = dof_handler.end();
- unsigned int evaluation_point_hits = 0;
- for (; cell!=endc; ++cell)
- for (unsigned int vertex=0;
- vertex<GeometryInfo<dim>::vertices_per_cell;
- ++vertex)
- 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_grads (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;
- }
+ typename DoFHandler<dim>::active_cell_iterator
+ cell = dof_handler.begin_active(),
+ endc = dof_handler.end();
+ bool evaluation_point_found = false;
+ for (; (cell!=endc) && !evaluation_point_found; ++cell)
+ for (unsigned int vertex=0;
+ vertex<GeometryInfo<dim>::vertices_per_cell;
+ ++vertex)
+ if (cell->vertex(vertex).distance (evaluation_point)
+ <
+ cell->diameter() * 1e-8)
+ {
+ point_value = solution(cell->vertex_dof_index(vertex,0));
- // 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;
- }
+ evaluation_point_found = true;
+ break;
+ }
+ AssertThrow (evaluation_point_found,
+ ExcEvaluationPointNotFound(evaluation_point));
-
- // @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;
- private:
- const std::string output_name_base;
- };
+ std::cout << " Point value=" << point_value
+ << std::endl;
+ }
- template <int dim>
- GridOutput<dim>::
- GridOutput (const std::string &output_name_base)
- :
- output_name_base (output_name_base)
- {}
-
+ // @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>
- void
- GridOutput<dim>::operator () (const DoFHandler<dim> &dof_handler,
- const Vector<double> &/*solution*/) const
- {
- std::ostringstream filename;
- filename << output_name_base << "-"
- << this->refinement_cycle
- << ".eps"
- << std::ends;
-
- std::ofstream out (filename.str().c_str());
- GridOut().write_eps (dof_handler.get_tria(), out);
- }
-}
-
- // @sect3{The Laplace solver classes}
+ template <int dim>
+ PointXDerivativeEvaluation<dim>::
+ PointXDerivativeEvaluation (const Point<dim> &evaluation_point)
+ :
+ evaluation_point (evaluation_point)
+ {}
- // Next are the actual solver
- // classes. Again, we discuss only
- // the differences to the previous
- // program.
-namespace LaplaceSolver
-{
- // Before everything else,
- // forward-declare one class that
- // we will have later, since we
- // will want to make it a friend of
- // some of the classes that follow,
- // which requires the class to be
- // known:
- template <int dim> class WeightedResidual;
-
-
- // @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 ();
- 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;
+ // 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 wil become
+ // clear below...
+ QTrapez<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:
+ typename DoFHandler<dim>::active_cell_iterator
+ cell = dof_handler.begin_active(),
+ endc = dof_handler.end();
+ unsigned int evaluation_point_hits = 0;
+ for (; cell!=endc; ++cell)
+ for (unsigned int vertex=0;
+ vertex<GeometryInfo<dim>::vertices_per_cell;
+ ++vertex)
+ 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_grads (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;
+ }
- virtual void set_refinement_cycle (const unsigned int cycle);
+ // 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;
+ }
- virtual void output_solution () const = 0;
-
- protected:
- const SmartPointer<Triangulation<dim> > triangulation;
- unsigned int refinement_cycle;
- };
+ // @sect4{The GridOutput class}
- template <int dim>
- Base<dim>::Base (Triangulation<dim> &coarse_grid)
- :
- triangulation (&coarse_grid)
- {}
+ // 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;
+ private:
+ const std::string output_name_base;
+ };
- template <int dim>
- Base<dim>::~Base ()
- {}
+ template <int dim>
+ GridOutput<dim>::
+ GridOutput (const std::string &output_name_base)
+ :
+ output_name_base (output_name_base)
+ {}
- template <int dim>
- void
- Base<dim>::set_refinement_cycle (const unsigned int cycle)
- {
- refinement_cycle = cycle;
+ template <int dim>
+ void
+ GridOutput<dim>::operator () (const DoFHandler<dim> &dof_handler,
+ const Vector<double> &/*solution*/) const
+ {
+ std::ostringstream filename;
+ filename << output_name_base << "-"
+ << this->refinement_cycle
+ << ".eps"
+ << std::ends;
+
+ std::ofstream out (filename.str().c_str());
+ GridOut().write_eps (dof_handler.get_tria(), out);
+ }
}
-
- // @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 ();
-
- virtual
- void
- solve_problem ();
-
- virtual
- void
- postprocess (const Evaluation::EvaluationBase<dim> &postprocessor) const;
-
- virtual
- unsigned int
- n_dofs () const;
-
- 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;
-
- ConstraintMatrix hanging_node_constraints;
- SparsityPattern sparsity_pattern;
- SparseMatrix<double> matrix;
- Vector<double> rhs;
- };
+ // @sect3{The Laplace solver classes}
+
+ // Next are the actual solver
+ // classes. Again, we discuss only
+ // the differences to the previous
+ // program.
+ namespace LaplaceSolver
+ {
+ // Before everything else,
+ // forward-declare one class that
+ // we will have later, since we
+ // will want to make it a friend of
+ // some of the classes that follow,
+ // which requires the class to be
+ // known:
+ template <int dim> class WeightedResidual;
+
+
+ // @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 ();
- void
- assemble_linear_system (LinearSystem &linear_system);
+ 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;
- void
- assemble_matrix (LinearSystem &linear_system,
- const typename DoFHandler<dim>::active_cell_iterator &begin_cell,
- const typename DoFHandler<dim>::active_cell_iterator &end_cell,
- Threads::ThreadMutex &mutex) const;
- };
+ virtual void set_refinement_cycle (const unsigned int cycle);
+ virtual void output_solution () const = 0;
+ protected:
+ const SmartPointer<Triangulation<dim> > triangulation;
- 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)
- {}
+ unsigned int refinement_cycle;
+ };
- template <int dim>
- Solver<dim>::~Solver ()
- {
- dof_handler.clear ();
- }
+ template <int dim>
+ Base<dim>::Base (Triangulation<dim> &coarse_grid)
+ :
+ triangulation (&coarse_grid)
+ {}
- template <int dim>
- void
- Solver<dim>::solve_problem ()
- {
- dof_handler.distribute_dofs (*fe);
- solution.reinit (dof_handler.n_dofs());
+ template <int dim>
+ Base<dim>::~Base ()
+ {}
- 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>
+ void
+ Base<dim>::set_refinement_cycle (const unsigned int cycle)
+ {
+ refinement_cycle = cycle;
+ }
- template <int dim>
- unsigned int
- Solver<dim>::n_dofs () const
- {
- return dof_handler.n_dofs();
- }
-
+ // @sect4{The Laplace Solver class}
- template <int dim>
- void
- Solver<dim>::assemble_linear_system (LinearSystem &linear_system)
- {
- typedef
- typename DoFHandler<dim>::active_cell_iterator
- active_cell_iterator;
-
- const unsigned int n_threads = multithread_info.n_default_threads;
- std::vector<std::pair<active_cell_iterator,active_cell_iterator> >
- thread_ranges
- = Threads::split_range<active_cell_iterator> (dof_handler.begin_active (),
- dof_handler.end (),
- n_threads);
-
- Threads::ThreadMutex mutex;
- Threads::ThreadGroup<> threads;
- for (unsigned int thread=0; thread<n_threads; ++thread)
- threads += Threads::new_thread (&Solver<dim>::assemble_matrix,
- *this,
- linear_system,
- thread_ranges[thread].first,
- thread_ranges[thread].second,
- mutex);
-
- assemble_rhs (linear_system.rhs);
- linear_system.hanging_node_constraints.condense (linear_system.rhs);
-
- std::map<unsigned int,double> boundary_value_map;
- VectorTools::interpolate_boundary_values (dof_handler,
- 0,
- *boundary_values,
- boundary_value_map);
-
- threads.join_all ();
- linear_system.hanging_node_constraints.condense (linear_system.matrix);
-
- MatrixTools::apply_boundary_values (boundary_value_map,
- linear_system.matrix,
- solution,
- linear_system.rhs);
- }
+ // 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 ();
+
+ virtual
+ void
+ solve_problem ();
+
+ virtual
+ void
+ postprocess (const Evaluation::EvaluationBase<dim> &postprocessor) const;
+
+ virtual
+ unsigned int
+ n_dofs () const;
+
+ 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;
- template <int dim>
- void
- Solver<dim>::assemble_matrix (LinearSystem &linear_system,
- const typename DoFHandler<dim>::active_cell_iterator &begin_cell,
- const typename DoFHandler<dim>::active_cell_iterator &end_cell,
- Threads::ThreadMutex &mutex) const
- {
- FEValues<dim> fe_values (*fe, *quadrature,
- update_gradients | update_JxW_values);
+ ConstraintMatrix hanging_node_constraints;
+ SparsityPattern sparsity_pattern;
+ SparseMatrix<double> matrix;
+ Vector<double> rhs;
+ };
+
+ void
+ assemble_linear_system (LinearSystem &linear_system);
- const unsigned int dofs_per_cell = fe->dofs_per_cell;
- const unsigned int n_q_points = quadrature->size();
+ void
+ assemble_matrix (LinearSystem &linear_system,
+ const typename DoFHandler<dim>::active_cell_iterator &begin_cell,
+ const typename DoFHandler<dim>::active_cell_iterator &end_cell,
+ Threads::ThreadMutex &mutex) const;
+ };
- FullMatrix<double> cell_matrix (dofs_per_cell, dofs_per_cell);
- std::vector<unsigned int> local_dof_indices (dofs_per_cell);
- for (typename DoFHandler<dim>::active_cell_iterator cell=begin_cell;
- cell!=end_cell; ++cell)
- {
- cell_matrix = 0;
+ 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();
+ }
- fe_values.reinit (cell);
- for (unsigned int q_point=0; q_point<n_q_points; ++q_point)
+ template <int dim>
+ void
+ Solver<dim>::assemble_linear_system (LinearSystem &linear_system)
+ {
+ typedef
+ typename DoFHandler<dim>::active_cell_iterator
+ active_cell_iterator;
+
+ const unsigned int n_threads = multithread_info.n_default_threads;
+ std::vector<std::pair<active_cell_iterator,active_cell_iterator> >
+ thread_ranges
+ = Threads::split_range<active_cell_iterator> (dof_handler.begin_active (),
+ dof_handler.end (),
+ n_threads);
+
+ Threads::ThreadMutex mutex;
+ Threads::ThreadGroup<> threads;
+ for (unsigned int thread=0; thread<n_threads; ++thread)
+ threads += Threads::new_thread (&Solver<dim>::assemble_matrix,
+ *this,
+ linear_system,
+ thread_ranges[thread].first,
+ thread_ranges[thread].second,
+ mutex);
+
+ assemble_rhs (linear_system.rhs);
+ linear_system.hanging_node_constraints.condense (linear_system.rhs);
+
+ std::map<unsigned int,double> boundary_value_map;
+ VectorTools::interpolate_boundary_values (dof_handler,
+ 0,
+ *boundary_values,
+ boundary_value_map);
+
+ threads.join_all ();
+ linear_system.hanging_node_constraints.condense (linear_system.matrix);
+
+ MatrixTools::apply_boundary_values (boundary_value_map,
+ linear_system.matrix,
+ solution,
+ linear_system.rhs);
+ }
+
+
+ template <int dim>
+ void
+ Solver<dim>::assemble_matrix (LinearSystem &linear_system,
+ const typename DoFHandler<dim>::active_cell_iterator &begin_cell,
+ const typename DoFHandler<dim>::active_cell_iterator &end_cell,
+ Threads::ThreadMutex &mutex) const
+ {
+ FEValues<dim> fe_values (*fe, *quadrature,
+ update_gradients | update_JxW_values);
+
+ const unsigned int dofs_per_cell = fe->dofs_per_cell;
+ const unsigned int n_q_points = quadrature->size();
+
+ FullMatrix<double> cell_matrix (dofs_per_cell, dofs_per_cell);
+
+ std::vector<unsigned int> local_dof_indices (dofs_per_cell);
+
+ for (typename DoFHandler<dim>::active_cell_iterator cell=begin_cell;
+ cell!=end_cell; ++cell)
+ {
+ cell_matrix = 0;
+
+ 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)
+ cell_matrix(i,j) += (fe_values.shape_grad(i,q_point) *
+ fe_values.shape_grad(j,q_point) *
+ fe_values.JxW(q_point));
+
+
+ cell->get_dof_indices (local_dof_indices);
+ Threads::ThreadMutex::ScopedLock lock (mutex);
for (unsigned int i=0; i<dofs_per_cell; ++i)
for (unsigned int j=0; j<dofs_per_cell; ++j)
- cell_matrix(i,j) += (fe_values.shape_grad(i,q_point) *
- fe_values.shape_grad(j,q_point) *
- fe_values.JxW(q_point));
-
-
- cell->get_dof_indices (local_dof_indices);
- Threads::ThreadMutex::ScopedLock lock (mutex);
- for (unsigned int i=0; i<dofs_per_cell; ++i)
- for (unsigned int j=0; j<dofs_per_cell; ++j)
- linear_system.matrix.add (local_dof_indices[i],
- local_dof_indices[j],
- cell_matrix(i,j));
- }
- }
+ linear_system.matrix.add (local_dof_indices[i],
+ local_dof_indices[j],
+ cell_matrix(i,j));
+ }
+ }
- template <int dim>
- Solver<dim>::LinearSystem::
- LinearSystem (const DoFHandler<dim> &dof_handler)
- {
- hanging_node_constraints.clear ();
-
- void (*mhnc_p) (const DoFHandler<dim> &,
- ConstraintMatrix &)
- = &DoFTools::make_hanging_node_constraints;
-
- Threads::Thread<>
- mhnc_thread = Threads::new_thread (mhnc_p,
- dof_handler,
- hanging_node_constraints);
-
- sparsity_pattern.reinit (dof_handler.n_dofs(),
- dof_handler.n_dofs(),
- dof_handler.max_couplings_between_dofs());
- DoFTools::make_sparsity_pattern (dof_handler, sparsity_pattern);
-
- mhnc_thread.join ();
- hanging_node_constraints.close ();
- hanging_node_constraints.condense (sparsity_pattern);
-
- sparsity_pattern.compress();
- matrix.reinit (sparsity_pattern);
- rhs.reinit (dof_handler.n_dofs());
- }
+ template <int dim>
+ Solver<dim>::LinearSystem::
+ LinearSystem (const DoFHandler<dim> &dof_handler)
+ {
+ hanging_node_constraints.clear ();
+ void (*mhnc_p) (const DoFHandler<dim> &,
+ ConstraintMatrix &)
+ = &DoFTools::make_hanging_node_constraints;
+ Threads::Thread<>
+ mhnc_thread = Threads::new_thread (mhnc_p,
+ dof_handler,
+ hanging_node_constraints);
- template <int dim>
- void
- Solver<dim>::LinearSystem::solve (Vector<double> &solution) const
- {
- SolverControl solver_control (5000, 1e-12);
- SolverCG<> cg (solver_control);
+ sparsity_pattern.reinit (dof_handler.n_dofs(),
+ dof_handler.n_dofs(),
+ dof_handler.max_couplings_between_dofs());
+ DoFTools::make_sparsity_pattern (dof_handler, sparsity_pattern);
- PreconditionSSOR<> preconditioner;
- preconditioner.initialize(matrix, 1.2);
+ mhnc_thread.join ();
+ hanging_node_constraints.close ();
+ hanging_node_constraints.condense (sparsity_pattern);
- cg.solve (matrix, solution, rhs, preconditioner);
+ sparsity_pattern.compress();
+ matrix.reinit (sparsity_pattern);
+ rhs.reinit (dof_handler.n_dofs());
+ }
- hanging_node_constraints.distribute (solution);
- }
+ template <int dim>
+ void
+ Solver<dim>::LinearSystem::solve (Vector<double> &solution) const
+ {
+ SolverControl solver_control (5000, 1e-12);
+ SolverCG<> cg (solver_control);
+ PreconditionSSOR<> preconditioner;
+ preconditioner.initialize(matrix, 1.2);
- // @sect4{The PrimalSolver class}
-
- // The <code>PrimalSolver</code> class is
- // also mostly unchanged except for
- // overloading the functions
- // <code>solve_problem</code>, <code>n_dofs</code>,
- // and <code>postprocess</code> of the base
- // class, and implementing the
- // <code>output_solution</code>
- // function. These overloaded
- // functions do nothing particular
- // besides calling the functions of
- // the base class -- that seems
- // superfluous, but works around a
- // bug in a popular compiler which
- // requires us to write such
- // functions for the following
- // scenario: Besides the
- // <code>PrimalSolver</code> class, we will
- // have a <code>DualSolver</code>, both
- // derived from <code>Solver</code>. We will
- // then have a final classes which
- // derived from these two, which
- // will then have two instances of
- // the <code>Solver</code> class as its base
- // classes. If we want, for
- // example, the number of degrees
- // of freedom of the primal solver,
- // we would have to indicate this
- // like so:
- // <code>PrimalSolver::n_dofs()</code>.
- // However, the compiler does not
- // accept this since the <code>n_dofs</code>
- // function is actually from a base
- // class of the <code>PrimalSolver</code>
- // class, so we have to inject the
- // name from the base to the
- // derived class using these
- // additional functions.
- //
- // Regarding the implementation of
- // 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.
- //
- // Except for this, the class is
- // unchanged with respect to the
- // previous example.
- 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 solve_problem ();
-
- virtual
- unsigned int n_dofs () const;
-
- virtual
- void postprocess (const Evaluation::EvaluationBase<dim> &postprocessor) const;
-
- virtual
- void output_solution () const;
-
- protected:
- const SmartPointer<const Function<dim> > rhs_function;
- virtual void assemble_rhs (Vector<double> &rhs) const;
-
- // Now, in order to work around
- // some problems in one of the
- // compilers this library can
- // be compiled with, we will
- // have to declare a
- // class that is actually
- // derived from the present
- // one, as a friend (strange as
- // that seems). The full
- // rationale will be explained
- // below.
- friend class WeightedResidual<dim>;
- };
+ cg.solve (matrix, solution, rhs, preconditioner);
+ hanging_node_constraints.distribute (solution);
+ }
- 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>::solve_problem ()
- {
- Solver<dim>::solve_problem ();
- }
+ // @sect4{The PrimalSolver class}
+
+ // The <code>PrimalSolver</code> class is
+ // also mostly unchanged except for
+ // overloading the functions
+ // <code>solve_problem</code>, <code>n_dofs</code>,
+ // and <code>postprocess</code> of the base
+ // class, and implementing the
+ // <code>output_solution</code>
+ // function. These overloaded
+ // functions do nothing particular
+ // besides calling the functions of
+ // the base class -- that seems
+ // superfluous, but works around a
+ // bug in a popular compiler which
+ // requires us to write such
+ // functions for the following
+ // scenario: Besides the
+ // <code>PrimalSolver</code> class, we will
+ // have a <code>DualSolver</code>, both
+ // derived from <code>Solver</code>. We will
+ // then have a final classes which
+ // derived from these two, which
+ // will then have two instances of
+ // the <code>Solver</code> class as its base
+ // classes. If we want, for
+ // example, the number of degrees
+ // of freedom of the primal solver,
+ // we would have to indicate this
+ // like so:
+ // <code>PrimalSolver::n_dofs()</code>.
+ // However, the compiler does not
+ // accept this since the <code>n_dofs</code>
+ // function is actually from a base
+ // class of the <code>PrimalSolver</code>
+ // class, so we have to inject the
+ // name from the base to the
+ // derived class using these
+ // additional functions.
+ //
+ // Regarding the implementation of
+ // 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.
+ //
+ // Except for this, the class is
+ // unchanged with respect to the
+ // previous example.
+ 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 solve_problem ();
+
+ virtual
+ unsigned int n_dofs () const;
+
+ virtual
+ void postprocess (const Evaluation::EvaluationBase<dim> &postprocessor) const;
+
+ virtual
+ void output_solution () const;
+
+ protected:
+ const SmartPointer<const Function<dim> > rhs_function;
+ virtual void assemble_rhs (Vector<double> &rhs) const;
+
+ // Now, in order to work around
+ // some problems in one of the
+ // compilers this library can
+ // be compiled with, we will
+ // have to declare a
+ // class that is actually
+ // derived from the present
+ // one, as a friend (strange as
+ // that seems). The full
+ // rationale will be explained
+ // below.
+ friend class WeightedResidual<dim>;
+ };
- template <int dim>
- unsigned int
- PrimalSolver<dim>::n_dofs() const
- {
- return Solver<dim>::n_dofs();
- }
+ 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>::solve_problem ()
+ {
+ Solver<dim>::solve_problem ();
+ }
- template <int dim>
- void
- PrimalSolver<dim>::
- postprocess (const Evaluation::EvaluationBase<dim> &postprocessor) const
- {
- Solver<dim>::postprocess(postprocessor);
- }
+ template <int dim>
+ unsigned int
+ PrimalSolver<dim>::n_dofs() const
+ {
+ return Solver<dim>::n_dofs();
+ }
- 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::ostringstream filename;
- filename << "solution-"
- << this->refinement_cycle
- << ".gnuplot"
- << std::ends;
-
- std::ofstream out (filename.str().c_str());
- data_out.write (out, DataOut<dim>::gnuplot);
- }
-
+ template <int dim>
+ void
+ PrimalSolver<dim>::
+ postprocess (const Evaluation::EvaluationBase<dim> &postprocessor) const
+ {
+ Solver<dim>::postprocess(postprocessor);
+ }
- 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->dofs_per_cell;
- const unsigned int n_q_points = this->quadrature->size();
+ 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::ostringstream filename;
+ filename << "solution-"
+ << this->refinement_cycle
+ << ".gnuplot"
+ << std::ends;
+
+ std::ofstream out (filename.str().c_str());
+ data_out.write (out, DataOut<dim>::gnuplot);
+ }
- Vector<double> cell_rhs (dofs_per_cell);
- std::vector<double> rhs_values (n_q_points);
- std::vector<unsigned int> local_dof_indices (dofs_per_cell);
- typename DoFHandler<dim>::active_cell_iterator
- cell = this->dof_handler.begin_active(),
- endc = this->dof_handler.end();
- for (; cell!=endc; ++cell)
- {
- cell_rhs = 0;
- fe_values.reinit (cell);
+ 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->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<unsigned int> local_dof_indices (dofs_per_cell);
+
+ typename DoFHandler<dim>::active_cell_iterator
+ cell = this->dof_handler.begin_active(),
+ endc = this->dof_handler.end();
+ for (; cell!=endc; ++cell)
+ {
+ 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) *
+ rhs_values[q_point] *
+ fe_values.JxW(q_point));
- rhs_function->value_list (fe_values.get_quadrature_points(),
- rhs_values);
-
- for (unsigned int q_point=0; q_point<n_q_points; ++q_point)
+ cell->get_dof_indices (local_dof_indices);
for (unsigned int i=0; i<dofs_per_cell; ++i)
- cell_rhs(i) += (fe_values.shape_value(i,q_point) *
- rhs_values[q_point] *
- fe_values.JxW(q_point));
+ rhs(local_dof_indices[i]) += cell_rhs(i);
+ }
+ }
- 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}
- // @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 ();
+ };
- // 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 ();
- };
+ 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>
- 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>
- 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 ();
+ };
- 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 ();
- };
+ 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>
- 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>(3),
+ typename FunctionMap<dim>::type(),
+ 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 ();
+ }
- 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>(3),
- typename FunctionMap<dim>::type(),
- 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 ();
+
+ 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 is
+ // computed can be read in the
+ // documentation of that class.
+ Vector<float> estimated_error (this->triangulation->n_active_cells());
+ KellyErrorEstimator<dim>::estimate (this->dof_handler,
+ *this->face_quadrature,
+ typename FunctionMap<dim>::type(),
+ this->solution,
+ estimated_error);
+
+ // Now we are going to weight
+ // these indicators by the value
+ // of the function given to the
+ // constructor:
+ typename DoFHandler<dim>::active_cell_iterator
+ cell = this->dof_handler.begin_active(),
+ endc = this->dof_handler.end();
+ for (unsigned int cell_index=0; cell!=endc; ++cell, ++cell_index)
+ estimated_error(cell_index)
+ *= weighting_function->value (cell->center());
+
+ GridRefinement::refine_and_coarsen_fixed_number (*this->triangulation,
+ estimated_error,
+ 0.3, 0.03);
+ this->triangulation->execute_coarsening_and_refinement ();
+ }
+
}
+ // @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;
- // @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 ();
-
- private:
- const SmartPointer<const Function<dim> > weighting_function;
- };
+ virtual
+ const Function<dim> & get_right_hand_side () const = 0;
+ virtual
+ void create_coarse_grid (Triangulation<dim> &coarse_grid) const = 0;
+ };
- 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)
- {}
+ // And now for the derived class
+ // that takes the template argument
+ // as explained above. For some
+ // reason, C++ requires us to
+ // define a constructor (which
+ // maybe empty), as otherwise a
+ // warning is generated that some
+ // data is not initialized.
+ //
+ // 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>
+ {
+ SetUp () {}
+ virtual
+ const Function<dim> & get_boundary_values () const;
+ virtual
+ const Function<dim> & get_right_hand_side () const;
- // 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 is
- // computed can be read in the
- // documentation of that class.
- Vector<float> estimated_error (this->triangulation->n_active_cells());
- KellyErrorEstimator<dim>::estimate (this->dof_handler,
- *this->face_quadrature,
- typename FunctionMap<dim>::type(),
- this->solution,
- estimated_error);
-
- // Now we are going to weight
- // these indicators by the value
- // of the function given to the
- // constructor:
- typename DoFHandler<dim>::active_cell_iterator
- cell = this->dof_handler.begin_active(),
- endc = this->dof_handler.end();
- for (unsigned int cell_index=0; cell!=endc; ++cell, ++cell_index)
- estimated_error(cell_index)
- *= weighting_function->value (cell->center());
-
- GridRefinement::refine_and_coarsen_fixed_number (*this->triangulation,
- estimated_error,
- 0.3, 0.03);
- this->triangulation->execute_coarsening_and_refinement ();
- }
-}
+ virtual
+ void create_coarse_grid (Triangulation<dim> &coarse_grid) const;
+ private:
+ static const typename Traits::BoundaryValues boundary_values;
+ static const typename Traits::RightHandSide right_hand_side;
+ };
- // @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;
+ // 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;
+ }
- virtual
- const Function<dim> & get_right_hand_side () const = 0;
- virtual
- void create_coarse_grid (Triangulation<dim> &coarse_grid) const = 0;
- };
+ template <class Traits, int dim>
+ const Function<dim> &
+ SetUp<Traits,dim>::get_right_hand_side () const
+ {
+ return right_hand_side;
+ }
- // And now for the derived class
- // that takes the template argument
- // as explained above. For some
- // reason, C++ requires us to
- // define a constructor (which
- // maybe empty), as otherwise a
- // warning is generated that some
- // data is not initialized.
- //
- // 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>
- {
- SetUp () {}
+ template <class Traits, int dim>
+ void
+ SetUp<Traits,dim>::
+ create_coarse_grid (Triangulation<dim> &coarse_grid) const
+ {
+ Traits::create_coarse_grid (coarse_grid);
+ }
- virtual
- const Function<dim> & get_boundary_values () const;
- virtual
- const Function<dim> & get_right_hand_side () const;
-
+ // @sect4{The CurvedRidges class}
- virtual
- void create_coarse_grid (Triangulation<dim> &coarse_grid) const;
+ // 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:
+ BoundaryValues () : Function<dim> () {}
- private:
- static const typename Traits::BoundaryValues boundary_values;
- static const typename Traits::RightHandSide right_hand_side;
- };
+ virtual double value (const Point<dim> &p,
+ const unsigned int component) const;
+ };
- // 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;
- }
+ class RightHandSide : public Function<dim>
+ {
+ public:
+ RightHandSide () : Function<dim> () {}
- template <class Traits, int dim>
- const Function<dim> &
- SetUp<Traits,dim>::get_right_hand_side () const
- {
- return right_hand_side;
- }
+ virtual double value (const Point<dim> &p,
+ const unsigned int component) const;
+ };
+ static
+ void
+ create_coarse_grid (Triangulation<dim> &coarse_grid);
+ };
- 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}
+ 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;
+ }
- // 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:
- BoundaryValues () : Function<dim> () {}
-
- virtual double value (const Point<dim> &p,
- const unsigned int component) const;
- };
- class RightHandSide : public Function<dim>
- {
- public:
- RightHandSide () : Function<dim> () {}
-
- virtual double value (const Point<dim> &p,
- const unsigned int component) const;
- };
+ 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;
- 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;
- }
+ 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);
+ }
- 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);
- }
+ // @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 a
+ // typedef:
+ typedef ZeroFunction<dim> BoundaryValues;
+
+ // 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 ConstantFunction<dim>
+ {
+ public:
+ RightHandSide () : ConstantFunction<dim> (1.) {}
+ };
- 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 a
- // typedef:
- typedef ZeroFunction<dim> BoundaryValues;
-
- // 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 ConstantFunction<dim>
- {
- public:
- RightHandSide () : 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);
- };
+ // 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.
- //
- // 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)
- {
- // 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
- // takes this as a starting point
- // to implement the 3d version.
- const unsigned int dim = 2;
-
- // Then 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. Note
- // that we leave the number of
- // vertices open at first, but
- // then let the compiler compute
- // this number afterwards. This
- // reduces the possibility of
- // having the dimension to large
- // and leaving the last ones
- // uninitialized.
- static const Point<2> vertices_1[]
- = { Point<2> (-1., -1.),
+ // 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.
+ //
+ // 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)
+ {
+ // 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
+ // takes this as a starting point
+ // to implement the 3d version.
+ const unsigned int dim = 2;
+
+ // Then 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. Note
+ // that we leave the number of
+ // vertices open at first, but
+ // then let the compiler compute
+ // this number afterwards. This
+ // reduces the possibility of
+ // having the dimension to large
+ // and leaving the last ones
+ // uninitialized.
+ static const Point<2> vertices_1[]
+ = { Point<2> (-1., -1.),
Point<2> (-1./2, -1.),
Point<2> (0., -1.),
Point<2> (+1./2, -1.),
Point<2> (+1, -1.),
-
+
Point<2> (-1., -1./2.),
Point<2> (-1./2, -1./2.),
Point<2> (0., -1./2.),
Point<2> (+1./2, -1./2.),
Point<2> (+1, -1./2.),
-
+
Point<2> (-1., 0.),
Point<2> (-1./2, 0.),
Point<2> (+1./2, 0.),
Point<2> (+1, 0.),
-
+
Point<2> (-1., 1./2.),
Point<2> (-1./2, 1./2.),
Point<2> (0., 1./2.),
Point<2> (+1./2, 1./2.),
Point<2> (+1, 1./2.),
-
+
Point<2> (-1., 1.),
Point<2> (-1./2, 1.),
- Point<2> (0., 1.),
+ Point<2> (0., 1.),
Point<2> (+1./2, 1.),
Point<2> (+1, 1.) };
- const unsigned int
- n_vertices = sizeof(vertices_1) / sizeof(vertices_1[0]);
-
- // From this static list of
- // vertices, we generate an STL
- // vector of the vertices, as
- // this is the data type the
- // library wants to see.
- const std::vector<Point<dim> > vertices (&vertices_1[0],
- &vertices_1[n_vertices]);
-
- // Next, we have to define the
- // cells and the vertices they
- // contain. Here, we have 8
- // vertices, but leave the number
- // open and let it be computed
- // afterwards:
- static const int cell_vertices[][GeometryInfo<dim>::vertices_per_cell]
- = {{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 = sizeof(cell_vertices) / sizeof(cell_vertices[0]);
-
- // 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<GeometryInfo<dim>::vertices_per_cell;
- ++j)
- cells[i].vertices[j] = cell_vertices[i][j];
- cells[i].material_id = 0;
- }
+ const unsigned int
+ n_vertices = sizeof(vertices_1) / sizeof(vertices_1[0]);
+
+ // From this static list of
+ // vertices, we generate an STL
+ // vector of the vertices, as
+ // this is the data type the
+ // library wants to see.
+ const std::vector<Point<dim> > vertices (&vertices_1[0],
+ &vertices_1[n_vertices]);
+
+ // Next, we have to define the
+ // cells and the vertices they
+ // contain. Here, we have 8
+ // vertices, but leave the number
+ // open and let it be computed
+ // afterwards:
+ static const int cell_vertices[][GeometryInfo<dim>::vertices_per_cell]
+ = {{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 = sizeof(cell_vertices) / sizeof(cell_vertices[0]);
+
+ // 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<GeometryInfo<dim>::vertices_per_cell;
+ ++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);
+ // 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);
+ }
}
-}
- // @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
- // loosing 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 comlementary.
-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{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
+ // loosing 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 comlementary.
+ 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 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;
-
- DeclException1 (ExcEvaluationPointNotFound,
- Point<dim>,
- << "The evaluation point " << arg1
- << " was not found among the vertices of the present grid.");
-
- protected:
- const Point<dim> evaluation_point;
- };
+ // @sect4{The 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;
- 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):
- typename DoFHandler<dim>::active_cell_iterator
- cell = dof_handler.begin_active(),
- endc = dof_handler.end();
- for (; cell!=endc; ++cell)
- for (unsigned int vertex=0;
- vertex<GeometryInfo<dim>::vertices_per_cell;
- ++vertex)
- if (cell->vertex(vertex).distance(evaluation_point)
- < cell->diameter()*1e-8)
+ 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):
+ typename DoFHandler<dim>::active_cell_iterator
+ cell = dof_handler.begin_active(),
+ endc = dof_handler.end();
+ for (; cell!=endc; ++cell)
+ for (unsigned int vertex=0;
+ vertex<GeometryInfo<dim>::vertices_per_cell;
+ ++vertex)
+ 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 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(4);
+ 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:
+ typename DoFHandler<dim>::active_cell_iterator
+ cell = dof_handler.begin_active(),
+ endc = dof_handler.end();
+ for (; cell!=endc; ++cell)
+ if (cell->center().distance(evaluation_point) <=
+ cell->diameter())
{
- // Ok, found, so set
- // corresponding entry,
- // and leave function
- // since we are finished:
- rhs(cell->vertex_dof_index(vertex,0)) = 1;
- return;
- }
+ // 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;
- // 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));
- }
+ 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] *
+ fe_values.JxW (q);
+ 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);
+ }
- // @sect4{The 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;
- };
+ // 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.scale (1./total_volume);
+ }
- 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
+
+ // @sect3{Extending the LaplaceSolver namespace}
+ namespace LaplaceSolver
{
- // 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(4);
- 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:
- typename DoFHandler<dim>::active_cell_iterator
- cell = dof_handler.begin_active(),
- endc = dof_handler.end();
- for (; cell!=endc; ++cell)
- 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] *
- fe_values.JxW (q);
- 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);
- }
+ // @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);
- // 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.scale (1./total_volume);
- }
-
+ virtual
+ void
+ solve_problem ();
-}
+ virtual
+ unsigned int
+ n_dofs () const;
+ virtual
+ void
+ postprocess (const Evaluation::EvaluationBase<dim> &postprocessor) const;
- // @sect3{Extending the LaplaceSolver namespace}
-namespace LaplaceSolver
-{
+ protected:
+ const SmartPointer<const DualFunctional::DualFunctionalBase<dim> > dual_functional;
+ virtual void assemble_rhs (Vector<double> &rhs) const;
- // @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);
-
- virtual
- void
- solve_problem ();
-
- virtual
- unsigned int
- n_dofs () const;
-
- virtual
- void
- postprocess (const Evaluation::EvaluationBase<dim> &postprocessor) const;
-
- protected:
- const SmartPointer<const DualFunctional::DualFunctionalBase<dim> > dual_functional;
- virtual void assemble_rhs (Vector<double> &rhs) const;
-
- static const ZeroFunction<dim> boundary_values;
-
- // Same as above -- make a
- // derived class a friend of
- // this one:
- friend class WeightedResidual<dim>;
- };
+ static const ZeroFunction<dim> boundary_values;
- template <int dim>
- const ZeroFunction<dim> DualSolver<dim>::boundary_values;
+ // Same as above -- make a
+ // derived class a friend of
+ // this one:
+ friend class WeightedResidual<dim>;
+ };
- 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>
+ const 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>::solve_problem ()
+ {
+ Solver<dim>::solve_problem ();
+ }
- template <int dim>
- void
- DualSolver<dim>::solve_problem ()
- {
- Solver<dim>::solve_problem ();
- }
+ template <int dim>
+ unsigned int
+ DualSolver<dim>::n_dofs() const
+ {
+ return Solver<dim>::n_dofs();
+ }
- template <int dim>
- unsigned int
- DualSolver<dim>::n_dofs() const
- {
- return Solver<dim>::n_dofs();
- }
+ template <int dim>
+ void
+ DualSolver<dim>::
+ postprocess (const Evaluation::EvaluationBase<dim> &postprocessor) const
+ {
+ Solver<dim>::postprocess(postprocessor);
+ }
- template <int dim>
- void
- DualSolver<dim>::
- postprocess (const Evaluation::EvaluationBase<dim> &postprocessor) const
- {
- Solver<dim>::postprocess(postprocessor);
- }
-
- template <int dim>
- void
- DualSolver<dim>::
- assemble_rhs (Vector<double> &rhs) const
- {
- dual_functional->assemble_rhs (this->dof_handler, rhs);
- }
+ 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 ();
-
- virtual
- void
- postprocess (const Evaluation::EvaluationBase<dim> &postprocessor) const;
-
- virtual
- unsigned int
- n_dofs () const;
-
- virtual void refine_grid ();
-
- virtual
- void
- output_solution () const;
-
- 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:
-
- typedef
- typename DoFHandler<dim>::active_cell_iterator
- 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:
- typedef
- typename std::map<typename DoFHandler<dim>::face_iterator,double>
- FaceIntegrals;
-
- // 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
- // neglectable 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 has now turned 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. In our experience,
- // more than 20 per cent of the
- // total run time of error
- // estimation functions are due
- // to memory allocation, if
- // done on a per-call level. 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, namely generating
- // these variables once in the
- // main function of each
- // thread, and passing them
- // down to the functions that
- // do the actual work. 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. Instead of
- // many individual objects, we
- // will then only pass one such
- // object to these functions,
- // making their calling
- // sequence simpler.
- 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);
- };
+ // @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 ();
+
+ virtual
+ void
+ postprocess (const Evaluation::EvaluationBase<dim> &postprocessor) const;
+
+ virtual
+ unsigned int
+ n_dofs () const;
+
+ virtual void refine_grid ();
+
+ virtual
+ void
+ output_solution () const;
+
+ 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:
+
+ typedef
+ typename DoFHandler<dim>::active_cell_iterator
+ 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:
+ typedef
+ typename std::map<typename DoFHandler<dim>::face_iterator,double>
+ FaceIntegrals;
+
+ // 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
+ // neglectable 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 has now turned 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. In our experience,
+ // more than 20 per cent of the
+ // total run time of error
+ // estimation functions are due
+ // to memory allocation, if
+ // done on a per-call level. 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, namely generating
+ // these variables once in the
+ // main function of each
+ // thread, and passing them
+ // down to the functions that
+ // do the actual work. 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. Instead of
+ // many individual objects, we
+ // will then only pass one such
+ // object to these functions,
+ // making their calling
+ // sequence simpler.
+ 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);
+ };
- 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);
- };
+ 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);
+ };
-
-
- // Regarding the evaluation of
- // the error estimator, we have
- // two driver functions that do
- // this: the first is called to
- // generate the cell-wise
- // estimates, and splits up the
- // task in a number of threads
- // each of which work on a
- // subset of the cells. The
- // first function will run the
- // second for each of these
- // threads:
- void estimate_error (Vector<float> &error_indicators) const;
-
- void estimate_some (const Vector<double> &primal_solution,
- const Vector<double> &dual_weights,
- const unsigned int n_threads,
- const unsigned int this_thread,
- 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 unsigned int cell_index,
- 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,
+
+
+ // Regarding the evaluation of
+ // the error estimator, we have
+ // two driver functions that do
+ // this: the first is called to
+ // generate the cell-wise
+ // estimates, and splits up the
+ // task in a number of threads
+ // each of which work on a
+ // subset of the cells. The
+ // first function will run the
+ // second for each of these
+ // threads:
+ void estimate_error (Vector<float> &error_indicators) const;
+
+ void estimate_some (const Vector<double> &primal_solution,
+ const Vector<double> &dual_weights,
+ const unsigned int n_threads,
+ const unsigned int this_thread,
+ 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 unsigned int cell_index,
+ 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 to length.
- 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())
- {}
-
-
+ // 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 to length.
+ 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>::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();
- 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);
- }
-
+ 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>::
- 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,
+
+ 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,
- rhs_function, bv),
- DualSolver<dim> (coarse_grid, dual_fe,
- quadrature, face_quadrature,
- dual_functional)
- {}
+ 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::ThreadGroup<> threads;
+ threads += Threads::new_thread (&WeightedResidual<dim>::solve_primal_problem,
+ *this);
+ threads += Threads::new_thread (&WeightedResidual<dim>::solve_dual_problem,
+ *this);
+ threads.join_all ();
+ }
- // 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::ThreadGroup<> threads;
- threads += Threads::new_thread (&WeightedResidual<dim>::solve_primal_problem,
- *this);
- threads += Threads::new_thread (&WeightedResidual<dim>::solve_dual_problem,
- *this);
- threads.join_all ();
- }
+ template <int dim>
+ void
+ WeightedResidual<dim>::solve_primal_problem ()
+ {
+ PrimalSolver<dim>::solve_problem ();
+ }
-
- 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>::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();
- }
+ 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 (Vector<float>::iterator i=error_indicators.begin();
- i != error_indicators.end(); ++i)
- *i = std::fabs (*i);
-
- // 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
- // <code>ConstraintMatrix</code> object
- // including the hanging node
- // constraints. The rest is
- // standard.
- //
- // There is, however, one
- // work-around worth mentioning: in
- // this function, as in a couple of
- // following ones, we have to
- // access the <code>DoFHandler</code>
- // objects and solutions of both
- // the primal as well as of the
- // dual solver. Since these are
- // members of the <code>Solver</code> base
- // class which exists twice in the
- // class hierarchy leading to the
- // present class (once as base
- // class of the <code>PrimalSolver</code>
- // class, once as base class of the
- // <code>DualSolver</code> class), we have
- // to disambiguate accesses to them
- // by telling the compiler a member
- // of which of these two instances
- // we want to access. The way to do
- // this would be identify the
- // member by pointing a path
- // through the class hierarchy
- // which disambiguates the base
- // class, for example writing
- // <code>PrimalSolver::dof_handler</code> to
- // denote the member variable
- // <code>dof_handler</code> from the
- // <code>Solver</code> base class of the
- // <code>PrimalSolver</code>
- // class. Unfortunately, this
- // confuses gcc's version 2.96 (a
- // version that was intended as a
- // development snapshot, but
- // delivered as system compiler by
- // Red Hat in their 7.x releases)
- // so much that it bails out and
- // refuses to compile the code.
- //
- // Thus, we have to work around
- // this problem. We do this by
- // introducing references to the
- // <code>PrimalSolver</code> and
- // <code>DualSolver</code> components of the
- // <code>WeightedResidual</code> object at
- // the beginning of the
- // function. Since each of these
- // has an unambiguous base class
- // <code>Solver</code>, we can access the
- // member variables we want through
- // these references. However, we
- // are now accessing protected
- // member variables of these
- // classes through a pointer other
- // than the <code>this</code> pointer (in
- // fact, this is of course the
- // <code>this</code> pointer, but not
- // explicitly). This finally is the
- // reason why we had to declare the
- // present class a friend of the
- // classes we so access.
- template <int dim>
- void
- WeightedResidual<dim>::output_solution () const
- {
- const PrimalSolver<dim> &primal_solver = *this;
- const DualSolver<dim> &dual_solver = *this;
-
- ConstraintMatrix primal_hanging_node_constraints;
- DoFTools::make_hanging_node_constraints (primal_solver.dof_handler,
- primal_hanging_node_constraints);
- primal_hanging_node_constraints.close();
- Vector<double> dual_solution (primal_solver.dof_handler.n_dofs());
- FETools::interpolate (dual_solver.dof_handler,
- dual_solver.solution,
- primal_solver.dof_handler,
- primal_hanging_node_constraints,
- dual_solution);
-
- DataOut<dim> data_out;
- data_out.attach_dof_handler (primal_solver.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 (primal_solver.solution,
- "primal_solution");
- data_out.add_data_vector (dual_solution,
- "dual_solution");
-
- data_out.build_patches ();
-
- std::ostringstream filename;
- filename << "solution-"
- << this->refinement_cycle
- << ".gnuplot"
- << std::ends;
-
- std::ofstream out (filename.str().c_str());
- data_out.write (out, DataOut<dim>::gnuplot);
- }
- // @sect3{Estimating errors}
+ // 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 (Vector<float>::iterator i=error_indicators.begin();
+ i != error_indicators.end(); ++i)
+ *i = std::fabs (*i);
+
+ // 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 ();
+ }
+
- // @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
- {
- const PrimalSolver<dim> &primal_solver = *this;
- const DualSolver<dim> &dual_solver = *this;
-
- // 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 a ConstraintMatrix
+ // 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
+ // <code>ConstraintMatrix</code> object
// including the hanging node
- // constraints, but this time of
- // the dual finite element space.
- ConstraintMatrix dual_hanging_node_constraints;
- DoFTools::make_hanging_node_constraints (dual_solver.dof_handler,
- dual_hanging_node_constraints);
- dual_hanging_node_constraints.close();
- Vector<double> primal_solution (dual_solver.dof_handler.n_dofs());
- FETools::interpolate (primal_solver.dof_handler,
- primal_solver.solution,
- dual_solver.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.
- ConstraintMatrix primal_hanging_node_constraints;
- DoFTools::make_hanging_node_constraints (primal_solver.dof_handler,
- primal_hanging_node_constraints);
- primal_hanging_node_constraints.close();
- Vector<double> dual_weights (dual_solver.dof_handler.n_dofs());
- FETools::interpolation_difference (dual_solver.dof_handler,
- dual_hanging_node_constraints,
- dual_solver.solution,
- primal_solver.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.
+ // constraints. The rest is
+ // standard.
//
- // We initialize this map already
- // with a value of -1e20 for all
- // faces, since this value will
- // strike in the results if
- // something should go wrong and
- // we fail to compute the value
- // for a face for some
- // reason. Secondly, we
- // initialize the map once before
- // we branch to different threads
- // since this way the map's
- // structure is no more modified
- // by the individual threads,
- // only existing entries are set
- // to new values. This relieves
- // us from the necessity to
- // synchronise the threads
- // through a mutex each time they
- // write to (and modify the
- // structure of) this map.
- FaceIntegrals face_integrals;
- for (active_cell_iterator cell=dual_solver.dof_handler.begin_active();
- cell!=dual_solver.dof_handler.end();
- ++cell)
- for (unsigned int face_no=0;
- face_no<GeometryInfo<dim>::faces_per_cell;
- ++face_no)
- face_integrals[cell->face(face_no)] = -1e20;
-
- // Then set up a vector with
- // error indicators. Reserve one
- // slot for each cell and set it
- // to zero.
- error_indicators.reinit (dual_solver.dof_handler
- .get_tria().n_active_cells());
-
- // Now start a number of threads
- // which compute the error
- // formula on parts of all the
- // cells, and once they are all
- // started wait until they have
- // all finished:
- const unsigned int n_threads = multithread_info.n_default_threads;
- Threads::ThreadGroup<> threads;
- for (unsigned int i=0; i<n_threads; ++i)
- threads += Threads::new_thread (&WeightedResidual<dim>::estimate_some,
- *this,
- primal_solution,
- dual_weights,
- n_threads, i,
- error_indicators,
- face_integrals);
- threads.join_all();
-
- // 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 (active_cell_iterator cell=dual_solver.dof_handler.begin_active();
- cell!=dual_solver.dof_handler.end();
- ++cell, ++present_cell)
- for (unsigned int face_no=0; face_no<GeometryInfo<dim>::faces_per_cell;
- ++face_no)
- {
- Assert(face_integrals.find(cell->face(face_no)) !=
- face_integrals.end(),
- ExcInternalError());
- error_indicators(present_cell)
- -= 0.5*face_integrals[cell->face(face_no)];
- }
- std::cout << " Estimated error="
- << std::accumulate (error_indicators.begin(),
- error_indicators.end(), 0.)
- << std::endl;
- }
+ // There is, however, one
+ // work-around worth mentioning: in
+ // this function, as in a couple of
+ // following ones, we have to
+ // access the <code>DoFHandler</code>
+ // objects and solutions of both
+ // the primal as well as of the
+ // dual solver. Since these are
+ // members of the <code>Solver</code> base
+ // class which exists twice in the
+ // class hierarchy leading to the
+ // present class (once as base
+ // class of the <code>PrimalSolver</code>
+ // class, once as base class of the
+ // <code>DualSolver</code> class), we have
+ // to disambiguate accesses to them
+ // by telling the compiler a member
+ // of which of these two instances
+ // we want to access. The way to do
+ // this would be identify the
+ // member by pointing a path
+ // through the class hierarchy
+ // which disambiguates the base
+ // class, for example writing
+ // <code>PrimalSolver::dof_handler</code> to
+ // denote the member variable
+ // <code>dof_handler</code> from the
+ // <code>Solver</code> base class of the
+ // <code>PrimalSolver</code>
+ // class. Unfortunately, this
+ // confuses gcc's version 2.96 (a
+ // version that was intended as a
+ // development snapshot, but
+ // delivered as system compiler by
+ // Red Hat in their 7.x releases)
+ // so much that it bails out and
+ // refuses to compile the code.
+ //
+ // Thus, we have to work around
+ // this problem. We do this by
+ // introducing references to the
+ // <code>PrimalSolver</code> and
+ // <code>DualSolver</code> components of the
+ // <code>WeightedResidual</code> object at
+ // the beginning of the
+ // function. Since each of these
+ // has an unambiguous base class
+ // <code>Solver</code>, we can access the
+ // member variables we want through
+ // these references. However, we
+ // are now accessing protected
+ // member variables of these
+ // classes through a pointer other
+ // than the <code>this</code> pointer (in
+ // fact, this is of course the
+ // <code>this</code> pointer, but not
+ // explicitly). This finally is the
+ // reason why we had to declare the
+ // present class a friend of the
+ // classes we so access.
+ template <int dim>
+ void
+ WeightedResidual<dim>::output_solution () const
+ {
+ const PrimalSolver<dim> &primal_solver = *this;
+ const DualSolver<dim> &dual_solver = *this;
+
+ ConstraintMatrix primal_hanging_node_constraints;
+ DoFTools::make_hanging_node_constraints (primal_solver.dof_handler,
+ primal_hanging_node_constraints);
+ primal_hanging_node_constraints.close();
+ Vector<double> dual_solution (primal_solver.dof_handler.n_dofs());
+ FETools::interpolate (dual_solver.dof_handler,
+ dual_solver.solution,
+ primal_solver.dof_handler,
+ primal_hanging_node_constraints,
+ dual_solution);
+
+ DataOut<dim> data_out;
+ data_out.attach_dof_handler (primal_solver.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 (primal_solver.solution,
+ "primal_solution");
+ data_out.add_data_vector (dual_solution,
+ "dual_solution");
+
+ data_out.build_patches ();
+
+ std::ostringstream filename;
+ filename << "solution-"
+ << this->refinement_cycle
+ << ".gnuplot"
+ << std::ends;
+
+ std::ofstream out (filename.str().c_str());
+ data_out.write (out, DataOut<dim>::gnuplot);
+ }
- // @sect4{Estimating on a subset of cells}
+ // @sect3{Estimating errors}
- // Next we have the function that
- // is called to estimate the error
- // on a subset of cells. The
- // function may be called multiply
- // if the library was configured to
- // use multi-threading. Here it
- // goes:
- template <int dim>
- void
- WeightedResidual<dim>::
- estimate_some (const Vector<double> &primal_solution,
- const Vector<double> &dual_weights,
- const unsigned int n_threads,
- const unsigned int this_thread,
- Vector<float> &error_indicators,
- FaceIntegrals &face_integrals) const
- {
- const PrimalSolver<dim> &primal_solver = *this;
- const DualSolver<dim> &dual_solver = *this;
-
- // At the beginning, we
- // initialize two variables for
- // each thread which may be
- // running this function. The
- // reason for these functions was
- // discussed above, when the
- // respective classes were
- // discussed, so we here only
- // point out that since they are
- // local to the function that is
- // spawned when running more than
- // one thread, the data of these
- // objects exists actually once
- // per thread, so we don't have
- // to take care about
- // synchronising access to them.
- CellData cell_data (*dual_solver.fe,
- *dual_solver.quadrature,
- *primal_solver.rhs_function);
- FaceData face_data (*dual_solver.fe,
- *dual_solver.face_quadrature);
-
- // Then calculate the start cell
- // for this thread. We let the
- // different threads run on
- // interleaved cells, i.e. for
- // example if we have 4 threads,
- // then the first thread treates
- // cells 0, 4, 8, etc, while the
- // second threads works on cells 1,
- // 5, 9, and so on. The reason is
- // that it takes vastly more time
- // to work on cells with hanging
- // nodes than on regular cells, but
- // such cells are not evenly
- // distributed across the range of
- // cell iterators, so in order to
- // have the different threads do
- // approximately the same amount of
- // work, we have to let them work
- // interleaved to the effect of a
- // pseudorandom distribution of the
- // `hard' cells to the different
- // threads.
- active_cell_iterator cell=dual_solver.dof_handler.begin_active();
- for (unsigned int t=0;
- (t<this_thread) && (cell!=dual_solver.dof_handler.end());
- ++t, ++cell)
- ;
-
- // If there are no cells for this
- // thread (for example if there
- // are a total of less cells than
- // there are threads), then go
- // back right now
- if (cell == dual_solver.dof_handler.end())
- return;
-
- // Next loop over all cells. The
- // check for loop end is done at
- // the end of the loop, along
- // with incrementing the loop
- // index.
- for (unsigned int cell_index=this_thread; true; )
- {
- // 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, cell_index,
- primal_solution,
- dual_weights,
- 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:
+ // @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
+ {
+ const PrimalSolver<dim> &primal_solver = *this;
+ const DualSolver<dim> &dual_solver = *this;
+
+ // 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 a ConstraintMatrix
+ // including the hanging node
+ // constraints, but this time of
+ // the dual finite element space.
+ ConstraintMatrix dual_hanging_node_constraints;
+ DoFTools::make_hanging_node_constraints (dual_solver.dof_handler,
+ dual_hanging_node_constraints);
+ dual_hanging_node_constraints.close();
+ Vector<double> primal_solution (dual_solver.dof_handler.n_dofs());
+ FETools::interpolate (primal_solver.dof_handler,
+ primal_solver.solution,
+ dual_solver.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.
+ ConstraintMatrix primal_hanging_node_constraints;
+ DoFTools::make_hanging_node_constraints (primal_solver.dof_handler,
+ primal_hanging_node_constraints);
+ primal_hanging_node_constraints.close();
+ Vector<double> dual_weights (dual_solver.dof_handler.n_dofs());
+ FETools::interpolation_difference (dual_solver.dof_handler,
+ dual_hanging_node_constraints,
+ dual_solver.solution,
+ primal_solver.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
+ // strike in the results if
+ // something should go wrong and
+ // we fail to compute the value
+ // for a face for some
+ // reason. Secondly, we
+ // initialize the map once before
+ // we branch to different threads
+ // since this way the map's
+ // structure is no more modified
+ // by the individual threads,
+ // only existing entries are set
+ // to new values. This relieves
+ // us from the necessity to
+ // synchronise the threads
+ // through a mutex each time they
+ // write to (and modify the
+ // structure of) this map.
+ FaceIntegrals face_integrals;
+ for (active_cell_iterator cell=dual_solver.dof_handler.begin_active();
+ cell!=dual_solver.dof_handler.end();
+ ++cell)
for (unsigned int face_no=0;
face_no<GeometryInfo<dim>::faces_per_cell;
++face_no)
+ face_integrals[cell->face(face_no)] = -1e20;
+
+ // Then set up a vector with
+ // error indicators. Reserve one
+ // slot for each cell and set it
+ // to zero.
+ error_indicators.reinit (dual_solver.dof_handler
+ .get_tria().n_active_cells());
+
+ // Now start a number of threads
+ // which compute the error
+ // formula on parts of all the
+ // cells, and once they are all
+ // started wait until they have
+ // all finished:
+ const unsigned int n_threads = multithread_info.n_default_threads;
+ Threads::ThreadGroup<> threads;
+ for (unsigned int i=0; i<n_threads; ++i)
+ threads += Threads::new_thread (&WeightedResidual<dim>::estimate_some,
+ *this,
+ primal_solution,
+ dual_weights,
+ n_threads, i,
+ error_indicators,
+ face_integrals);
+ threads.join_all();
+
+ // 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 (active_cell_iterator cell=dual_solver.dof_handler.begin_active();
+ cell!=dual_solver.dof_handler.end();
+ ++cell, ++present_cell)
+ for (unsigned int face_no=0; face_no<GeometryInfo<dim>::faces_per_cell;
+ ++face_no)
{
- // 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;
+ Assert(face_integrals.find(cell->face(face_no)) !=
+ face_integrals.end(),
+ ExcInternalError());
+ error_indicators(present_cell)
+ -= 0.5*face_integrals[cell->face(face_no)];
+ }
+ std::cout << " Estimated error="
+ << std::accumulate (error_indicators.begin(),
+ error_indicators.end(), 0.)
+ << std::endl;
+ }
+
+
+ // @sect4{Estimating on a subset of cells}
+
+ // Next we have the function that
+ // is called to estimate the error
+ // on a subset of cells. The
+ // function may be called multiply
+ // if the library was configured to
+ // use multi-threading. Here it
+ // goes:
+ template <int dim>
+ void
+ WeightedResidual<dim>::
+ estimate_some (const Vector<double> &primal_solution,
+ const Vector<double> &dual_weights,
+ const unsigned int n_threads,
+ const unsigned int this_thread,
+ Vector<float> &error_indicators,
+ FaceIntegrals &face_integrals) const
+ {
+ const PrimalSolver<dim> &primal_solver = *this;
+ const DualSolver<dim> &dual_solver = *this;
+
+ // At the beginning, we
+ // initialize two variables for
+ // each thread which may be
+ // running this function. The
+ // reason for these functions was
+ // discussed above, when the
+ // respective classes were
+ // discussed, so we here only
+ // point out that since they are
+ // local to the function that is
+ // spawned when running more than
+ // one thread, the data of these
+ // objects exists actually once
+ // per thread, so we don't have
+ // to take care about
+ // synchronising access to them.
+ CellData cell_data (*dual_solver.fe,
+ *dual_solver.quadrature,
+ *primal_solver.rhs_function);
+ FaceData face_data (*dual_solver.fe,
+ *dual_solver.face_quadrature);
+
+ // Then calculate the start cell
+ // for this thread. We let the
+ // different threads run on
+ // interleaved cells, i.e. for
+ // example if we have 4 threads,
+ // then the first thread treates
+ // cells 0, 4, 8, etc, while the
+ // second threads works on cells 1,
+ // 5, 9, and so on. The reason is
+ // that it takes vastly more time
+ // to work on cells with hanging
+ // nodes than on regular cells, but
+ // such cells are not evenly
+ // distributed across the range of
+ // cell iterators, so in order to
+ // have the different threads do
+ // approximately the same amount of
+ // work, we have to let them work
+ // interleaved to the effect of a
+ // pseudorandom distribution of the
+ // `hard' cells to the different
+ // threads.
+ active_cell_iterator cell=dual_solver.dof_handler.begin_active();
+ for (unsigned int t=0;
+ (t<this_thread) && (cell!=dual_solver.dof_handler.end());
+ ++t, ++cell)
+ ;
+
+ // If there are no cells for this
+ // thread (for example if there
+ // are a total of less cells than
+ // there are threads), then go
+ // back right now
+ if (cell == dual_solver.dof_handler.end())
+ return;
+
+ // Next loop over all cells. The
+ // check for loop end is done at
+ // the end of the loop, along
+ // with incrementing the loop
+ // index.
+ for (unsigned int cell_index=this_thread; true; )
+ {
+ // 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, cell_index,
+ primal_solution,
+ dual_weights,
+ 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 (unsigned int face_no=0;
+ face_no<GeometryInfo<dim>::faces_per_cell;
+ ++face_no)
+ {
+ // 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;
- }
-
- // 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,
- primal_solution,
- dual_weights,
- face_data,
- face_integrals);
- else
- integrate_over_irregular_face (cell, face_no,
+
+ // 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,
primal_solution,
dual_weights,
face_data,
face_integrals);
- }
-
- // After computing the cell
- // contributions and looping
- // over the faces, go to the
- // next cell for this
- // thread. Note again that
- // the cells for each of the
- // threads are interleaved.
- // If we are at the end of
- // our workload, jump out
- // of the loop.
- for (unsigned int t=0;
- ((t<n_threads) && (cell!=dual_solver.dof_handler.end()));
- ++t, ++cell, ++cell_index)
- ;
-
- if (cell == dual_solver.dof_handler.end())
- break;
- }
- }
-
+ else
+ integrate_over_irregular_face (cell, face_no,
+ primal_solution,
+ dual_weights,
+ face_data,
+ face_integrals);
+ }
- // @sect4{Computing cell term error contributions}
+ // After computing the cell
+ // contributions and looping
+ // over the faces, go to the
+ // next cell for this
+ // thread. Note again that
+ // the cells for each of the
+ // threads are interleaved.
+ // If we are at the end of
+ // our workload, jump out
+ // of the loop.
+ for (unsigned int t=0;
+ ((t<n_threads) && (cell!=dual_solver.dof_handler.end()));
+ ++t, ++cell, ++cell_index)
+ ;
+
+ if (cell == dual_solver.dof_handler.end())
+ break;
+ }
+ }
- // 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 unsigned int cell_index,
- 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_index) += sum;
- }
+ // @sect4{Computing cell term error contributions}
- // @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_grads (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
- // neigbor 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>home-many</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 neigbor 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_grads (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;
- }
+ // 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 unsigned int cell_index,
+ 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_index) += sum;
+ }
- // @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,
+ // @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_grads (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
+ // neigbor 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>home-many</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 neigbor 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_grads (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());
+
+ // Then find out which neighbor
+ // the present cell is of the
+ // adjacent cell. Note that we
+ // will operator on the children
+ // of this adjacent cell, but
+ // that their orientation is the
+ // same as that of their mother,
+ // i.e. the neigbor 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_grads (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_grads (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;
+ }
+
+ }
+
+
+ // @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
{
- // 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());
-
- // Then find out which neighbor
- // the present cell is of the
- // adjacent cell. Note that we
- // will operator on the children
- // of this adjacent cell, but
- // that their orientation is the
- // same as that of their mother,
- // i.e. the neigbor 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)
+ public:
+ // First, we declare two
+ // abbreviations for simple use
+ // of the respective data types:
+ typedef Evaluation::EvaluationBase<dim> Evaluator;
+ typedef std::list<Evaluator*> EvaluatorList;
+
+
+ // 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
{
- // 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_grads (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_grads (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;
- }
+ // 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.
+ SmartPointer<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.
+ SmartPointer<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.
+ SmartPointer<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
+ // intermittend mesh
+ // refinement. Its default
+ // value is 20,000.
+ unsigned int max_degrees_of_freedom;
+
+ // Finally the default
+ // constructor of this class:
+ ProblemDescription ();
+ };
- // 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;
- }
-
-}
+ // 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);
+ };
- // @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:
- typedef Evaluation::EvaluationBase<dim> Evaluator;
- typedef std::list<Evaluator*> EvaluatorList;
-
-
- // 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.
- SmartPointer<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
- };
+ // 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)
+ {}
- 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.
- SmartPointer<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.
- SmartPointer<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
- // intermittend 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.
- LaplaceSolver::Base<dim> * solver = 0;
- switch (descriptor.refinement_criterion)
- {
- case ProblemDescription::dual_weighted_error_estimator:
- {
- solver
- = new 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
- = new 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:
+
+ // 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.
+ LaplaceSolver::Base<dim> * solver = 0;
+ switch (descriptor.refinement_criterion)
{
- solver
- = new LaplaceSolver::RefinementKelly<dim> (triangulation,
- primal_fe,
- quadrature,
- face_quadrature,
- descriptor.data->get_right_hand_side(),
- descriptor.data->get_boundary_values());
- break;
+ case ProblemDescription::dual_weighted_error_estimator:
+ {
+ solver
+ = new 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
+ = new 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
+ = new 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
+ = new 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());
}
- case ProblemDescription::weighted_kelly_indicator:
+ // 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)
{
- solver
- = new LaplaceSolver::RefinementWeightedKelly<dim> (triangulation,
- primal_fe,
- quadrature,
- face_quadrature,
- descriptor.data->get_right_hand_side(),
- descriptor.data->get_boundary_values(),
- *descriptor.kelly_weight);
- break;
+ 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 (typename EvaluatorList::const_iterator
+ e = descriptor.evaluator_list.begin();
+ e != descriptor.evaluator_list.end(); ++e)
+ {
+ (*e)->set_refinement_cycle (step);
+ solver->postprocess (**e);
+ }
+
+
+ if (solver->n_dofs() < descriptor.max_degrees_of_freedom)
+ solver->refine_grid ();
+ else
+ 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 (typename EvaluatorList::const_iterator
- e = descriptor.evaluator_list.begin();
- e != descriptor.evaluator_list.end(); ++e)
- {
- (*e)->set_refinement_cycle (step);
- solver->postprocess (**e);
- }
-
- if (solver->n_dofs() < descriptor.max_degrees_of_freedom)
- solver->refine_grid ();
- else
- break;
- }
+ // After the loop has run, clean up
+ // the screen, and delete objects
+ // no more needed:
+ std::cout << std::endl;
+ delete solver;
+ solver = 0;
+ }
- // After the loop has run, clean up
- // the screen, and delete objects
- // no more needed:
- std::cout << std::endl;
- delete solver;
- solver = 0;
}
-
// @sect3{The main function}
// Here finally comes the main
// etc), and passes them packed into
// a structure to the frame work
// class above.
-int main ()
+int main ()
{
- deallog.depth_console (0);
try
{
+ using namespace dealii;
+ using namespace Step14;
+
+ deallog.depth_console (0);
// Describe the problem we want
// to solve here by passing a
// descriptor object to the
// can also use
// <code>CurvedRidges@<dim@></code>:
descriptor.data = new Data::SetUp<Data::Exercise_2_3<dim>,dim> ();
-
+
// Next set first a dual
// functional, then a list of
// evaluation objects. We
const Point<dim> evaluation_point (0.75, 0.75);
descriptor.dual_functional
= new 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);
// stop refining the mesh
// further:
descriptor.max_degrees_of_freedom = 20000;
-
+
// Finally pass the descriptor
// object to a function that
// runs the entire solution
<< std::endl;
return 1;
}
- catch (...)
+ catch (...)
{
std::cerr << std::endl << std::endl
<< "----------------------------------------------------"