saturation $S^n$, and then do an explicit time step for $S^{n+1}$ that only
depends on the previously known $S^n$ and the just computed
$\mathbf{u}^{n+1}$. This way, we never have to iterate for the nonlinearities
-of the system as we would have if we used a fully implicit method.
+of the system as we would have if we used a fully implicit method. (In
+a more modern perspective, this should be seen as an "operator
+splitting" method. step-58 has a long description of the idea behind this.)
We can then state the problem in weak form as follows, by multiplying each
equation with test functions $\mathbf v$, $\phi$, and $\sigma$ and integrating
Theory</i> (Chapter 5, Section 2) and G. B. Whitham's <i>Linear and
Nonlinear Waves</i> (Chapter 17, Sections 10-13).
+@note We will cover a separate nonlinear equation from quantum
+ mechanics, the Nonlinear Schrödinger Equation, in step-58.
+
<h3>Statement of the problem</h3>
The sine-Gordon initial-boundary-value problem (IBVP) we wish to solve
consists of the following equations:
A question that comes up frequently is how to solve problems involving complex
valued functions with deal.II. For many problems, instead of working with
-complex valued finite elements directly, which are not readily available in
-the library, it is often much more convenient to split complex valued
+complex valued finite elements directly, it is often more convenient to split complex valued
functions into their real and imaginary parts and use separate scalar finite
element fields for discretizing each one of them. Basically this amounts to
viewing a single complex valued equation as a system of two real valued
equations. This short example demonstrates how this can be implemented in
deal.II by using an <code>FE_system</code> object to stack two finite element
-fields representing real and imaginary parts. We also discuss the
-ParameterHandler class, which provides a convenient way for reading parameters
-from a configuration file at runtime without the need to recompile the
+fields representing real and imaginary parts. (The opposite approach,
+keeping everything complex-valued, is demonstrated in a different
+tutorial program: see step-58 for this.)
+When split into real and imaginary parts, the equations covered here
+fall into the class of vector-valued problems. A toplevel overview of
+this topic can be found in the @ref vector_valued module.
+
+In addition to this discussion, we also discuss the ParameterHandler
+class, which provides a convenient way for reading parameters from a
+configuration file at runtime without the need to recompile the
program code.
-The equations covered here fall into the class of vector-valued problems. A
-top level overview of this topic can be found in the @ref vector_valued module.
-
<h3>Problem setting</h3>
velocities/pressure on the one hand and temperature on the other is to use an
operator splitting where we first solve the Stokes system for the velocities
and pressures using the old temperature field, and then solve for the new
-temperature field using the just computed velocity field.
+temperature field using the just computed velocity field. (A more
+extensive discussion of operator splitting methods can be found in step-58.)
<h5>Linear solvers for the Stokes problem</h5>
* // return all components at one point
* void
* vector_value(const Point<dim> &p,
- * Vector<double> & value) const;
+ * Vector<double> &value) const;
* @endcode
*
* For more efficiency, there are other functions returning one or all
* // access to one component at several points
* void
* value_list(const std::vector<Point<dim>> &point_list,
- * std::vector<double> & value_list,
+ * std::vector<double> &value_list,
* const unsigned int component = 0) const;
*
* // return all components at several points
* void
* vector_value_list(const std::vector<Point<dim>> &point_list,
- * std::vector<Vector<double>> & value_list) const;
+ * std::vector<Vector<double>> &value_list) const;
* @endcode
*
* Furthermore, there are functions returning the gradient of the function or
* argument of this class: it describes the scalar type to be used for each
* component of your return values. It defaults to @p double, but in the
* example above, it could be set to <code>std::complex@<double@></code>.
+ * step-58 is an example of this.
*
* @tparam dim The space dimension of the range space within which the domain
* $\Omega$ of the function lies. Consequently, the function will be
* and `solution_hessians` fields: First the gradients/Hessians of
* the real components, then all the gradients/Hessians of the
* imaginary components. There is more information about the subject in the
- * documentation of the DataPostprocessor class itself.
+ * documentation of the DataPostprocessor class itself. step-58 provides an
+ * example of how this class is used in a complex-valued situation.
*
* Through the fields in the CommonInputs base class, this class also
* makes available access to the locations of evaluations points,
* and possibly the first and second derivatives of the solution. Examples are
* the calculation of Mach numbers from velocity and density in supersonic
* flow computations, or the computation of the magnitude of a complex-valued
- * solution as demonstrated in step-29. Other uses are shown in step-32 and
- * step-33. This class offers the interface to perform such postprocessing.
- * Given the values and derivatives of the solution at those points where we
- * want to generated output, the functions of this class can be overloaded to
- * compute new quantities.
+ * solution as demonstrated in step-29 and step-58 (where it is actually
+ * the *square* of the magnitude). Other uses are shown in
+ * step-32 and step-33. This class offers the interface to perform such
+ * postprocessing. Given the values and derivatives of the solution at those
+ * points where we want to generated output, the functions of this class can be
+ * overloaded to compute new quantities.
*
* A data vector and an object of a class derived from the current one can be
* given to the DataOut::add_data_vector() function (and similarly for
*
* <h3>Complex-valued solutions</h3>
*
- * There are PDEs whose solutions are complex-valued. For example, step-62
- * solves a problem whose solution at each point consists of a complex number
- * represented by a `std::complex<double>` variable. (step-29 also solves such
- * a problem, but there we choose to represent the solution by two real-valued
- * fields.) In such cases, the vector that is handed to
+ * There are PDEs whose solutions are complex-valued. For example, step-58 and
+ * step-62 solve problems whose solutions at each point consists of a complex
+ * number represented by a `std::complex<double>` variable. (step-29 also solves
+ * such a problem, but there we choose to represent the solution by two
+ * real-valued fields.) In such cases, the vector that is handed to
* DataOut::build_patches() is of type `Vector<std::complex<double>>`, or
* something essentially equivalent to this. The issue with this, as also
* discussed in the documentation of DataOut itself, is that the most widely
* parts of all solution components, and then the values (or gradients,
* or Hessians) of the imaginary parts of all solution components.
*
+ * step-58 provides an example of how this class (or, rather, the derived
+ * DataPostprocessorScalar class) is used in a complex-valued situation.
*
* @ingroup output
* @author Tobias Leicht, 2007; Wolfgang Bangerth, 2016, 2019
* DataPostprocessor::evaluate_vector_field() as discussed in the
* DataPostprocessor class's documentation.
*
- * An example of how this class can be used can be found in step-29.
+ * An example of how this class can be used can be found in step-29 for the case
+ * where we are interested in computing the magnitude (a scalar) of a
+ * complex-valued solution. While in step-29, the solution vector consists of
+ * separate real and imaginary parts of the solution, step-58 computes the
+ * solution vector as a vector with complex entries and the
+ * DataPostprocessorScalar class is used there to compute the magnitude and
+ * phase of the solution in a different way there.
+ *
* 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.