# this rule, we actually had to write this sequence out in our
# documentation. Unfortunately, as a consequence, there are vestiges
# of this style, so we can't substitute things that look like
-# "step-xx".
+# "step-xx". We therefore not substitute if step-xx is preceded or
+# followed by quotation marks, or if the text is explicitly
+# preceded by a backslash for escaping.
#
-# There is other exceptions:
+# There are other exceptions:
# - the scripts in doc/doxygen/tutorial produce files that have
# table of contents entries. We don't want these cross-linked
# to itself.
-# - things like step_12.solution.png that typically appear in
+# - things like step-12.solution.png that typically appear in
# @image commands.
# - things in headings
-s/(?<!\")step-(\d+)(?!\")/\@ref step_\1 \"step-\1\"/gi
+s/(?<![\"\\])step-(\d+)(?!\")/\@ref step_\1 \"step-\1\"/gi
if !m/(\@page|\@image|<h\d>)/i;
+# If step-xx was explicitly escaped with a backslash, remove the
+# latter
+s/\\(step-\d+)/\1/g;
+
s#(static dealii::ExceptionBase\&)#\n//\! \@ingroup Exceptions\n \1#g;
format you like. The program that does this is the step-19 tutorial program:
for example, for the first time step, call it through
@code
- ../step-19/step-19 solution-0001.0000.*.d2 solution-0001.0000.gmv
+ ../\step-19/\step-19 solution-0001.0000.*.d2 solution-0001.0000.gmv
@endcode
to merge all the intermediate format files into a single file in GMV
format. More details on the parameters of this program and what it can do for
If run, the program prints the following output, explaining what it is
doing during all that time:
@code
-examples/step-18> time make run
-============================ Running step-18
+examples/\step-18> time make run
+============================ Running \step-18
Timestep 1 at time 1
Cycle 0:
Number of active cells: 3712 (by partition: 3712)
problem to keep a computer busy for a while. At the end of the day,
this is what we have for output:
@code
-examples/step-18> ls -l *.d2
+examples/\step-18> ls -l *.d2
-rw-r--r-- 1 bangerth wheeler 8797414 May 25 09:10 solution-0001.0000.d2
-rw-r--r-- 1 bangerth wheeler 8788500 May 25 09:32 solution-0002.0000.d2
-rw-r--r-- 1 bangerth wheeler 8763718 May 25 09:55 solution-0003.0000.d2
Let us convert these files in deal.II intermediate format to gmv
-format (this assumes that you have already compiled the
+format (this assumes that you have already compiled the
step-19 example program):
@code
-examples/step-18> ../step-19/step-19
+examples/\step-18> ../\step-19/\step-19
Converter from deal.II intermediate format to other graphics formats.
-Usage: ./step-19 [-p parameter_file] list_of_input_files [-x output_format] output_file
+Usage: ./\step-19 [-p parameter_file] list_of_input_files [-x output_format] output_file
-examples/step-18> ../step-19/step-19 solution-0001.0000.d2 -x gmv solution-0001.0000.gmv
-examples/step-18> ../step-19/step-19 solution-0002.0000.d2 -x gmv solution-0002.0000.gmv
+examples/\step-18> ../\step-19/\step-19 solution-0001.0000.d2 -x gmv solution-0001.0000.gmv
+examples/\step-18> ../\step-19/\step-19 solution-0002.0000.d2 -x gmv solution-0002.0000.gmv
[...]
@endcode
Of course, since we have run the program only in sequential mode, we
That's quite a good number of unknowns, given that we are in 3d. The output of
this program are 16 files for each time step:
@code
-examples/step-18> ls -l solution-0001.000*
+examples/\step-18> ls -l solution-0001.000*
-rw-r--r-- 1 bangerth mfw 4325219 Aug 11 09:44 solution-0001.0000-000.d2
-rw-r--r-- 1 bangerth mfw 4454460 Aug 11 09:44 solution-0001.0000-001.d2
-rw-r--r-- 1 bangerth mfw 4485242 Aug 11 09:43 solution-0001.0000-002.d2
-rw-r--r-- 1 bangerth mfw 4340488 Aug 11 09:44 solution-0001.0000-015.d2
@endcode
We merge and convert these 16 intermediate files into a single gmv file as
-follows:
+follows:
@code
-examples/step-18> time ../step-19/step-19 solution-0001.0000-* -x gmv -o solution-0001.0000.gmv
+examples/\step-18> time ../\step-19/\step-19 solution-0001.0000-* -x gmv -o solution-0001.0000.gmv
real 0m45.929s
user 0m41.290s
sys 0m0.990s
-examples/step-18> ls -l solution-0001.0000.gmv
+examples/\step-18> ls -l solution-0001.0000.gmv
-rw-r--r-- 1 bangerth mfw 68925360 Aug 11 17:04 solution-0001.0000.gmv
@endcode
The most obvious extension is to use a more
realistic material model for large-scale quasistatic deformation. The natural
choice for this would be plasticity, in which a nonlinear relationship between
-stress and strain replaces equation <a href="#step-18.stress-strain">[stress-strain]</a>. Plasticity
+stress and strain replaces equation <a href="#step_18.stress-strain">[stress-strain]</a>. Plasticity
models are usually rather complicated to program since the stress-strain
dependence is generally non-smooth. The material can be thought of being able
to withstand only a maximal stress (the yield stress) after which it starts to
pp. 3559-3586, 2004).
-<h5>Stabilization issues</h5>
+<h5>Stabilization issues</h5>
The formulation we have chosen, i.e. using
piecewise (bi-, tri-)linear elements for all components of the displacement
pp. 1093-1112, 2005).
-<h5>Refinement during timesteps</h5>
+<h5>Refinement during timesteps</h5>
In the present form, the program
only refines the initial mesh a number of times, but then never again. For any
kind of realistic simulation, one would want to extend this so that the mesh
is refined and coarsened every few time steps instead. This is not hard to do,
in fact, but has been left for future tutorial programs or as an exercise, if
-you wish.
+you wish.
The main complication one has to overcome is that one has to
transfer the data that is stored in the quadrature points of the cells of the
old mesh to the new mesh, preferably by some sort of projection scheme. The
-general approach to this would go like this:
+general approach to this would go like this:
- At the beginning, the data is only available in the quadrature points of
individual cells, not as a finite element field that is defined everywhere.
for example, if you have a QGauss(2) quadrature formula (i.e. 4 points per
cell in 2d, 8 points in 3d), then one would use a finite element of kind
FE_DGQ(1), i.e. bi-/tri-linear functions as these have 4 degrees of freedom
- per cell in 2d and 8 in 3d.
+ per cell in 2d and 8 in 3d.
-- There are functions that can make this conversion from individual points to
- a global field simpler. The following piece of pseudo-code should help if
- you use a QGauss(2) quadrature formula. Note that the multiplication by the
+- There are functions that can make this conversion from individual points to
+ a global field simpler. The following piece of pseudo-code should help if
+ you use a QGauss(2) quadrature formula. Note that the multiplication by the
projection matrix below takes a vector of scalar components, i.e., we can only
convert one set of scalars at a time from the quadrature points to the degrees
- of freedom and vice versa. So we need to store each component of stress separately,
+ of freedom and vice versa. So we need to store each component of stress separately,
which requires <code>dim*dim</code> vectors. We'll store this set of vectors in a 2D array to
- make it easier to read off components in the same way you would the stress tensor.
+ make it easier to read off components in the same way you would the stress tensor.
Thus, we'll loop over the components of stress on each cell and store
- these values in the global history field. (The prefix <code>history_</code>
- indicates that we work with quantities related to the history variables defined
- in the quadrature points.)
+ these values in the global history field. (The prefix <code>history_</code>
+ indicates that we work with quantities related to the history variables defined
+ in the quadrature points.)
@code
FE_DGQ<dim> history_fe (1);
DoFHandler<dim> history_dof_handler (triangulation);
history_dof_handler.distribute_dofs (history_fe);
- std::vector< std::vector< Vector<double> > >
+ std::vector< std::vector< Vector<double> > >
history_field (dim, std::vector< Vector<double> >(dim)),
local_history_values_at_qpoints (dim, std::vector< Vector<double> >(dim)),
local_history_fe_values (dim, std::vector< Vector<double> >(dim));
-
+
for (unsigned int i=0; i<dim; i++)
for (unsigned int j=0; j<dim; j++)
{
quadrature, quadrature,
qpoint_to_dof_matrix);
- typename DoFHandler<dim>::active_cell_iterator cell = dof_handler.begin_active(),
+ typename DoFHandler<dim>::active_cell_iterator cell = dof_handler.begin_active(),
endc = dof_handler.end(),
dg_cell = history_dof_handler.begin_active();
PointHistory<dim> *local_quadrature_points_history
= reinterpret_cast<PointHistory<dim> *>(cell->user_pointer());
-
+
Assert (local_quadrature_points_history >=
&quadrature_point_history.front(),
ExcInternalError());
for (unsigned int i=0; i<dim; i++)
for (unsigned int j=0; j<dim; j++)
- {
+ {
for (unsigned int q=0; q<quadrature.size(); ++q)
- local_history_values_at_qpoints[i][j](q)
+ local_history_values_at_qpoints[i][j](q)
= local_quadrature_points_history[q].old_stress[i][j];
-
+
qpoint_to_dof_matrix.vmult (local_history_fe_values[i][j],
local_history_values_at_qpoints[i][j]);
- Now that we have a global field, we can refine the mesh and transfer the
history_field vector as usual using the SolutionTransfer class. This will
- interpolate everything from the old to the new mesh.
+ interpolate everything from the old to the new mesh.
- In a final step, we have to get the data back from the now interpolated
global field to the quadrature points on the new mesh. The following code
typename DoFHandler<dim>::active_cell_iterator cell = dof_handler.begin_active(),
endc = dof_handler.end(),
dg_cell = history_dof_handler.begin_active();
-
+
for (; cell != endc; ++cell, ++dg_cell)
{
PointHistory<dim> *local_quadrature_points_history
= reinterpret_cast<PointHistory<dim> *>(cell->user_pointer());
-
+
Assert (local_quadrature_points_history >=
&quadrature_point_history.front(),
ExcInternalError());
Assert (local_quadrature_points_history <
&quadrature_point_history.back(),
ExcInternalError());
-
+
for (unsigned int i=0; i<dim; i++)
for (unsigned int j=0; j<dim; j++)
- {
+ {
dg_cell->get_dof_values (history_field[i][j],
local_history_fe_values[i][j]);
local_history_fe_values[i][j]);
for (unsigned int q=0; q<quadrature.size(); ++q)
- local_quadrature_points_history[q].old_stress[i][j]
+ local_quadrature_points_history[q].old_stress[i][j]
= local_history_values_at_qpoints[i][j](q);
}
@endcode
With all that above, here is first what we get if we just run the program
without any parameters at all:
@code
-examples/step-19> ./step-19
+examples/\step-19> ./\step-19
Converter from deal.II intermediate format to other graphics formats.
Usage:
- ./step-19 [-p parameter_file] list_of_input_files
+ ./\step-19 [-p parameter_file] list_of_input_files
[-x output_format] [-o output_file]
Parameter sequences in brackets can be omitted if a parameter file is
set Dummy iterations = 42
# The name of the output file to be generated
-set Output file =
+set Output file =
# A name for the output format to be used
set Output format = gnuplot
Let us try to run this program on a set of input files generated by a modified
step-18 run on 32 nodes of a
-cluster. The computation was rather big, with more
+cluster. The computation was rather big, with more
than 350,000 cells and some 1.2M unknowns. That makes for 32 rather big
intermediate files that we will try to merge using the present program. Here is
the list of files, totaling some 245MB of data:
@code
-examples/step-19> ls -l *d2
+examples/\step-19> ls -l *d2
-rw-r--r-- 1 bangerth wheeler 7982085 Aug 12 10:11 solution-0005.0000-000.d2
-rw-r--r-- 1 bangerth wheeler 7888316 Aug 12 10:13 solution-0005.0000-001.d2
-rw-r--r-- 1 bangerth wheeler 7715984 Aug 12 10:09 solution-0005.0000-002.d2
@endcode
So let's see what happens if we attempt to merge all these files into a single
-one:
+one:
@code
-examples/step-19> time ./step-19 solution-0005.0000-*.d2 -x gmv -o solution-0005.gmv
+examples/\step-19> time ./\step-19 solution-0005.0000-*.d2 -x gmv -o solution-0005.gmv
real 2m08.35s
user 1m26.61s
system 0m05.74s
-examples/step-19> ls -l solution-0005.gmv
+examples/\step-19> ls -l solution-0005.gmv
-rw-r--r-- 1 bangerth wheeler 240680494 Sep 9 11:53 solution-0005.gmv
@endcode
So in roughly two minutes we have merged 240MB of data. Counting reading and
-If visualized, the output looks very much like that shown for
+If visualized, the output looks very much like that shown for
step-18. But that's not quite as
important for the moment, rather we are interested in showing how to use the
parameter file. To this end, remember that if no parameter file is given, or if
it is empty, all the default values listed above are used. However, whatever we
-specify in the parameter file is used, unless overridden again by
+specify in the parameter file is used, unless overridden again by
parameters found later on the command line.
don't want to visualize 240MB of data anyway, we only convert the one, the
twelfth, intermediate file to gnuplot format):
@code
-examples/step-19> ./step-19 solution-0005.0000-012.d2 -p solution-0005.prm -o solution-0005.gnuplot
+examples/\step-19> ./\step-19 solution-0005.0000-012.d2 -p solution-0005.prm -o solution-0005.gnuplot
-examples/step-19> ls -l solution-0005.gnuplot
+examples/\step-19> ls -l solution-0005.gnuplot
-rw-r--r-- 1 bangerth wheeler 20281669 Sep 9 12:15 solution-0005.gnuplot
@endcode
If one runs step-19 with this input file, this is what is going to happen:
@code
-examples/step-19> ./step-19 solution-0005.0000-012.d2 -p solution-0005.prm
+examples/\step-19> ./\step-19 solution-0005.0000-012.d2 -p solution-0005.prm
Line 4:
The entry value
-13
programs; maybe not the easiest to use since it is command line driven, but
also universally available on all linux and other unix-like systems):
@code
-examples/step-2> gnuplot
+examples/\step-2> gnuplot
G N U P L O T
Version 3.7 patchlevel 3
If we run the program as is, we get this output:
@code
-examples/step-20> make run
+examples/\step-20> make run
============================ Remaking Makefile.dep
-==============debug========= step-20.cc
-============================ Linking step-20
-============================ Running step-20
+==============debug========= \step-20.cc
+============================ Linking \step-20
+============================ Running \step-20
Number of active cells: 64
Total number of cells: 85
Number of degrees of freedom: 208 (144+64)
homogenous and, while permeably to fluids, not overly so; on the other stone,
the stone has cracked, or faulted, along one line, and the fluids flow much
easier along this large crask. Here is how we could implement something like
-this:
+this:
@code
template <int dim>
void
const double distance_to_flowline
= std::fabs(points[p][1]-0.2*std::sin(10*points[p][0]));
-
+
const double permeability = std::max(std::exp(-(distance_to_flowline*
distance_to_flowline)
/ (0.1 * 0.1)),
0.001);
-
+
for (unsigned int d=0; d<dim; ++d)
values[p][d][d] = 1./permeability;
}
With a significantly higher mesh resolution, we can visualize this, here with
-x- and y-velocity:
+x- and y-velocity:
@image html step-20.u-wiggle.png
{
public:
KInverse ();
-
+
virtual void value_list (const std::vector<Point<dim> > &points,
std::vector<Tensor<2,dim> > &values) const;
template <int dim>
-KInverse<dim>::KInverse ()
+KInverse<dim>::KInverse ()
{
const unsigned int N = 40;
centers.resize (N);
for (unsigned int i=0; i<centers.size(); ++i)
permeability += std::exp(-(points[p]-centers[i]).square()
/ (0.1 * 0.1));
-
+
const double normalized_permeability
= std::max(permeability, 0.005);
-
+
for (unsigned int d=0; d<dim; ++d)
values[p][d][d] = 1./normalized_permeability;
}
yields the following output (when the flag is set to optimized in the
Makefile):
@code
-examples/step-22> make run
+examples/\step-22> make run
============================ Remaking Makefile.dep
-==============optimized===== step-22.cc
-============================ Linking step-22
-============================ Running step-22
+==============optimized===== \step-22.cc
+============================ Linking \step-22
+============================ Running \step-22
Refinement cycle 0
Number of active cells: 64
Number of degrees of freedom: 679 (594+85)
href="#improved-solver">below</a>.
As for the graphical output, the grids generated during the solution
-look as follow:
+look as follow:
<TABLE WIDTH="60%" ALIGN="center">
<tr>
@image html step-22.2d.sparsity-nor.png
In order to generate such a graph, you have to insert a piece of
-code like the following to the end of the setup step.
+code like the following to the end of the setup step.
@code
{
std::ofstream out ("sparsity_pattern.gpl");
matrix, again after one adaptive refinement. Apart from the fact that the matrix
size has increased, it is also visible that there are many more entries
in the matrix. Moreover, even for the optimized renumbering, there will be a
-considerable amount of tentative fill-in elements. This illustrates why UMFPACK
+considerable amount of tentative fill-in elements. This illustrates why UMFPACK
is not a good choice in 3D - a full decomposition needs many new entries that
eventually won't fit into the physical memory (RAM):
<code>vmult</code> operation of the inverse matrix structure will converge in
one single CG step, but this changes in 3D where we only use an ILU
preconditioner. There, the number of required preconditioned CG steps to
-invert $A$ increases as the mesh is refined, and each <code>vmult</code>
+invert $A$ increases as the mesh is refined, and each <code>vmult</code>
operation involves on average approximately 14, 23, 36, 59, 75 and 101 inner
CG iterations in the refinement steps shown above. (On the other hand,
the number of iterations for applying the inverse pressure mass matrix is
-always around five, both in two and three dimensions.) To summarize, most work
+always around five, both in two and three dimensions.) To summarize, most work
is spent on solving linear systems with the same matrix $A$ over and over again.
-What makes this look even worse is the fact that we
+What makes this look even worse is the fact that we
actually invert a matrix that is about 95 precent the size of the total system
matrix and stands for 85 precent of the non-zero entries in the sparsity
pattern. Hence, the natural question is whether it is reasonable to solve a
-linear system with matrix $A$ for about 15 times when calculating the solution
+linear system with matrix $A$ for about 15 times when calculating the solution
to the block system.
-
+
The answer is, of course, that we can do that in a few other (most of the time
better) ways.
Nevertheless, it has to be remarked that an indefinite system as the one
at hand puts indeed much higher
demands on the linear algebra than standard elliptic problems as we have seen
-in the early tutorial programs. The improvements are still rather
+in the early tutorial programs. The improvements are still rather
unsatisfactory, if one compares with an elliptic problem of similar
size. Either way, we will introduce below a number of improvements to the
linear solver, a discussion that we will re-consider again with additional
several choices for the renumbering of dofs for the Stokes equations. The best
result regarding the computing time was found for the King ordering, which is
accessed through the call DoFRenumbering::boost::king_ordering. With that
-program, the inner solver needs considerably less operations, e.g. about 62
-inner CG iterations for the inversion of $A$ at cycle 4 compared to about 75
-iterations with the standard Cuthill-McKee-algorithm. Also, the computing time
-at cycle 4 decreased from about 17 to 11 minutes for the <code>solve()</code>
+program, the inner solver needs considerably less operations, e.g. about 62
+inner CG iterations for the inversion of $A$ at cycle 4 compared to about 75
+iterations with the standard Cuthill-McKee-algorithm. Also, the computing time
+at cycle 4 decreased from about 17 to 11 minutes for the <code>solve()</code>
call. However, the King ordering (and the orderings provided by the
DoFRenumbering::boost namespace in general) has a serious drawback - it uses
much more memory than the in-build deal versions, since it acts on abstract
unknowns.
<h5>Better preconditioner for the inner CG solver</h5>
-Another idea to improve the situation even more would be to choose a
-preconditioner that makes CG for the (0,0) matrix $A$ converge in a
+Another idea to improve the situation even more would be to choose a
+preconditioner that makes CG for the (0,0) matrix $A$ converge in a
mesh-independent number of iterations, say 10 to 30. We have seen such a
canditate in step-16: multigrid.
<h5>Block Schur complement preconditioner</h5>
-Even with a good preconditioner for $A$, we still
-need to solve of the same linear system repeatedly (with different
-right hand sides, though) in order to make the Schur complement solve
-converge. The approach we are going to discuss here is how inner iteration
-and outer iteration can be combined. If we persist in calculating the Schur
+Even with a good preconditioner for $A$, we still
+need to solve of the same linear system repeatedly (with different
+right hand sides, though) in order to make the Schur complement solve
+converge. The approach we are going to discuss here is how inner iteration
+and outer iteration can be combined. If we persist in calculating the Schur
complement, there is no other possibility.
The alternative is to attack the block system at once and use an approximate
few iterations. Using the Schur complement $S = B A^{-1} B^T$, one finds that
@f{eqnarray*}
P^{-1}
- =
+ =
\left(\begin{array}{cc}
A^{-1} & 0 \\ S^{-1} B A^{-1} & -S^{-1}
\end{array}\right)
@f{eqnarray*}
P^{-1}\left(\begin{array}{cc}
A & B^T \\ B & 0
- \end{array}\right)
- =
+ \end{array}\right)
+ =
\left(\begin{array}{cc}
A^{-1} & 0 \\ S^{-1} B A^{-1} & -S^{-1}
\end{array}\right)\cdot \left(\begin{array}{cc}
A & B^T \\ B & 0
- \end{array}\right)
- =
+ \end{array}\right)
+ =
\left(\begin{array}{cc}
I & A^{-1} B^T \\ 0 & I
\end{array}\right).
discuss the choice of an adequate solver for this problem. First, we are
going to have a closer look at the implementation of the preconditioner.
-Since $P$ is aimed to be a preconditioner only, we shall use approximations to
+Since $P$ is aimed to be a preconditioner only, we shall use approximations to
the inverse of the Schur complement $S$ and the matrix $A$. Hence, the Schur
complement will be approximated by the pressure mass matrix $M_p$, and we use
a preconditioner to $A$ (without an InverseMatrix class around it) for
private:
const SmartPointer<const BlockSparseMatrix<double> > system_matrix;
- const SmartPointer<const InverseMatrix<SparseMatrix<double>,
+ const SmartPointer<const InverseMatrix<SparseMatrix<double>,
PreconditionerMp > > m_inverse;
const PreconditionerA &a_preconditioner;
-
+
mutable Vector<double> tmp;
};
{}
// Now the interesting function, the multiplication of
- // the preconditioner with a BlockVector.
+ // the preconditioner with a BlockVector.
template <class PreconditionerA, class PreconditionerMp>
void BlockSchurPreconditioner<PreconditionerA, PreconditionerMp>::vmult (
BlockVector<double> &dst,
{
// Form u_new = A^{-1} u
a_preconditioner.vmult (dst.block(0), src.block(0));
- // Form tmp = - B u_new + p
+ // Form tmp = - B u_new + p
// (<code>SparseMatrix::residual</code>
// does precisely this)
system_matrix->block(1,0).residual(tmp, dst.block(0), src.block(1));
// Change sign in tmp
tmp *= -1;
- // Multiply by approximate Schur complement
+ // Multiply by approximate Schur complement
// (i.e. a pressure mass matrix)
m_inverse->vmult (dst.block(1), tmp);
}
was used for the solution of the unsymmetric advection problem in step-9. The
second option, the one we are going to choose, is @ref SolverGMRES "GMRES"
(generalized minimum residual). Both methods have their pros and cons - there
-are problems where one of the two candidates clearly outperforms the other, and
+are problems where one of the two candidates clearly outperforms the other, and
vice versa.
-<a href="http://en.wikipedia.org/wiki/GMRES#Comparison_with_other_solvers">Wikipedia</a>'s
+<a href="http://en.wikipedia.org/wiki/GMRES#Comparison_with_other_solvers">Wikipedia</a>'s
article on the GMRES method gives a comparative presentation.
A more comprehensive and well-founded comparsion can be read e.g. in the book by
J.W. Demmel (Applied Numerical Linear Algebra, SIAM, 1997, section 6.6.6).
to perform hundreds of iterations on the block system for large problem sizes
(we won't beat CG!). Actually, this disfavors GMRES: During the GMRES
iterations, a basis of Krylov vectors is successively built up and some
-operations are performed on these vectors. The more vectors are in this basis,
+operations are performed on these vectors. The more vectors are in this basis,
the more operations and memory will be needed. The number of operations scales
-as ${\cal O}(n + k^2)$ and memory as ${\cal O}(kn)$, where $k$ is the number of
+as ${\cal O}(n + k^2)$ and memory as ${\cal O}(kn)$, where $k$ is the number of
vectors in the Krylov basis and $n$ the size of the (block) matrix.
To not let these demands grow excessively, deal.II limits the size $k$ of the
basis to 30 vectors by default.
Then, the basis is rebuilt. This implementation of the GMRES method is called
-GMRES(k), with default $k=30$. What we have gained by this restriction,
+GMRES(k), with default $k=30$. What we have gained by this restriction,
namely a bound on operations and memory requirements, will be compensated by
the fact that we use an incomplete basis - this will increase the number of
-required iterations.
+required iterations.
BiCGStab, on the other hand, won't get slower when many iterations are needed
(one iteration uses only results from one preceeding step and
this problem: The preconditioner applies the inverse of the pressure
mass matrix by using the InverseMatrix class. Since the application of the
inverse matrix to a vector is done only in approximative way (an exact inverse
-is too expensive), this will also affect the solver. In the case of BiCGStab,
+is too expensive), this will also affect the solver. In the case of BiCGStab,
the Krylov vectors will not be orthogonal due to that perturbation. While
this is uncritical for a small number of steps (up to about 50), it ruins the
performance of the solver when these perturbations have grown to a significant
magnitude in the coarse of iterations.
We did some experiments with BiCGStab and found it to
-be faster than GMRES up to refinement cycle 3 (in 3D), but it became very slow
+be faster than GMRES up to refinement cycle 3 (in 3D), but it became very slow
for cycles 4 and 5 (even slower than the original Schur complement), so the
solver is useless in this situation. Choosing a sharper tolerance for the
-inverse matrix class (<code>1e-10*src.l2_norm()</code> instead of
-<code>1e-6*src.l2_norm()</code>) made BiCGStab perform well also for cycle 4,
+inverse matrix class (<code>1e-10*src.l2_norm()</code> instead of
+<code>1e-6*src.l2_norm()</code>) made BiCGStab perform well also for cycle 4,
but did not change the failure on the very large problems.
GMRES is of course also effected by the approximate inverses, but it is not as
sensitive to orthogonality and retains a relatively good performance also for
large sizes, see the results below.
-With this said, we turn to the realization of the solver call with GMRES with
+With this said, we turn to the realization of the solver call with GMRES with
$k=100$ temporary vectors:
@code
system_matrix.block(1,1) = 0;
SparseILU<double> pmass_preconditioner;
- pmass_preconditioner.initialize (pressure_mass_matrix,
+ pmass_preconditioner.initialize (pressure_mass_matrix,
SparseILU<double>::AdditionalData());
-
+
InverseMatrix<SparseMatrix<double>,SparseILU<double> >
m_inverse (pressure_mass_matrix, pmass_preconditioner);
BlockSchurPreconditioner<typename InnerPreconditioner<dim>::type,
- SparseILU<double> >
+ SparseILU<double> >
preconditioner (system_matrix, m_inverse, *A_preconditioner);
-
+
SolverControl solver_control (system_matrix.m(),
1e-6*system_rhs.l2_norm());
GrowingVectorMemory<BlockVector<double> > vector_memory;
SolverGMRES<BlockVector<double> >::AdditionalData gmres_data;
gmres_data.max_n_tmp_vectors = 100;
-
+
SolverGMRES<BlockVector<double> > gmres(solver_control, vector_memory,
gmres_data);
-
+
gmres.solve(system_matrix, solution, system_rhs,
preconditioner);
-
+
constraints.distribute (solution);
-
+
std::cout << " "
<< solver_control.last_step()
<< " block GMRES iterations";
@endcode
-Obviously, one needs to add the include file @ref SolverGMRES
+Obviously, one needs to add the include file @ref SolverGMRES
"<lac/solver_gmres.h>" in order to make this run.
We call the solver with a BlockVector template in order to enable
GMRES to operate on block vectors and matrices.
@code
Refinement cycle 0
Number of active cells: 64
- Number of degrees of freedom: 679 (594+85) [0.005999 s]
+ Number of degrees of freedom: 679 (594+85) [0.005999 s]
Assembling... [0.002 s]
Computing preconditioner... [0.003 s]
Solving...
Refinement cycle 1
Number of active cells: 160
- Number of degrees of freedom: 1683 (1482+201) [0.013998 s]
+ Number of degrees of freedom: 1683 (1482+201) [0.013998 s]
Assembling... [0.005999 s]
Computing preconditioner... [0.012998 s]
Solving...
Refinement cycle 2
Number of active cells: 376
- Number of degrees of freedom: 3813 (3370+443) [0.031995 s]
+ Number of degrees of freedom: 3813 (3370+443) [0.031995 s]
Assembling... [0.014998 s]
Computing preconditioner... [0.044994 s]
Solving...
Refinement cycle 3
Number of active cells: 880
- Number of degrees of freedom: 8723 (7722+1001) [0.074988 s]
+ Number of degrees of freedom: 8723 (7722+1001) [0.074988 s]
Assembling... [0.035995 s]
Computing preconditioner... [0.110983 s]
Solving...
Refinement cycle 4
Number of active cells: 2008
- Number of degrees of freedom: 19383 (17186+2197) [0.180973 s]
+ Number of degrees of freedom: 19383 (17186+2197) [0.180973 s]
Assembling... [0.081987 s]
Computing preconditioner... [0.315952 s]
Solving...
Refinement cycle 5
Number of active cells: 4288
- Number of degrees of freedom: 40855 (36250+4605) [0.386941 s]
+ Number of degrees of freedom: 40855 (36250+4605) [0.386941 s]
Assembling... [0.171974 s]
Computing preconditioner... [0.766883 s]
Solving...
@code
Refinement cycle 0
Number of active cells: 32
- Number of degrees of freedom: 1356 (1275+81) [0.025996 s]
+ Number of degrees of freedom: 1356 (1275+81) [0.025996 s]
Assembling... [0.056992 s]
Computing preconditioner... [0.027995 s]
Solving...
Refinement cycle 1
Number of active cells: 144
- Number of degrees of freedom: 5088 (4827+261) [0.102984 s]
+ Number of degrees of freedom: 5088 (4827+261) [0.102984 s]
Assembling... [0.254961 s]
Computing preconditioner... [0.161976 s]
Solving...
Refinement cycle 2
Number of active cells: 704
- Number of degrees of freedom: 22406 (21351+1055) [0.52592 s]
+ Number of degrees of freedom: 22406 (21351+1055) [0.52592 s]
Assembling... [1.24481 s]
Computing preconditioner... [0.948856 s]
Solving...
Refinement cycle 3
Number of active cells: 3168
- Number of degrees of freedom: 93176 (89043+4133) [2.66759 s]
+ Number of degrees of freedom: 93176 (89043+4133) [2.66759 s]
Assembling... [5.66014 s]
Computing preconditioner... [4.69529 s]
Solving...
Refinement cycle 4
Number of active cells: 11456
- Number of degrees of freedom: 327808 (313659+14149) [12.0242 s]
+ Number of degrees of freedom: 327808 (313659+14149) [12.0242 s]
Assembling... [20.2669 s]
Computing preconditioner... [17.3384 s]
Solving...
Refinement cycle 5
Number of active cells: 45056
- Number of degrees of freedom: 1254464 (1201371+53093) [89.8533 s]
+ Number of degrees of freedom: 1254464 (1201371+53093) [89.8533 s]
Assembling... [80.3588 s]
Computing preconditioner... [73.0849 s]
Solving...
above. Nonetheless, the improvement by a factor of 3-5 for moderate problem
sizes is quite impressive.
-<h5>Combining block preconditioner and multigrid</h5>
-An ultimate linear solver for this problem could be imagined as a
+<h5>Combining block preconditioner and multigrid</h5>
+An ultimate linear solver for this problem could be imagined as a
combination of an optimal
preconditioner for $A$ (e.g. multigrid) and the block preconditioner
described above, which is the approach taken in the step-31
ExcIndexRange (component, 0, this->n_components));
const double x_offset = std::atan(p[1]*4)/3;
-
+
if (component == 0)
return (p[0] < x_offset ? -1 : (p[0] > x_offset ? 1 : 0));
return 0;
}
@endcode
and the following way to generate the mesh as the domain
-$[-2,2]\times[-2,2]\times[-1,0]$
+$[-2,2]\times[-2,2]\times[-1,0]$
@code
std::vector<unsigned int> subdivisions (dim, 1);
subdivisions[0] = 4;
const Point<dim> top_right = (dim == 2 ?
Point<dim>(2,0) :
Point<dim>(2,2,0));
-
+
GridGenerator::subdivided_hyper_rectangle (triangulation,
subdivisions,
bottom_left,
When run, this is what the program produces:
@code
-examples/step-27> make run
-============================ Running step-27
+examples/\step-27> make run
+============================ Running \step-27
Cycle 0:
Number of active cells: 768
Number of degrees of freedom: 3264
<h1>Results</h1>
The current program reads its run-time parameters from an input file
-called <code>step-29.prm</code> that looks like this:
+called <code>\step-29.prm</code> that looks like this:
@code
subsection Mesh & geometry parameters
# Distance of the focal point of the lens to the x-axis
Here's the console output of the program in debug mode:
@code
-examples/step-29> make run
-============================ Running step-29
+examples/\step-29> make run
+============================ Running \step-29
DEAL::Generating grid... done (1.11607s)
DEAL:: Number of active cells: 25600
DEAL::Setting up system... done (1.10807s)
hardly sped up at all:
@code
-============================ Running step-29
+============================ Running \step-29
DEAL::Generating grid... done (0.0280020s)
DEAL:: Number of active cells: 25600
DEAL::Setting up system... done (0.112007s)
viewed as follows: invoke GNUPLOT and enter the following sequence of
commands at its prompt:
@code
-examples/step-3> gnuplot
+examples/\step-3> gnuplot
G N U P L O T
Version 3.7 patchlevel 3
</p>
<ul>
- <li>
+ <li>
Change the geometry and mesh: In the program, we have generated a square
domain and mesh by using the <code>GridGenerator::hyper_cube</code>
function. However, the <code>GridGenerator</code> has a good number of other
VectorTools::interpolate_boundary_values (dof_handler,
1,
ConstantFunction<2>(1.),
- boundary_values);
+ boundary_values);
@endcode
If you have this call immediately after the first one to this function, then
it will interpolate boundary values on faces with boundary indicator 1 to the
the following code to <code>LaplaceProblem::output_results</code>:
@code
std::cout << "Mean value: "
- << VectorTools::compute_mean_value (dof_handler,
+ << VectorTools::compute_mean_value (dof_handler,
QGauss<2>(3),
solution,
0)
as step-31 did, though with an entirely different testcase.
-<h3>Comparison of results with step-31</h3>
+<h3>Comparison of results with \step-31</h3>
Before we go to this testcase, however, let us show a few results from a
slightly earlier version of this program that was solving exactly the
mesh for the last timestep shown into the 10 subdomains used for this
computation. The full dynamics are really only visible by looking at
an animation. <a
-href="http://www.math.tamu.edu/~bangerth/images/pictures/convection-outward/step-32.2d.convection.gif">At
+href="http://www.math.tamu.edu/~bangerth/images/pictures/convection-outward/\step-32.2d.convection.gif">At
this site</a> is such an animation. Beware that this animation is
about 20MB large, though it is well worth watching due to its almost
artistic quality.
We run the problem with the mesh <code>slide.inp</code> (this file is in the
same directory as the source code for this program) and the following input
-deck (available as <code>input.prm</code> in the same directory):
+deck (available as <code>input.prm</code> in the same directory):
@verbatim
# Listing of Parameters
# ---------------------
-# The input grid
+# The input grid
set mesh = slide.inp
# Stabilization parameter
----------------------------------------------------
-Exception on processing:
+Exception on processing:
--------------------------------------------------------
-An error occurred in line <3119> of file <step-33.cc> in function
+An error occurred in line <3119> of file <\step-33.cc> in function
void ConservationLaw<dim>::run() [with int dim = 2]
-The violated condition was:
+The violated condition was:
nonlin_iter <= 10
The name and call sequence of the exception was:
ExcMessage ("No convergence in nonlinear solver")
-Additional Information:
+Additional Information:
No convergence in nonlinear solver
--------------------------------------------------------
<h2>Running the problem</h2>
-The problem's input is parameterized by an input file <code>step-36.prm</code>
+The problem's input is parameterized by an input file <code>\step-36.prm</code>
which could, for example, contain the following text:
@code
periods in $x$ and $y$ directions. This matches the output our program
generates:
@code
-examples/step-36> make run
-============================ Running step-36
+examples/\step-36> make run
+============================ Running \step-36
Number of active cells: 1024
Number of degrees of freedom: 1089
It is always worth playing a few games in the playground! So here goes
with a few suggestions:
-<ul>
+<ul>
<li> The potential used above (called the <i>infinite well</i> because
it is a flat potential surrounded by infinitely high walls) is
play around with the potential by just setting it to something
different in the input file. For example, let us assume that we wanted
to work with the following potential in
-2d:
+2d:
@f[
- V(x,y) = \left\{
+ V(x,y) = \left\{
\begin{array}{ll}
- -100 & \text{if}\ \sqrt{x^2+y^2}<\frac 34 \ \text{and}
+ -100 & \text{if}\ \sqrt{x^2+y^2}<\frac 34 \ \text{and}
\ xy>0
\\
- -5 & \text{if}\ \sqrt{x^2+y^2}<\frac 34 \ \text{and}
+ -5 & \text{if}\ \sqrt{x^2+y^2}<\frac 34 \ \text{and}
\ xy\le 0
\\
0 & \text{otherwise}
set Potential = if (x^2 + y^2 < 0.75^2, if (x*y > 0, -100, -5), 0)
@endcode
If in addition we also increase the mesh refinement by one level, we get the
-following results:
+following results:
@code
-examples/step-36> make run
-============================ Running step-36
+examples/\step-36> make run
+============================ Running \step-36
Number of active cells: 4096
Number of degrees of freedom: 4225
Number of degrees of freedom: 20609
182 CG iterations needed to obtain convergence.
--------------------------------------------------------
-An error occurred in line <273> of file <step-5.cc> in function
+An error occurred in line <273> of file <\step-5.cc> in function
void Coefficient<dim>::value_list(const std::vector<Point<dim>, std::allocator<Point<dim> > >&, std::vector<double, std::allocator<double> >&, unsigned int)
const [with int dim = 2]
The violated condition was:
Stacktrace:
-----------
-#0 ./step-5: Coefficient<2>::value_list(std::vector<Point<2>, std::allocator<Point<2> > > const&, std::vector<double, std::allocator<double> >&, unsigned) const
-#1 ./step-5: main
+#0 ./\step-5: Coefficient<2>::value_list(std::vector<Point<2>, std::allocator<Point<2> > > const&, std::vector<double, std::allocator<double> >&, unsigned) const
+#1 ./\step-5: main
--------------------------------------------------------
make: *** [run] Aborted
@endcode
As for the error — let's look at it again:
@code
--------------------------------------------------------
-An error occurred in line <273> of file <step-5.cc> in function
+An error occurred in line <273> of file <\step-5.cc> in function
void Coefficient<dim>::value_list(const std::vector<Point<dim>, std::allocator<Point<dim> > >&, std::vector<double, std::allocator<double> >&, unsigned int)
const [with int dim = 2]
The violated condition was:
Stacktrace:
-----------
-#0 ./step-5: Coefficient<2>::value_list(std::vector<Point<2>, std::allocator<Point<2> > > const&, std::vector<double, std::allocator<double> >&, unsigned) const
-#1 ./step-5: main
+#0 ./\step-5: Coefficient<2>::value_list(std::vector<Point<2>, std::allocator<Point<2> > > const&, std::vector<double, std::allocator<double> >&, unsigned) const
+#1 ./\step-5: main
--------------------------------------------------------
make: *** [run] Aborted
@endcode
-----------
#0 /u/bangerth/p/deal.II/1/deal.II/lib/libbase.g.so: Subscriptor::~Subscriptor()
#1 /u/bangerth/p/deal.II/1/deal.II/lib/libdeal_II_2d.g.so: FiniteElement<2>::~FiniteElement()
-#2 ./step-6: FE_Poly<TensorProductPolynomials<2>, 2>::~FE_Poly()
-#3 ./step-6: FE_Q<2>::~FE_Q()
-#4 ./step-6: Step6<2>::~Step6()
-#5 ./step-6: main
+#2 ./\step-6: FE_Poly<TensorProductPolynomials<2>, 2>::~FE_Poly()
+#3 ./\step-6: FE_Q<2>::~FE_Q()
+#4 ./\step-6: Step6<2>::~Step6()
+#5 ./\step-6: main
--------------------------------------------------------
make: *** [run] Aborted
@endcode
From the above error message, we conclude that an object of type
<code>10DoFHandlerILi2EE</code> is still using the object of type
<code>4FE_QILi2EE</code>. These are of course <a
-href="http://en.wikipedia.org/wiki/Name_mangling">"mangled" names</a> for
+href="http://en.wikipedia.org/wiki/Name_mangling">"mangled" names</a> for
<code>DoFHandler</code> and <code>FE_Q</code>. The mangling works as
follows: the first number indicates the number of characters of the
template class, i.e. 10 for <code>DoFHandler</code> and 4
files <code>solution-adaptive-q1.gmv</code>,
<code>solution-global-q1.gmv</code>, and
<code>solution-global-q2.gmv</code>. We show the latter in a 3d view
-here:
+here:
@image html step-7.solution.png
@code
-examples/step-7> make run
-============================ Running step-7
+examples/\step-7> make run
+============================ Running \step-7
Solving with Q1 elements, adaptive refinement
=============================================
Number of active cells: 466
Number of degrees of freedom: 543
-cycle cells dofs L2 H1 Linfty
- 0 4 9 1.198e+00 2.732e+00 1.383e+00
- 1 13 22 8.795e-02 1.193e+00 1.816e-01
- 2 31 46 8.147e-02 1.167e+00 1.654e-01
- 3 64 87 7.702e-02 1.077e+00 1.310e-01
- 4 127 160 4.643e-02 7.988e-01 6.745e-02
- 5 244 297 2.470e-02 5.568e-01 3.668e-02
- 6 466 543 1.622e-02 4.107e-01 2.966e-02
+cycle cells dofs L2 H1 Linfty
+ 0 4 9 1.198e+00 2.732e+00 1.383e+00
+ 1 13 22 8.795e-02 1.193e+00 1.816e-01
+ 2 31 46 8.147e-02 1.167e+00 1.654e-01
+ 3 64 87 7.702e-02 1.077e+00 1.310e-01
+ 4 127 160 4.643e-02 7.988e-01 6.745e-02
+ 5 244 297 2.470e-02 5.568e-01 3.668e-02
+ 6 466 543 1.622e-02 4.107e-01 2.966e-02
Solving with Q1 elements, global refinement
===========================================
Number of active cells: 16384
Number of degrees of freedom: 16641
-cycle cells dofs L2 H1 Linfty
- 0 4 9 1.198e+00 2.732e+00 1.383e+00
- 1 16 25 8.281e-02 1.190e+00 1.808e-01
- 2 64 81 8.142e-02 1.129e+00 1.294e-01
- 3 256 289 2.113e-02 5.828e-01 4.917e-02
- 4 1024 1089 5.319e-03 2.934e-01 1.359e-02
- 5 4096 4225 1.332e-03 1.469e-01 3.482e-03
- 6 16384 16641 3.332e-04 7.350e-02 8.758e-04
-
-n cells H1 L2
- 0 4 2.732e+00 - 1.198e+00 - -
- 1 16 1.190e+00 1.20 8.281e-02 14.47 3.86
- 2 64 1.129e+00 0.08 8.142e-02 1.02 0.02
- 3 256 5.828e-01 0.95 2.113e-02 3.85 1.95
- 4 1024 2.934e-01 0.99 5.319e-03 3.97 1.99
- 5 4096 1.469e-01 1.00 1.332e-03 3.99 2.00
- 6 16384 7.350e-02 1.00 3.332e-04 4.00 2.00
+cycle cells dofs L2 H1 Linfty
+ 0 4 9 1.198e+00 2.732e+00 1.383e+00
+ 1 16 25 8.281e-02 1.190e+00 1.808e-01
+ 2 64 81 8.142e-02 1.129e+00 1.294e-01
+ 3 256 289 2.113e-02 5.828e-01 4.917e-02
+ 4 1024 1089 5.319e-03 2.934e-01 1.359e-02
+ 5 4096 4225 1.332e-03 1.469e-01 3.482e-03
+ 6 16384 16641 3.332e-04 7.350e-02 8.758e-04
+
+n cells H1 L2
+ 0 4 2.732e+00 - 1.198e+00 - -
+ 1 16 1.190e+00 1.20 8.281e-02 14.47 3.86
+ 2 64 1.129e+00 0.08 8.142e-02 1.02 0.02
+ 3 256 5.828e-01 0.95 2.113e-02 3.85 1.95
+ 4 1024 2.934e-01 0.99 5.319e-03 3.97 1.99
+ 5 4096 1.469e-01 1.00 1.332e-03 3.99 2.00
+ 6 16384 7.350e-02 1.00 3.332e-04 4.00 2.00
Solving with Q2 elements, global refinement
===========================================
Number of active cells: 16384
Number of degrees of freedom: 66049
-cycle cells dofs L2 H1 Linfty
- 0 4 25 1.433e+00 2.445e+00 1.286e+00
- 1 16 81 7.912e-02 1.168e+00 1.728e-01
- 2 64 289 7.755e-03 2.511e-01 1.991e-02
- 3 256 1089 9.969e-04 6.235e-02 2.764e-03
- 4 1024 4225 1.265e-04 1.571e-02 3.527e-04
- 5 4096 16641 1.587e-05 3.937e-03 4.343e-05
- 6 16384 66049 1.986e-06 9.847e-04 5.402e-06
-
-n cells H1 L2
- 0 4 2.445e+00 - 1.433e+00 - -
- 1 16 1.168e+00 1.07 7.912e-02 18.11 4.18
- 2 64 2.511e-01 2.22 7.755e-03 10.20 3.35
- 3 256 6.235e-02 2.01 9.969e-04 7.78 2.96
- 4 1024 1.571e-02 1.99 1.265e-04 7.88 2.98
- 5 4096 3.937e-03 2.00 1.587e-05 7.97 2.99
- 6 16384 9.847e-04 2.00 1.986e-06 7.99 3.00
+cycle cells dofs L2 H1 Linfty
+ 0 4 25 1.433e+00 2.445e+00 1.286e+00
+ 1 16 81 7.912e-02 1.168e+00 1.728e-01
+ 2 64 289 7.755e-03 2.511e-01 1.991e-02
+ 3 256 1089 9.969e-04 6.235e-02 2.764e-03
+ 4 1024 4225 1.265e-04 1.571e-02 3.527e-04
+ 5 4096 16641 1.587e-05 3.937e-03 4.343e-05
+ 6 16384 66049 1.986e-06 9.847e-04 5.402e-06
+
+n cells H1 L2
+ 0 4 2.445e+00 - 1.433e+00 - -
+ 1 16 1.168e+00 1.07 7.912e-02 18.11 4.18
+ 2 64 2.511e-01 2.22 7.755e-03 10.20 3.35
+ 3 256 6.235e-02 2.01 9.969e-04 7.78 2.96
+ 4 1024 1.571e-02 1.99 1.265e-04 7.88 2.98
+ 5 4096 3.937e-03 2.00 1.587e-05 7.97 2.99
+ 6 16384 9.847e-04 2.00 1.986e-06 7.99 3.00
@endcode