From: Wolfgang Bangerth
Date: Sat, 25 Oct 2014 02:31:40 +0000 (-0500)
Subject: Add a mechanism to attach slots to the signal that now controls convergence.
X-Git-Tag: v8.2.0-rc1~92^2~1
X-Git-Url: https://gitweb.dealii.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e0e3ea8b2fde9142d1c62937ae032c7de71bf703;p=dealii.git
Add a mechanism to attach slots to the signal that now controls convergence.
Document this with an example that illustrates how we can observe
the iteration in process.
---
diff --git a/doc/doxygen/images/cg-monitor-smoothing-0.png b/doc/doxygen/images/cg-monitor-smoothing-0.png
new file mode 100644
index 0000000000..88476efaf2
Binary files /dev/null and b/doc/doxygen/images/cg-monitor-smoothing-0.png differ
diff --git a/doc/doxygen/images/cg-monitor-smoothing-1.png b/doc/doxygen/images/cg-monitor-smoothing-1.png
new file mode 100644
index 0000000000..93985d8350
Binary files /dev/null and b/doc/doxygen/images/cg-monitor-smoothing-1.png differ
diff --git a/doc/doxygen/images/cg-monitor-smoothing-2.png b/doc/doxygen/images/cg-monitor-smoothing-2.png
new file mode 100644
index 0000000000..da9bb014ac
Binary files /dev/null and b/doc/doxygen/images/cg-monitor-smoothing-2.png differ
diff --git a/doc/doxygen/images/cg-monitor-smoothing-3.png b/doc/doxygen/images/cg-monitor-smoothing-3.png
new file mode 100644
index 0000000000..92e4c81bd6
Binary files /dev/null and b/doc/doxygen/images/cg-monitor-smoothing-3.png differ
diff --git a/doc/doxygen/images/cg-monitor-smoothing-4.png b/doc/doxygen/images/cg-monitor-smoothing-4.png
new file mode 100644
index 0000000000..d1bd7aef6c
Binary files /dev/null and b/doc/doxygen/images/cg-monitor-smoothing-4.png differ
diff --git a/doc/doxygen/images/cg-monitor-smoothing-5.png b/doc/doxygen/images/cg-monitor-smoothing-5.png
new file mode 100644
index 0000000000..43e5707a88
Binary files /dev/null and b/doc/doxygen/images/cg-monitor-smoothing-5.png differ
diff --git a/doc/news/changes.h b/doc/news/changes.h
index ce74a29e2b..7dc07cdaca 100644
--- a/doc/news/changes.h
+++ b/doc/news/changes.h
@@ -39,6 +39,15 @@ inconvenience this causes.
+ - Removed: The base class to the iterative linear solvers, Solver,
+ received a SolverControl object upon construction and had a member
+ function
control()
that returned a reference to the
+ object previously passed in. The class now no longer stores such
+ a reference, and consequently, the function has been removed.
+
+ (Wolfgang Bangerth, 2014/10/24)
+
+
- Removed: The constructor of the Utilities::MPI::MPI_InitFinalize
class used to interpret a last argument equal to numbers::invalid_unsigned_int
as "create as many threads as there are processor cores on the current
@@ -110,6 +119,15 @@ inconvenience this causes.
+ - New: The classes implementing iterative solvers have gained
+ a mechanism by which it is possible to observe the progress of
+ the iterations, or to influence when to stop the iteration. The
+ documentation of the Solver class now contains an extended
+ discussion and example of this functionality.
+
+ (Wolfgang Bangerth, 2014/10/24)
+
+
- New: There is now a section in the introduction of step-36 that
discusses the interaction of Dirichlet boundary values and the solution
of eigenvalue problems.
diff --git a/include/deal.II/lac/solver.h b/include/deal.II/lac/solver.h
index 327f7e4e6f..7431309c6e 100644
--- a/include/deal.II/lac/solver.h
+++ b/include/deal.II/lac/solver.h
@@ -1,6 +1,6 @@
// ---------------------------------------------------------------------
//
-// Copyright (C) 1998 - 2013 by the deal.II authors
+// Copyright (C) 1998 - 2014 by the deal.II authors
//
// This file is part of the deal.II library.
//
@@ -150,8 +150,163 @@ template class Vector;
* way to give these additional data to the @p SolverSelector object for each
* solver which it may use.
*
+ *
+ *
Observing the progress of linear solver iterations
+ *
+ * The Solver class, being the base class for all of the iterative solvers such
+ * as SolverCG, SolverGMRES, etc, provides the facilities by which actual solver
+ * implementations determine whether the iteration is converged, not yet
+ * converged, or has failed. Typically, this is done using an object of type
+ * SolverControl that is passed to the solver classes's constructors and from
+ * them down to the constructor of this base class. Every one of the tutorial
+ * programs that solves a linear problem (starting with step-3) uses this
+ * method and it is described in detail there. However, the underlying mechanism
+ * is more general and allows for many other uses to observe how the linear
+ * solver iterations progress.
+ *
+ * The basic approach is that the iterative solvers invoke a signal at
+ * the end of each iteration to determine whether the solution is converged.
+ * A signal is a class that has, conceptually, a list of pointers to functions
+ * and every time the signal is invoked, each of these functions are called.
+ * In the language of signals, the functions called are called slots
+ * and one can attach any number of slots to a signal.
+ * (The implementation of signals and slots we use here is the one from the
+ * BOOST.signals2 library.) A number of details may clarify what is happening
+ * underneath:
+ * - In reality, the signal object does not store pointers to functions, but
+ * function objects as slots. Each slot must conform to a particular
+ * signature: here, it is an object that can be called with three arguments
+ * (the number of the current linear iteration, the current residual, and
+ * the current iterate; more specifics are discussed in the documentation of
+ * the connect() function). A pointer to a function with this argument list
+ * satisfies the requirements, but you can also pass a member function
+ * whose this
argument has been bound using the
+ * std_cxx11::bind
mechanism (see the example below).
+ * - Each of the slots will return a value that indicates whether
+ * the iteration should continue, should stop because it has succeeded,
+ * or stop because it has failed. The return type of slots is therefore
+ * of type SolverControl::State. The returned values from all of the
+ * slots will then have to be combined before they are returned to the
+ * iterative solver that invoked the signal. The way this works is that
+ * if at least one slot returned SolverControl::failure, then the
+ * combined value is SolverControl::failure; otherwise, if at least
+ * one slot returned SolverControl::iterate, then this is going to be
+ * the return value of the signal; finally, only if all slots return
+ * SolverControl::success will the signal's return value be
+ * SolverControl::success.
+ * - It may of course be that a particular
+ * slot has been connected to the signal only to observe how the
+ * solution or a specific part of it converges, but has no particular
+ * opinion on whether the iteration should continue or not. In such
+ * cases, the slot should just return SolverControl::success, which
+ * is the weakest of all return values according to the rules laid
+ * out above.
+ *
+ * Given all this, it should now be obvious how the SolverControl
+ * object fits into this scheme: when a SolverControl object is passed
+ * to the constructor of the current class, we simply connect the
+ * SolverControl::check() function of that object as a slot to the
+ * signal we maintain here. In other words, since a Solver object
+ * is always constructed using a SolverControl object, there is
+ * always at least one slot associated with the signal, namely the
+ * one that determines convergence.
+ *
+ * On the other hand, using the connect() member function, it is
+ * possible to connect any number of other slots to the signal to
+ * observe whatever it is you want to observe. The connect() function
+ * also returns an object that describes the connection from the
+ * signal to the slot, and the corresponding BOOST functions then allow
+ * you to disconnect the slot if you want.
+ *
+ * An example may illuminate these issues. In the step-3 tutorial
+ * program, let us add a member function as follows to the main class:
+ * @code
+ * SolverControl::State
+ * Step3::write_intermediate_solution (const unsigned int iteration,
+ * const double , //check_value
+ * const Vector ¤t_iterate) const
+ * {
+ * DataOut<2> data_out;
+ * data_out.attach_dof_handler (dof_handler);
+ * data_out.add_data_vector (current_iterate, "solution");
+ * data_out.build_patches ();
+ *
+ * std::ofstream output ((std::string("solution-")
+ * + Utilities::int_to_string(iteration,4) + ".vtu").c_str());
+ * data_out.write_vtu (output);
+ *
+ * return SolverControl::success;
+ * }
+ * @endcode
+ * The function satisfies the signature necessary to be a slot for the
+ * signal discussed above, with the exception that it is a member
+ * function and consequently requires a this
pointer.
+ * What the function does is to take the vector given as last
+ * argument and write it into a file in VTU format with a file
+ * name derived from the number of the iteration.
+ *
+ * This function can then be hooked into the CG solver by modifying
+ * the Step3::solve()
function as follows:
+ * @code
+ * void Step3::solve ()
+ * {
+ * SolverControl solver_control (1000, 1e-12);
+ * SolverCG<> solver (solver_control);
+ *
+ * solver.connect (std_cxx11::bind (&Step3::write_intermediate_solution,
+ * this,
+ * std_cxx11::_1,
+ * std_cxx11::_2,
+ * std_cxx11::_3));
+ * solver.solve (system_matrix, solution, system_rhs,
+ * PreconditionIdentity());
+ * }
+ * @endcode
+ * The use of std_cxx11::bind
here ensures that we convert
+ * the member function with its three arguments plus the this
+ * pointer, to a function that only takes three arguments, by fixing
+ * the implicit this
argument of the function to the
+ * this
pointer in the current function.
+ *
+ * It is well understood that the CG method is a smoothing iteration (in
+ * the same way as the more commonly used Jacobi or SSOR iterations are
+ * smoothers). The code above therefore allows to observe how the solution
+ * becomes smoother and smoother in every iteration. This is best
+ * observed by initializing the solution vector with randomly distributed
+ * numbers in $[-1,1]$, using code such as
+ * @code
+ * for (unsigned int i=0; i
+ *
+ *
+ * @image html "cg-monitor-smoothing-0.png"
+ * |
+ *
+ * @image html "cg-monitor-smoothing-1.png"
+ * |
+ *
+ * @image html "cg-monitor-smoothing-2.png"
+ * |
+ *
+ *
+ *
+ * @image html "cg-monitor-smoothing-3.png"
+ * |
+ *
+ * @image html "cg-monitor-smoothing-4.png"
+ * |
+ *
+ * @image html "cg-monitor-smoothing-5.png"
+ * |
+ *
+ *
+ *
* @ingroup Solvers
- * @author Wolfgang Bangerth, Guido Kanschat, Ralf Hartmann, 1997-2001, 2005
+ * @author Wolfgang Bangerth, Guido Kanschat, Ralf Hartmann, 1997-2001, 2005, 2014
*/
template >
class Solver : public Subscriptor
@@ -192,7 +347,44 @@ public:
* as long as that of the solver
* object.
*/
- Solver (SolverControl &solver_control);
+ Solver (SolverControl &solver_control);
+
+ /**
+ * Connect a function object that will be called periodically within
+ * iterative solvers. This function is used to attach monitors to
+ * iterative solvers, either to determine when convergence has happened,
+ * or simply to observe the progress of an iteration. See the documentation
+ * of this class for more information.
+ *
+ * @param slot A function object specified here will, with
+ * each call, receive the number of the current iteration,
+ * the value that is used to check for convergence (typically the residual
+ * of the current iterate with respect to the linear system to be solved)
+ * and the currently best available guess for the current iterate.
+ * Note that some solvers do not update the approximate solution in every
+ * iteration but only after convergence or failure has been determined
+ * (GMRES is an example); in such cases, the vector passed as the last
+ * argument to the signal is simply the best approximate at the time
+ * the signal is called, but not the vector that will be returned if
+ * the signal's return value indicates that the iteration should be
+ * terminated. The function object must return a SolverControl::State
+ * value that indicates whether the iteration should continue, has
+ * failed, or has succeeded. The results of all connected functions
+ * will then be combined to determine what should happen with the
+ * iteration.
+ *
+ * @return A connection object that represents the connection from the
+ * signal to the function object. It can be used to disconnect the
+ * function object again from the signal. See the documentation of
+ * the BOOST Signals2 library for more information on connection
+ * management.
+ */
+ boost::signals2::connection
+ connect (const std_cxx11::function &slot);
+
+
protected:
/**
@@ -313,10 +505,10 @@ Solver::Solver (SolverControl &solver_control,
// only takes two arguments, the iteration and the check_value, and so
// we simply ignore the third argument that is passed in whenever the
// signal is executed
- iteration_status.connect (std_cxx11::bind(&SolverControl::check,
- std_cxx11::ref(solver_control),
- std_cxx11::_1,
- std_cxx11::_2));
+ connect (std_cxx11::bind(&SolverControl::check,
+ std_cxx11::ref(solver_control),
+ std_cxx11::_1,
+ std_cxx11::_2));
}
@@ -332,14 +524,26 @@ Solver::Solver (SolverControl &solver_control)
// only takes two arguments, the iteration and the check_value, and so
// we simply ignore the third argument that is passed in whenever the
// signal is executed
- iteration_status.connect (std_cxx11::bind(&SolverControl::check,
- std_cxx11::ref(solver_control),
- std_cxx11::_1,
- std_cxx11::_2));
+ connect (std_cxx11::bind(&SolverControl::check,
+ std_cxx11::ref(solver_control),
+ std_cxx11::_1,
+ std_cxx11::_2));
}
+template
+inline
+boost::signals2::connection
+Solver::
+connect (const std_cxx11::function &slot)
+{
+ return iteration_status.connect (slot);
+}
+
+
DEAL_II_NAMESPACE_CLOSE
diff --git a/tests/lac/solver_monitor.cc b/tests/lac/solver_monitor.cc
new file mode 100644
index 0000000000..aff175969f
--- /dev/null
+++ b/tests/lac/solver_monitor.cc
@@ -0,0 +1,141 @@
+// ---------------------------------------------------------------------
+//
+// Copyright (C) 1998 - 2014 by the deal.II authors
+//
+// This file is part of the deal.II library.
+//
+// The deal.II library is free software; you can use it, redistribute
+// it, and/or modify it under the terms of the GNU Lesser General
+// Public License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+// The full text of the license can be found in the file LICENSE at
+// the top level of the deal.II distribution.
+//
+// ---------------------------------------------------------------------
+
+
+// use signals to monitor solutions converging
+
+
+#include "../tests.h"
+#include "testmatrix.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+
+SolverControl::State monitor_norm (const unsigned int iteration,
+ const double check_value,
+ const Vector ¤t_iterate)
+{
+ deallog << " -- " << iteration << ' ' << check_value << std::endl;
+ deallog << " Norm=" << current_iterate.l2_norm() << std::endl;
+ return SolverControl::success;
+}
+
+
+SolverControl::State monitor_mean (const unsigned int iteration,
+ const double check_value,
+ const Vector ¤t_iterate)
+{
+ deallog << " Mean=" << current_iterate.mean_value() << std::endl;
+ return SolverControl::success;
+}
+
+
+
+template
+void
+check_solve( SOLVER &solver, const MATRIX &A,
+ VECTOR &u, VECTOR &f, const PRECONDITION &P)
+{
+ u = 0.;
+ f = 1.;
+ try
+ {
+ solver.solve(A,u,f,P);
+ }
+ catch (dealii::SolverControl::NoConvergence &e)
+ {
+ deallog << "Exception: " << e.get_exc_name() << std::endl;
+ }
+}
+
+int main()
+{
+ std::ofstream logfile("output");
+// logfile.setf(std::ios::fixed);
+ deallog << std::setprecision(4);
+ deallog.attach(logfile);
+ deallog.depth_console(0);
+ deallog.threshold_double(1.e-10);
+
+ GrowingVectorMemory<> mem;
+
+ SolverControl control(100, 1.e-3);
+
+ // create CG and GMRES solvers and attach monitors to it
+ SolverCG<> cg(control, mem);
+ cg.connect (&monitor_norm);
+ cg.connect (&monitor_mean);
+
+ SolverGMRES<> gmres(control, mem, 8);
+ gmres.connect (&monitor_norm);
+ gmres.connect (&monitor_mean);
+
+ for (unsigned int size=4; size <= 30; size *= 3)
+ {
+ unsigned int dim = (size-1)*(size-1);
+
+ deallog << "Size " << size << " Unknowns " << dim << std::endl;
+
+ // Make matrix
+ FDMatrix testproblem(size, size);
+ SparsityPattern structure(dim, dim, 5);
+ testproblem.five_point_structure(structure);
+ structure.compress();
+ SparseMatrix A(structure);
+ testproblem.five_point(A);
+
+ PreconditionSSOR<> prec_ssor;
+ prec_ssor.initialize(A, 1.2);
+
+ Vector f(dim);
+ Vector u(dim);
+ Vector res(dim);
+
+ f = 1.;
+ u = 1.;
+
+ A.residual(res,u,f);
+ A.SOR(res);
+ res.add(1.,u);
+ A.SOR_step(u,f);
+ res.add(-1.,u);
+
+ deallog << "SOR-diff:" << res *res << std::endl;
+
+ try
+ {
+ check_solve(cg,A,u,f,prec_ssor);
+ check_solve(gmres,A,u,f,prec_ssor);
+ }
+ catch (std::exception &e)
+ {
+ std::cerr << "Exception: " << e.what() << std::endl;
+ }
+ }
+}
+
diff --git a/tests/lac/solver_monitor.output b/tests/lac/solver_monitor.output
new file mode 100644
index 0000000000..98090021e0
--- /dev/null
+++ b/tests/lac/solver_monitor.output
@@ -0,0 +1,94 @@
+
+DEAL::Size 4 Unknowns 9
+DEAL::SOR-diff:0
+DEAL:cg::Starting value 3.000
+DEAL:cg:: -- 0 3.000
+DEAL:cg:: Norm=0
+DEAL:cg:: Mean=0
+DEAL:cg:: -- 1 0.7126
+DEAL:cg:: Norm=2.432
+DEAL:cg:: Mean=0.8020
+DEAL:cg:: -- 2 0.06314
+DEAL:cg:: Norm=2.493
+DEAL:cg:: Mean=0.8194
+DEAL:cg:: -- 3 0.001494
+DEAL:cg:: Norm=2.494
+DEAL:cg:: Mean=0.8194
+DEAL:cg::Convergence step 4 value 6.018e-05
+DEAL:cg:: -- 4 6.018e-05
+DEAL:cg:: Norm=2.494
+DEAL:cg:: Mean=0.8194
+DEAL:GMRES::Starting value 1.920
+DEAL:GMRES:: -- 0 1.920
+DEAL:GMRES:: Norm=0
+DEAL:GMRES:: Mean=0
+DEAL:GMRES:: -- 1 0.1979
+DEAL:GMRES:: Norm=0
+DEAL:GMRES:: Mean=0
+DEAL:GMRES:: -- 2 0.009469
+DEAL:GMRES:: Norm=0
+DEAL:GMRES:: Mean=0
+DEAL:GMRES::Convergence step 3 value 0.0003574
+DEAL:GMRES:: -- 3 0.0003574
+DEAL:GMRES:: Norm=0
+DEAL:GMRES:: Mean=0
+DEAL::Size 12 Unknowns 121
+DEAL::SOR-diff:0
+DEAL:cg::Starting value 11.00
+DEAL:cg:: -- 0 11.00
+DEAL:cg:: Norm=0
+DEAL:cg:: Mean=0
+DEAL:cg:: -- 1 12.13
+DEAL:cg:: Norm=50.48
+DEAL:cg:: Mean=4.496
+DEAL:cg:: -- 2 4.992
+DEAL:cg:: Norm=68.00
+DEAL:cg:: Mean=5.737
+DEAL:cg:: -- 3 1.090
+DEAL:cg:: Norm=70.78
+DEAL:cg:: Mean=5.884
+DEAL:cg:: -- 4 0.1907
+DEAL:cg:: Norm=70.88
+DEAL:cg:: Mean=5.889
+DEAL:cg:: -- 5 0.04614
+DEAL:cg:: Norm=70.89
+DEAL:cg:: Mean=5.889
+DEAL:cg:: -- 6 0.007982
+DEAL:cg:: Norm=70.88
+DEAL:cg:: Mean=5.889
+DEAL:cg:: -- 7 0.001234
+DEAL:cg:: Norm=70.88
+DEAL:cg:: Mean=5.889
+DEAL:cg::Convergence step 8 value 0.0005816
+DEAL:cg:: -- 8 0.0005816
+DEAL:cg:: Norm=70.88
+DEAL:cg:: Mean=5.889
+DEAL:GMRES::Starting value 13.27
+DEAL:GMRES:: -- 0 13.27
+DEAL:GMRES:: Norm=0
+DEAL:GMRES:: Mean=0
+DEAL:GMRES:: -- 1 7.177
+DEAL:GMRES:: Norm=0
+DEAL:GMRES:: Mean=0
+DEAL:GMRES:: -- 2 2.514
+DEAL:GMRES:: Norm=0
+DEAL:GMRES:: Mean=0
+DEAL:GMRES:: -- 3 0.4482
+DEAL:GMRES:: Norm=0
+DEAL:GMRES:: Mean=0
+DEAL:GMRES:: -- 4 0.1000
+DEAL:GMRES:: Norm=0
+DEAL:GMRES:: Mean=0
+DEAL:GMRES:: -- 5 0.01999
+DEAL:GMRES:: Norm=0
+DEAL:GMRES:: Mean=0
+DEAL:GMRES:: -- 6 0.002554
+DEAL:GMRES:: Norm=0
+DEAL:GMRES:: Mean=0
+DEAL:GMRES:: -- 6 0.002554
+DEAL:GMRES:: Norm=70.88
+DEAL:GMRES:: Mean=5.889
+DEAL:GMRES::Convergence step 7 value 0.0008206
+DEAL:GMRES:: -- 7 0.0008206
+DEAL:GMRES:: Norm=70.88
+DEAL:GMRES:: Mean=5.889
diff --git a/tests/lac/solver_monitor_disconnect.cc b/tests/lac/solver_monitor_disconnect.cc
new file mode 100644
index 0000000000..eeef090818
--- /dev/null
+++ b/tests/lac/solver_monitor_disconnect.cc
@@ -0,0 +1,140 @@
+// ---------------------------------------------------------------------
+//
+// Copyright (C) 1998 - 2014 by the deal.II authors
+//
+// This file is part of the deal.II library.
+//
+// The deal.II library is free software; you can use it, redistribute
+// it, and/or modify it under the terms of the GNU Lesser General
+// Public License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+// The full text of the license can be found in the file LICENSE at
+// the top level of the deal.II distribution.
+//
+// ---------------------------------------------------------------------
+
+
+// use signals to monitor solutions converging. test that we can
+// disconnect from a signal
+
+
+#include "../tests.h"
+#include "testmatrix.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+
+SolverControl::State monitor_norm (const unsigned int iteration,
+ const double check_value,
+ const Vector ¤t_iterate)
+{
+ deallog << " -- " << iteration << ' ' << check_value << std::endl;
+ deallog << " Norm=" << current_iterate.l2_norm() << std::endl;
+ return SolverControl::success;
+}
+
+
+SolverControl::State monitor_mean (const unsigned int iteration,
+ const double check_value,
+ const Vector ¤t_iterate)
+{
+ deallog << " Mean=" << current_iterate.mean_value() << std::endl;
+ return SolverControl::success;
+}
+
+
+
+template
+void
+check_solve( SOLVER &solver, const MATRIX &A,
+ VECTOR &u, VECTOR &f, const PRECONDITION &P)
+{
+ u = 0.;
+ f = 1.;
+ try
+ {
+ solver.solve(A,u,f,P);
+ }
+ catch (dealii::SolverControl::NoConvergence &e)
+ {
+ deallog << "Exception: " << e.get_exc_name() << std::endl;
+ }
+}
+
+int main()
+{
+ std::ofstream logfile("output");
+// logfile.setf(std::ios::fixed);
+ deallog << std::setprecision(4);
+ deallog.attach(logfile);
+ deallog.depth_console(0);
+ deallog.threshold_double(1.e-10);
+
+ GrowingVectorMemory<> mem;
+
+ SolverControl control(100, 1.e-3);
+
+ // create CG and GMRES solvers and attach monitors to it
+ SolverCG<> cg(control, mem);
+ boost::signals2::connection cg_c1 = cg.connect (&monitor_norm);
+ boost::signals2::connection cg_c2 = cg.connect (&monitor_mean);
+
+ for (unsigned int size=4; size <= 12; size *= 3)
+ {
+ unsigned int dim = (size-1)*(size-1);
+
+ deallog << "Size " << size << " Unknowns " << dim << std::endl;
+
+ // Make matrix
+ FDMatrix testproblem(size, size);
+ SparsityPattern structure(dim, dim, 5);
+ testproblem.five_point_structure(structure);
+ structure.compress();
+ SparseMatrix A(structure);
+ testproblem.five_point(A);
+
+ PreconditionSSOR<> prec_ssor;
+ prec_ssor.initialize(A, 1.2);
+
+ Vector f(dim);
+ Vector u(dim);
+ Vector res(dim);
+
+ f = 1.;
+ u = 1.;
+
+ A.residual(res,u,f);
+ A.SOR(res);
+ res.add(1.,u);
+ A.SOR_step(u,f);
+ res.add(-1.,u);
+
+ deallog << "SOR-diff:" << res *res << std::endl;
+
+ try
+ {
+ check_solve(cg,A,u,f,prec_ssor);
+ }
+ catch (std::exception &e)
+ {
+ std::cerr << "Exception: " << e.what() << std::endl;
+ }
+
+ // disconnect one of the signals
+ cg_c1.disconnect();
+ }
+}
+
diff --git a/tests/lac/solver_monitor_disconnect.output b/tests/lac/solver_monitor_disconnect.output
new file mode 100644
index 0000000000..ba4c75c6c8
--- /dev/null
+++ b/tests/lac/solver_monitor_disconnect.output
@@ -0,0 +1,33 @@
+
+DEAL::Size 4 Unknowns 9
+DEAL::SOR-diff:0
+DEAL:cg::Starting value 3.000
+DEAL:cg:: -- 0 3.000
+DEAL:cg:: Norm=0
+DEAL:cg:: Mean=0
+DEAL:cg:: -- 1 0.7126
+DEAL:cg:: Norm=2.432
+DEAL:cg:: Mean=0.8020
+DEAL:cg:: -- 2 0.06314
+DEAL:cg:: Norm=2.493
+DEAL:cg:: Mean=0.8194
+DEAL:cg:: -- 3 0.001494
+DEAL:cg:: Norm=2.494
+DEAL:cg:: Mean=0.8194
+DEAL:cg::Convergence step 4 value 6.018e-05
+DEAL:cg:: -- 4 6.018e-05
+DEAL:cg:: Norm=2.494
+DEAL:cg:: Mean=0.8194
+DEAL::Size 12 Unknowns 121
+DEAL::SOR-diff:0
+DEAL:cg::Starting value 11.00
+DEAL:cg:: Mean=0
+DEAL:cg:: Mean=4.496
+DEAL:cg:: Mean=5.737
+DEAL:cg:: Mean=5.884
+DEAL:cg:: Mean=5.889
+DEAL:cg:: Mean=5.889
+DEAL:cg:: Mean=5.889
+DEAL:cg:: Mean=5.889
+DEAL:cg::Convergence step 8 value 0.0005816
+DEAL:cg:: Mean=5.889