]> https://gitweb.dealii.org/ - dealii.git/commitdiff
Add DataPostprocessorTensor along with documentation.
authorWolfgang Bangerth <bangerth@colostate.edu>
Thu, 31 Aug 2017 00:27:33 +0000 (18:27 -0600)
committerWolfgang Bangerth <bangerth@colostate.edu>
Fri, 1 Sep 2017 22:30:26 +0000 (16:30 -0600)
doc/doxygen/images/data_postprocessor_tensor_0.png [new file with mode: 0644]
doc/doxygen/images/data_postprocessor_tensor_1.png [new file with mode: 0644]
doc/doxygen/images/data_postprocessor_tensor_2.png [new file with mode: 0644]
include/deal.II/numerics/data_postprocessor.h
source/numerics/data_postprocessor.cc
source/numerics/data_postprocessor.inst.in

diff --git a/doc/doxygen/images/data_postprocessor_tensor_0.png b/doc/doxygen/images/data_postprocessor_tensor_0.png
new file mode 100644 (file)
index 0000000..7ad0e06
Binary files /dev/null and b/doc/doxygen/images/data_postprocessor_tensor_0.png differ
diff --git a/doc/doxygen/images/data_postprocessor_tensor_1.png b/doc/doxygen/images/data_postprocessor_tensor_1.png
new file mode 100644 (file)
index 0000000..a4f144c
Binary files /dev/null and b/doc/doxygen/images/data_postprocessor_tensor_1.png differ
diff --git a/doc/doxygen/images/data_postprocessor_tensor_2.png b/doc/doxygen/images/data_postprocessor_tensor_2.png
new file mode 100644 (file)
index 0000000..f12a046
Binary files /dev/null and b/doc/doxygen/images/data_postprocessor_tensor_2.png differ
index 635178771834d1824781dd3a827d9a8748ce0901..686e6119169f7c5aca1d87dae5ca7821291348a0 100644 (file)
@@ -64,7 +64,8 @@ namespace DataPostprocessorInputs
    * DataPostprocessor is typically called from classes such as DataOut
    * or DataOutFaces that evaluate solution fields on a cell-by-cell
    * basis. As a consequence, classes derived from DataPostprocessor
-   * (or DataPostprocessorScalar or DataPostprocessorVector) sometimes
+   * (or DataPostprocessorScalar, DataPostprocessorVector, or
+   * DataPostprocessorTensor) sometimes
    * need to use which cell is currently under investigation. Consequently,
    * DataOut and similar classes pass the cell they are currently working
    * on to DataPostprocessor via the classes in this namespace (and
@@ -156,9 +157,9 @@ namespace DataPostprocessorInputs
      * DataPostprocessor::get_needed_update_flags(), and the function
      * returns (possibly among other flags)
      * UpdateFlags::update_normal_vectors.  Alternatively, a class
-     * derived from DataPostprocessorScalar or DataPostprocessorVector
-     * may pass this flag to the constructor of
-     * DataPostprocessorScalar or DataPostprocessorVector.
+     * derived from DataPostprocessorScalar, DataPostprocessorVector,
+     * or DataPostprocessorTensor may pass this flag to the constructor of
+     * these three classes.
      */
     std::vector<Tensor<1, spacedim> > normals;
 
@@ -170,9 +171,9 @@ namespace DataPostprocessorInputs
      * DataPostprocessor::get_needed_update_flags(), and the function
      * returns (possibly among other flags)
      * UpdateFlags::update_quadrature_points.  Alternatively, a class
-     * derived from DataPostprocessorScalar or DataPostprocessorVector
-     * may pass this flag to the constructor of
-     * DataPostprocessorScalar or DataPostprocessorVector.
+     * derived from DataPostprocessorScalar, DataPostprocessorVector,
+     * or DataPostprocessorTensor may pass this flag to the constructor of
+     * these three classes.
      */
     std::vector<Point<spacedim> >     evaluation_points;
 
@@ -239,10 +240,10 @@ namespace DataPostprocessorInputs
      * This array is only filled if a user-derived class overloads the
      * DataPostprocessor::get_needed_update_flags(), and the function
      * returns (possibly among other flags)
-     * UpdateFlags::update_gradients.  Alternatively, a class derived
-     * from DataPostprocessorScalar or DataPostprocessorVector may
-     * pass this flag to the constructor of DataPostprocessorScalar or
-     * DataPostprocessorVector.
+     * UpdateFlags::update_gradients.  Alternatively, a class
+     * derived from DataPostprocessorScalar, DataPostprocessorVector,
+     * or DataPostprocessorTensor may pass this flag to the constructor of
+     * these three classes.
      */
     std::vector<Tensor<1, spacedim> > solution_gradients;
 
@@ -254,10 +255,10 @@ namespace DataPostprocessorInputs
      * This array is only filled if a user-derived class overloads the
      * DataPostprocessor::get_needed_update_flags(), and the function
      * returns (possibly among other flags)
-     * UpdateFlags::update_hessians.  Alternatively, a class derived
-     * from DataPostprocessorScalar or DataPostprocessorVector may
-     * pass this flag to the constructor of DataPostprocessorScalar or
-     * DataPostprocessorVector.
+     * UpdateFlags::update_hessians.  Alternatively, a class
+     * derived from DataPostprocessorScalar, DataPostprocessorVector,
+     * or DataPostprocessorTensor may pass this flag to the constructor of
+     * these three classes.
      */
     std::vector<Tensor<2, spacedim> > solution_hessians;
   };
