From 6dd31bf3b49e405dce3c4f66e85d575cccd24b25 Mon Sep 17 00:00:00 2001 From: Matthias Maier Date: Tue, 12 Feb 2019 00:19:47 -0600 Subject: [PATCH] examples/step-20: Change preconditioner to a fixed number of iterations This improves the total runtime performance with preconditioner significantly! (Even though the total number of iterations increases significantly.) --- examples/step-20/doc/intro.dox | 27 ++++++++++++++++++--------- examples/step-20/step-20.cc | 9 ++++----- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/examples/step-20/doc/intro.dox b/examples/step-20/doc/intro.dox index 844507e93f..773141dfcc 100644 --- a/examples/step-20/doc/intro.dox +++ b/examples/step-20/doc/intro.dox @@ -654,12 +654,21 @@ with $M$ is defined: it is the multiplication with the inverse of the diagonal of $M$; in other words, the operation $({\textrm{diag}\ }M)^{-1}x$ on a vector $x$ is exactly what PreconditionJacobi does.) -With all this, we nearly already have the preconditioner: it should be the -inverse of the approximate Schur complement. We implement this again with -the inverse_operator() function +With all this we almost have the preconditioner completed: it should be the +inverse of the approximate Schur complement. We implement this again by +creating a linear operator with inverse_operator() function. This time +however we would like to choose a relatively modest tolerance for the CG +solver (that inverts op_aS). The reasoning is that +op_aS is only coarse approximation to op_S, so we +actually do not need to invert it exactly. This, however creates a subtle +problem: preconditioner_S will be used in the final outer CG +iteration to create an orthogonal basis. But for this to work, it must be +precisely the same linear operation for every invocation. We ensure this by +using an IterationNumberControl that allows us to fix the number of CG +iterations that are performed to a fixed small number (in our case 30): @code - ReductionControl reduction_control_aS(2000, 1.e-18, 1.0e-6); - SolverCG<> solver_aS(reduction_control_aS); + IterationNumberControl iteration_number_control_aS(30, 1.e-18); + SolverCG<> solver_aS(iteration_number_control_aS); PreconditionIdentity preconditioner_aS; const auto preconditioner_S = inverse_operator(op_aS, solver_aS, preconditioner_aS); @@ -672,15 +681,15 @@ expensive preconditioner, almost as expensive as inverting the Schur complement itself. We can expect it to significantly reduce the number of outer iterations required for the Schur complement. In fact it does: in a typical run on 7 times refined meshes using elements of order 0, the number of -outer iterations drops from 592 to 25. On the other hand, we now have to apply +outer iterations drops from 592 to 39. On the other hand, we now have to apply a very expensive preconditioner 25 times. A better measure is therefore simply the run-time of the program: on a current laptop (as of January 2019), it -drops from 3.57 to 2.48 seconds for this test case. That doesn't seem too +drops from 3.57 to 2.05 seconds for this test case. That doesn't seem too impressive, but the savings become more pronounced on finer meshes and with elements of higher order. For example, an seven times refined mesh and using elements of order 2 (which amounts to about 0.4 million degrees of -freedom) yields an improvement of 1134 to 28 outer iterations, at a runtime -of 168 seconds to 78 seconds. Not earth shattering, but significant. +freedom) yields an improvement of 1134 to 83 outer iterations, at a runtime +of 168 seconds to 40 seconds. Not earth shattering, but significant.

Definition of the test case

diff --git a/examples/step-20/step-20.cc b/examples/step-20/step-20.cc index 5879d3b6a1..f1b12f6e18 100644 --- a/examples/step-20/step-20.cc +++ b/examples/step-20/step-20.cc @@ -610,11 +610,10 @@ namespace Step20 transpose_operator(op_B) * linear_operator(preconditioner_M) * op_B; // We now create a preconditioner out of op_aS that - // applies a small number of CG iterations (until a very modest - // relative reduction of $10^{-3}$ is reached): - ReductionControl reduction_control_aS(2000, 1.e-18, 1.0e-3); - SolverCG<> solver_aS(reduction_control_aS); - PreconditionIdentity preconditioner_aS; + // applies a fixed number of 30 (inexpensive) CG iterations: + IterationNumberControl iteration_number_control_aS(30, 1.e-18); + SolverCG<> solver_aS(iteration_number_control_aS); + PreconditionIdentity preconditioner_aS; const auto preconditioner_S = inverse_operator(op_aS, solver_aS, preconditioner_aS); -- 2.39.5