@@ -303,10 +304,10 @@ namespace DataPostprocessorInputs
      * This array is only filled if a user-derived class overloads the
      * DataPostprocessor::get_needed_update_flags(), and the function
      * returns (possibly among other flags)
-     * UpdateFlags::update_gradients.  Alternatively, a class derived
-     * from DataPostprocessorScalar or DataPostprocessorVector may
-     * pass this flag to the constructor of DataPostprocessorScalar or
-     * DataPostprocessorVector.
+     * UpdateFlags::update_gradients.  Alternatively, a class
+     * derived from DataPostprocessorScalar, DataPostprocessorVector,
+     * or DataPostprocessorTensor may pass this flag to the constructor of
+     * these three classes.
      */
     std::vector<std::vector<Tensor<1, spacedim> > > solution_gradients;
 
@@ -322,10 +323,10 @@ namespace DataPostprocessorInputs
      * This array is only filled if a user-derived class overloads the
      * DataPostprocessor::get_needed_update_flags(), and the function
      * returns (possibly among other flags)
-     * UpdateFlags::update_hessians.  Alternatively, a class derived
-     * from DataPostprocessorScalar or DataPostprocessorVector may
-     * pass this flag to the constructor of DataPostprocessorScalar or
-     * DataPostprocessorVector.
+     * UpdateFlags::update_hessians.  Alternatively, a class
+     * derived from DataPostprocessorScalar, DataPostprocessorVector,
+     * or DataPostprocessorTensor may pass this flag to the constructor of
+     * these three classes.
      */
     std::vector<std::vector<Tensor<2, spacedim> > > solution_hessians;
   };
@@ -400,16 +401,20 @@ namespace DataPostprocessorInputs
  * (evaluate_scalar_field() or evaluate_vector_field(), get_names(), get_update_flags() and
  * get_data_component_interpretation()).
  *
- * To this end there are two classes DataPostprocessorScalar and
- * DataPostprocessorVector that are meant to be used if the output quantity is
- * either a single scalar or a single vector (here used meaning to have
- * exactly dim components). When using these classes, one only has to write a
+ * To this end there are three classes DataPostprocessorScalar,
+ * DataPostprocessorVector, and DataPostprocessorTensor that are meant to be
+ * used if the output quantity is either a single scalar, a single vector
+ * (here used meaning to have exactly @p dim components), or a single
+ * tensor (here used meaning to have exactly <code>dim*dim</code> components).
+ * When using these classes, one only has to write a
  * constructor that passes the name of the output variable and the update
  * flags to the constructor of the base class and overload the function that
  * actually computes the results.
  *
- * The DataPostprocessorVector class documentation also contains an extensive
- * example of how it can be used.
+ * The DataPostprocessorVector and DataPostprocessorTensor class documentations
+ * also contains a extensive examples of how they can be used. The step-29
+ * tutorial program contains an example of using the DataPostprocessorScalar
+ * class.
  *
  *
  * @ingroup output
@@ -519,6 +524,9 @@ public:
  * DataPostprocessor::evaluate_vector_field().
  *
  * An example of how this class can be used can be found in step-29.
+ * An example of how the closely related DataPostprocessorVector
+ * class can be used is found in the documentation of that class.
+ * The same is true for the DataPostprocessorTensor class.
  *
  * @ingroup output
  * @author Wolfgang Bangerth, 2011
@@ -580,7 +588,7 @@ private:
 /**
  * This class provides a simpler interface to the functionality offered by the
  * DataPostprocessor class in case one wants to compute only a single vector
- * quantity (defined as having exactly dim components) from the finite element
+ * quantity (defined as having exactly @p dim components) from the finite element
  * field passed to the DataOut class. For this particular case, it is clear
  * what the returned value of
  * DataPostprocessor::get_data_component_interpretation() should be and we
@@ -593,7 +601,8 @@ private:
  * DataPostprocessor::evaluate_vector_field().
  *
  * An example of how the closely related class DataPostprocessorScalar is used
- * can be found in step-29.
+ * can be found in step-29. An example of how the DataPostprocessorTensor
+ * class can be used is found in the documentation of that class.
  *
  *
  * <h3> An example </h3>
@@ -750,13 +759,8 @@ private:
  *
  * deal.II does not currently support outputting tensor-valued quantities, but
  * they can of course be output as a collection of scalar-valued components of
- * the tensor. This means that the current class is not applicable any more,
- * but it is not very difficult to derive a true "stress" postprocessor directly
- * from the DataPostprocessor class that simply outputs <code>dim*dim</code>
- * components of the stress vector as scalars and that is structured in a
- * similar way to the postprocessors above. (It has to overload the
- * @p evaluate_vector_field() function, however, given that the solution is
- * vector valued already.)
+ * the tensor. This can be facilitated using the DataPostprocessorTensor
+ * class. The documentation of that class contains an example.
  *
  *
  * @ingroup output
@@ -816,6 +820,229 @@ private:
 
 
 
+/**
+ * This class provides a simpler interface to the functionality offered by the
+ * DataPostprocessor class in case one wants to compute only a single tensor
+ * quantity (defined as having exactly <code>dim*dim</code> components) from
+ * the finite element field passed to the DataOut class.
+ *
+ * For this case, we would like to output all of these components as parts
+ * of a tensor-valued quantity. Unfortunately, the various backends that
+ * write DataOut data in graphical file formats (see the DataOutBase
+ * namespace for what formats can be written) do not support tensor data
+ * at the current time. In fact, neither does the DataComponentInterpretation
+ * namespace that provides semantic information how individual components
+ * of graphical data should be interpreted. Nevertheless, like
+ * DataPostprocessorScalar and DataPostprocessorVector, this class helps
+ * with setting up what the get_names() and get_needed_update_flags()
+ * functions required by the DataPostprocessor base class should return,
+ * and so the current class implements these based on information that
+ * the constructor of the current class receives from further derived
+ * classes.
+ *
+ * (In order to visualize this collection of scalar fields that, together,
+ * are then supposed to be interpreted as a tensor, one has to (i) use a
+ * visualization program that can visualize tensors, and (ii) teach it
+ * how to re-combine the scalar fields into tensors. In the case of
+ * Visit -- see https://wci.llnl.gov/simulation/computer-codes/visit/ --
+ * this is done by creating a new "Expression": in essence, one creates
+ * a variable, say "grad_u", that is tensor-valued and whose value is
+ * given by the expression <code>{{grad_u_xx,grad_u_xy},
+ * {grad_u_yx, grad_u_yy}}</code>, where the referenced variables are
+ * the names of scalar fields that, here, are produced by the example
+ * below. Visit is then able to visualize this "new" variable as a
+ * tensor.)
+ *
+ * All derived classes have to do is implement a constructor and overload
+ * either DataPostprocessor::evaluate_scalar_field() or
+ * DataPostprocessor::evaluate_vector_field().
+ *
+ * An example of how the closely related class DataPostprocessorScalar is used
+ * can be found in step-29. An example of how the DataPostprocessorVector
+ * class can be used is found in the documentation of that class.
+ *
+ *
+ * <h3> An example </h3>
+ *
+ * A common example of what one wants to do with postprocessors is to visualize
+ * not just the value of the solution, but the gradient. This class is meant for
+ * tensor-valued outputs, so we will start with a vector-valued solution: the
+ * displacement field of step-8. The gradient is a rank-2 tensor (with exactly
+ * <code>dim*dim</code> components), so the
+ * current class fits the bill to produce the gradient through postprocessing.
+ * Then, the following code snippet implements everything you need to have
+ * to visualize the gradient:
+ * @code
+ *   template <int dim>
+ *   class GradientPostprocessor : public DataPostprocessorTensor<dim>
+ *   {
+ *   public:
+ *     GradientPostprocessor ()
+ *       :
+ *       DataPostprocessorTensor<dim> ("grad_u",
+ *                                     update_gradients)
+ *     {}
+ *
+ *     virtual
+ *     void
+ *     evaluate_vector_field (const DataPostprocessorInputs::Vector<dim> &input_data,
+ *                            std::vector<Vector<double> >               &computed_quantities) const
+ *     {
+ *       // ensure that there really are as many output slots
+ *       // as there are points at which DataOut provides the
+ *       // gradients:
+ *       AssertDimension (input_data.solution_gradients.size(),
+ *                        computed_quantities.size());
+ *
+ *       for (unsigned int p=0; p<input_data.solution_gradients.size(); ++p)
+ *         {
+ *           // ensure that each output slot has exactly 'dim*dim'
+ *           // components (as should be expected, given that we
+ *           // want to create tensor-valued outputs), and copy the
+ *           // gradients of the solution at the evaluation points
+ *           // into the output slots:
+ *           AssertDimension (computed_quantities[p].size(), dim*dim);
+ *           for (unsigned int d=0; d<dim; ++d)
+ *             for (unsigned int e=0; e<dim; ++e)
+ *               computed_quantities[p][d*dim+e]
+ *                 = input_data.solution_gradients[p][d][e];
+ *         }
+ *     }
+ *   };
+ * @endcode
+ * The only thing that is necessary is to add another output to the call
+ * of DataOut::add_vector() in the @p output_results() function of the @p Step8
+ * class of that example program. The corresponding code snippet would then look
+ * like this:
+ * @code
+ *     GradientPostprocessor<dim> grad_u;
+ *
+ *     DataOut<dim> data_out;
+ *     data_out.attach_dof_handler (dof_handler);
+ *
+ *     std::vector<DataComponentInterpretation::DataComponentInterpretation>
+ *     data_component_interpretation
+ *     (dim, DataComponentInterpretation::component_is_part_of_vector);
+ *     data_out.add_data_vector (solution, std::vector<std::string>(dim,"displacement"),
+ *                               DataOut<dim>::type_dof_data,
+ *                               data_component_interpretation);
+ *     data_out.add_data_vector (solution, grad_u);
+ *     data_out.build_patches ();
+ *     data_out.write_vtk (output);
+ * @endcode
+ *
+ * This leads to the following output for the displacement field (i.e., the
+ * solution) and the gradients (you may want to compare with the solution shown
+ * in the results section of step-8; the current data is generated on a uniform
+ * mesh for simplicity):
+ *
+ * @image html data_postprocessor_tensor_0.png
+ * @image html data_postprocessor_tensor_1.png
+ *
+ * These pictures show an ellipse representing the gradient tensor at, on average,
+ * every tenth mesh point. You may want to read through the documentation of the
+ * Visit visualization program (see https://wci.llnl.gov/simulation/computer-codes/visit/)
+ * for an interpretation of how exactly tensors are visualizated.
+ *
+ * In elasticity, one is often interested not in the gradient of the displacement,
+ * but in the "strain", i.e., the symmetrized version of the gradient
+ * $\varepsilon=\frac 12 (\nabla u + \nabla u^T)$. This is easily facilitated with
+ * the following minor modification:
+ * @code
+ *   template <int dim>
+ *   class StrainPostprocessor : public DataPostprocessorTensor<dim>
+ *   {
+ *   public:
+ *     StrainPostprocessor ()
+ *       :
+ *       DataPostprocessorTensor<dim> ("strain",
+ *                                     update_gradients)
+ *     {}
+ *
+ *     virtual
+ *     void
+ *     evaluate_vector_field (const DataPostprocessorInputs::Vector<dim> &input_data,
+ *                            std::vector<Vector<double> >               &computed_quantities) const
+ *     {
+ *       AssertDimension (input_data.solution_gradients.size(),
+ *                        computed_quantities.size());
+ *
+ *       for (unsigned int p=0; p<input_data.solution_gradients.size(); ++p)
+ *         {
+ *           AssertDimension (computed_quantities[p].size(), dim*dim);
+ *           for (unsigned int d=0; d<dim; ++d)
+ *             for (unsigned int e=0; e<dim; ++e)
+ *               computed_quantities[p][d*dim+e]
+ *                 = (input_data.solution_gradients[p][d][e]
+ *                    +
+ *                    input_data.solution_gradients[p][e][d]) / 2;
+ *         }
+ *     }
+ *   };
+ * @endcode
+ *
+ * This leads to the following visualization:
+ *
+ * @image html data_postprocessor_tensor_2.png
+ *
+ *
+ * @ingroup output
+ * @author Wolfgang Bangerth, 2017
+ */
+template <int dim>
+class DataPostprocessorTensor : public DataPostprocessor<dim>
+{
+public:
+  /**
+   * Constructor. Take the name of the single vector variable computed by
+   * classes derived from the current one, as well as the update flags
+   * necessary to compute this quantity.
+   *
+   * @param name The name by which the vector variable computed by this class
+   * should be made available in graphical output files.
+   * @param update_flags This has to be a combination of @p update_values, @p
+   * update_gradients and @p update_hessians. If the DataPostprocessor is to
+   * be used in combination with DataOutFaces, you may also ask for a update
+   * of normals via the @p update_normal_vectors flag.
+   */
+  DataPostprocessorTensor (const std::string &name,
+                           const UpdateFlags  update_flags);
+
+  /**
+   * Return the vector of strings describing the names of the computed
+   * quantities. Given the purpose of this class, this is a vector with dim
+   * entries all equal to the name given to the constructor.
+   */
+  virtual std::vector<std::string> get_names () const;
+
+  /**
+   * This functions returns information about how the individual components of
+   * output files that consist of more than one data set are to be
+   * interpreted. Since the current class is meant to be used for a single
+   * vector result variable, the returned value is obviously
+   * DataComponentInterpretation::component_is_part repeated dim times.
+   */
+  virtual
+  std::vector<DataComponentInterpretation::DataComponentInterpretation>
+  get_data_component_interpretation () const;
+
+  /**
+   * Return which data has to be provided to compute the derived quantities.
+   * The flags returned here are the ones passed to the constructor of this
+   * class.
+   */
+  virtual UpdateFlags get_needed_update_flags () const;
+
+private:
+  /**
+   * Copies of the two arguments given to the constructor of this class.
+   */
+  const std::string name;
+  const UpdateFlags update_flags;
+};
+
+
+
 #ifndef DOXYGEN
 // -------------------- template functions ----------------------
 
index 45cf4c3480eedc69528b6ba0a22a8882426a31c2..67678d14df1660998910c9539b20fbd376f51ff9 100644 (file)
@@ -149,6 +149,61 @@ get_needed_update_flags () const
 }
 
 
+
+
+// -------------------------- DataPostprocessorTensor ---------------------------
+
+template <int dim>
+DataPostprocessorTensor<dim>::
+DataPostprocessorTensor (const std::string &name,
+                         const UpdateFlags  update_flags)
+  :
+  name (name),
+  update_flags (update_flags)
+{}
+
+
+
+template <int dim>
+std::vector<std::string>
+DataPostprocessorTensor<dim>::
+get_names () const
+{
+  static_assert (dim <= 3,
+                 "The following variable needs to be expanded for dim>3");
+  static const char suffixes[] = { 'x', 'y', 'z' };
+
+  std::vector<std::string> names;
+  for (unsigned int d=0; d<dim; ++d)
+    for (unsigned int e=0; e<dim; ++e)
+      names.push_back (name + '_' + suffixes[d] + suffixes[e]);
+  return names;
+}
+
+
+
+template <int dim>
+std::vector<DataComponentInterpretation::DataComponentInterpretation>
+DataPostprocessorTensor<dim>::
+get_data_component_interpretation () const
+{
+  return
+    std::vector<DataComponentInterpretation::DataComponentInterpretation>
+    (dim*dim, DataComponentInterpretation::component_is_scalar);
+}
+
+
+template <int dim>
+UpdateFlags
+DataPostprocessorTensor<dim>::
+get_needed_update_flags () const
+{
+  return update_flags;
+}
+
+
+
+
 // explicit instantiation
 #include "data_postprocessor.inst"
 
index 589d6ef8b57b4a698c20cf9e2ceca5bf46c0c5d2..307919d1bc46c462229e2a1d360177aed77bd27f 100644 (file)
@@ -19,4 +19,5 @@ for (deal_II_dimension : DIMENSIONS)
     template class DataPostprocessor<deal_II_dimension>;
     template class DataPostprocessorScalar<deal_II_dimension>;
     template class DataPostprocessorVector<deal_II_dimension>;
+    template class DataPostprocessorTensor<deal_II_dimension>;
 }

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

Douglas Adams


Typeset in Trocchi and Trocchi Bold Sans Serif.