GCC Code Coverage Report


Directory: ../../../builds/dumux-repositories/
File: dumux/dumux/linear/istlsolverfactorybackend.hh
Date: 2025-04-12 19:19:20
Exec Total Coverage
Lines: 85 94 90.4%
Functions: 144 183 78.7%
Branches: 74 241 30.7%

Line Branch Exec Source
1 // -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 // vi: set et ts=4 sw=4 sts=4:
3 //
4 // SPDX-FileCopyrightText: Copyright © DuMux Project contributors, see AUTHORS.md in root folder
5 // SPDX-License-Identifier: GPL-3.0-or-later
6 //
7 /*!
8 * \file
9 * \ingroup Linear
10 * \brief Provides a generic linear solver based on the ISTL that chooses the
11 * solver and preconditioner at runtime
12 */
13
14 #ifndef DUMUX_LINEAR_ISTL_SOLVERFACTORYBACKEND_HH
15 #define DUMUX_LINEAR_ISTL_SOLVERFACTORYBACKEND_HH
16
17 #include <memory>
18
19 #include <dune/common/version.hh>
20 #include <dune/common/parallel/mpihelper.hh>
21 #include <dune/common/parametertree.hh>
22
23 #include "linearsolverparameters.hh"
24
25 // preconditioners
26 #include <dune/istl/preconditioners.hh>
27 #include <dune/istl/paamg/amg.hh>
28 #include "preconditioners.hh"
29
30 // solvers
31 #include <dune/istl/solvers.hh>
32 #include <dune/istl/solverfactory.hh>
33
34 #include <dumux/common/typetraits/matrix.hh>
35 #include <dumux/common/typetraits/vector.hh>
36 #include <dumux/linear/solver.hh>
37 #include <dumux/linear/parallelhelpers.hh>
38 #include <dumux/linear/istlsolverregistry.hh>
39 #include <dumux/linear/solvercategory.hh>
40 #include <dumux/linear/istlsolversmultitype.hh>
41
42 namespace Dumux {
43
44 // initSolverFactoriesForMultiTypeBlockMatrix differs in different compilation units,
45 // so we have it in an anonymous namespace
46 namespace {
47
48 /*!
49 * \brief Initializes the direct solvers, preconditioners and iterative solvers in
50 * the factories with the corresponding Matrix and Vector types.
51 * \note We currently consider only direct solvers and preconditioners provided by
52 * Dumux which, unlike the ones implemented in Dune, also support MultiTypeBlockMatrices.
53 * \note This function could be removed once Dune::initSolverFactories supports MultiTypeBlockMatrices.
54 * \tparam LinearOperator the linear operator type
55 */
56 template<class LinearOperator>
57 1 int initSolverFactoriesForMultiTypeBlockMatrix()
58 {
59 #if DUNE_VERSION_LT(DUNE_ISTL,2,11)
60 using M = typename LinearOperator::matrix_type;
61 using X = typename LinearOperator::range_type;
62 using Y = typename LinearOperator::domain_type;
63 using TL = Dune::TypeList<M,X,Y>;
64 1 auto& dsfac = Dune::DirectSolverFactory<M,X,Y>::instance();
65 Dune::addRegistryToFactory<TL>(dsfac, Dumux::MultiTypeBlockMatrixDirectSolverTag{});
66 1 auto& pfac = Dune::PreconditionerFactory<LinearOperator,X,Y>::instance();
67 1 Dune::addRegistryToFactory<TL>(pfac, Dumux::MultiTypeBlockMatrixPreconditionerTag{});
68 using TLS = Dune::TypeList<X,Y>;
69 1 auto& isfac = Dune::IterativeSolverFactory<X,Y>::instance();
70 1 return Dune::addRegistryToFactory<TLS>(isfac, Dune::IterativeSolverTag{});
71 #else
72 using OpTraits = Dune::OperatorTraits<LinearOperator>;
73 auto& pfac = Dune::PreconditionerFactory<LinearOperator>::instance();
74 Dune::addRegistryToFactory<OpTraits>(pfac, Dumux::MultiTypeBlockMatrixPreconditionerTag{});
75 auto& sfac = Dune::SolverFactory<LinearOperator>::instance();
76 return Dune::addRegistryToFactory<OpTraits>(sfac, Dumux::MultiTypeBlockMatrixSolverTag{});
77 #endif
78 }
79 } // end namespace
80
81 /*!
82 * \brief Initialize the solver factories for regular matrices or MultiTypeBlockMatrices
83 * \tparam Matrix the matrix
84 * \tparam LinearOperator the linear operator
85 *
86 * \note This function could be removed once Dune::initSolverFactories supports MultiTypeBlockMatrices.
87 */
88 template<class Matrix, class LinearOperator>
89 45 void initSolverFactories()
90 {
91 if constexpr (isMultiTypeBlockMatrix<Matrix>::value)
92
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 initSolverFactoriesForMultiTypeBlockMatrix<LinearOperator>();
93 else
94
1/2
✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
44 Dune::initSolverFactories<LinearOperator>();
95 45 }
96
97 /*!
98 * \ingroup Linear
99 * \brief A linear solver using the dune-istl solver factory
100 * to choose the solver and preconditioner at runtime.
101 * \note the solvers are configured via the input file
102 */
103 template <class LinearSolverTraits, class LinearAlgebraTraits>
104 class IstlSolverFactoryBackend : public LinearSolver
105 {
106 using Matrix = typename LinearAlgebraTraits::Matrix;
107 using Vector = typename LinearAlgebraTraits::Vector;
108 using ScalarProduct = Dune::ScalarProduct<Vector>;
109 #if HAVE_MPI
110 using Comm = Dune::OwnerOverlapCopyCommunication<Dune::bigunsignedint<96>, int>;
111 #endif
112 public:
113
114 /*!
115 * \brief Construct the backend for the sequential case only
116 *
117 * \param paramGroup the parameter group for parameter lookup
118 */
119 3 IstlSolverFactoryBackend(const std::string& paramGroup = "")
120 : paramGroup_(paramGroup)
121
5/10
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 3 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 3 times.
✗ Branch 9 not taken.
✓ Branch 11 taken 3 times.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✓ Branch 14 taken 3 times.
9 , isParallel_(Dune::MPIHelper::getCommunication().size() > 1)
122 {
123
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (isParallel_)
124 DUNE_THROW(Dune::InvalidStateException, "Using sequential constructor for parallel run. Use signature with gridView and dofMapper!");
125
126 3 firstCall_ = true;
127
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 initializeParameters_();
128
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 scalarProduct_ = std::make_shared<ScalarProduct>();
129 3 solverCategory_ = Dune::SolverCategory::sequential;
130 3 }
131
132 /*!
133 * \brief Construct the backend for parallel or sequential runs
134 *
135 * \param gridView the grid view for parallel communication via the grid
136 * \param dofMapper an index mapper for dof entities
137 * \param paramGroup the parameter group for parameter lookup
138 */
139 42 IstlSolverFactoryBackend(const typename LinearSolverTraits::GridView& gridView,
140 const typename LinearSolverTraits::DofMapper& dofMapper,
141 const std::string& paramGroup = "")
142 : paramGroup_(paramGroup)
143 #if HAVE_MPI
144
7/12
✓ Branch 2 taken 42 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 42 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 26 times.
✓ Branch 10 taken 26 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 26 times.
✗ Branch 14 not taken.
✓ Branch 9 taken 16 times.
✓ Branch 12 taken 16 times.
126 , isParallel_(gridView.comm().size() > 1)
145 #endif
146 {
147 42 firstCall_ = true;
148
1/2
✓ Branch 1 taken 42 times.
✗ Branch 2 not taken.
42 initializeParameters_();
149 #if HAVE_MPI
150
1/2
✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
42 solverCategory_ = Detail::solverCategory<LinearSolverTraits>(gridView);
151
2/2
✓ Branch 0 taken 30 times.
✓ Branch 1 taken 12 times.
42 if (solverCategory_ != Dune::SolverCategory::sequential)
152 {
153
3/6
✓ Branch 1 taken 30 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 30 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 18 times.
30 parallelHelper_ = std::make_unique<ParallelISTLHelper<LinearSolverTraits>>(gridView, dofMapper);
154
7/10
✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
✓ Branch 3 taken 18 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 12 times.
✓ Branch 6 taken 18 times.
✓ Branch 8 taken 18 times.
✗ Branch 9 not taken.
✓ Branch 2 taken 12 times.
✓ Branch 7 taken 12 times.
30 comm_ = std::make_shared<Comm>(gridView.comm(), solverCategory_);
155
3/6
✓ Branch 1 taken 30 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 30 times.
✓ Branch 6 taken 30 times.
✗ Branch 7 not taken.
30 scalarProduct_ = Dune::createScalarProduct<Vector>(*comm_, solverCategory_);
156
1/2
✓ Branch 1 taken 30 times.
✗ Branch 2 not taken.
30 parallelHelper_->createParallelIndexSet(*comm_);
157 }
158 else
159
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 scalarProduct_ = std::make_shared<ScalarProduct>();
160 #else
161 solverCategory_ = Dune::SolverCategory::sequential;
162 scalarProduct_ = std::make_shared<ScalarProduct>();
163 #endif
164 42 }
165
166 /*!
167 * \brief Solve a linear system.
168 *
169 * \param A the matrix
170 * \param x the seeked solution vector, containing the initial solution upon entry
171 * \param b the right hand side vector
172 */
173 1062 bool solve(Matrix& A, Vector& x, Vector& b)
174 {
175 #if HAVE_MPI
176
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
1062 solveSequentialOrParallel_(A, x, b);
177 #else
178 solveSequential_(A, x, b);
179 #endif
180
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
1062 firstCall_ = false;
181 1060 return result_.converged;
182 }
183
184 const Dune::InverseOperatorResult& result() const
185 {
186 return result_;
187 }
188
189 const std::string& name() const
190 {
191 return name_;
192 }
193
194 254 Scalar norm(const Vector& x) const
195 {
196 #if HAVE_MPI
197 if constexpr (LinearSolverTraits::canCommunicate && !isMultiTypeBlockVector<Vector>::value)
198 {
199
2/2
✓ Branch 0 taken 50 times.
✓ Branch 1 taken 136 times.
186 if (solverCategory_ == Dune::SolverCategory::nonoverlapping)
200 {
201 50 auto y(x); // make a copy because the vector needs to be made consistent
202 using GV = typename LinearSolverTraits::GridView;
203 using DM = typename LinearSolverTraits::DofMapper;
204
1/2
✓ Branch 1 taken 50 times.
✗ Branch 2 not taken.
50 ParallelVectorHelper<GV, DM, LinearSolverTraits::dofCodim> vectorHelper(parallelHelper_->gridView(), parallelHelper_->dofMapper());
205
1/2
✓ Branch 1 taken 50 times.
✗ Branch 2 not taken.
50 vectorHelper.makeNonOverlappingConsistent(y);
206
2/4
✓ Branch 1 taken 50 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 50 times.
✗ Branch 4 not taken.
50 return scalarProduct_->norm(y);
207 50 }
208 }
209 #endif
210
1/2
✓ Branch 2 taken 17 times.
✗ Branch 3 not taken.
204 return scalarProduct_->norm(x);
211 }
212
213 private:
214
215 45 void initializeParameters_()
216 {
217 45 params_ = Dumux::LinearSolverParameters<LinearSolverTraits>::createParameterTree(paramGroup_);
218 45 checkMandatoryParameters_();
219
4/8
✓ Branch 2 taken 45 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 45 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 45 times.
✗ Branch 9 not taken.
✓ Branch 11 taken 45 times.
✗ Branch 12 not taken.
90 name_ = params_.get<std::string>("preconditioner.type") + "-preconditioned " + params_.get<std::string>("type");
220
3/4
✓ Branch 2 taken 45 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 3 times.
✓ Branch 6 taken 42 times.
45 if (params_.get<int>("verbose", 0) > 0)
221 3 std::cout << "Initialized linear solver of type: " << name_ << std::endl;
222 45 }
223
224 45 void checkMandatoryParameters_()
225 {
226
2/4
✓ Branch 2 taken 45 times.
✗ Branch 3 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 45 times.
45 if (!params_.hasKey("type"))
227 DUNE_THROW(Dune::InvalidStateException, "Solver factory needs \"LinearSolver.Type\" parameter to select the solver");
228
229
2/4
✓ Branch 2 taken 45 times.
✗ Branch 3 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 45 times.
45 if (!params_.hasKey("preconditioner.type"))
230 DUNE_THROW(Dune::InvalidStateException, "Solver factory needs \"LinearSolver.Preconditioner.Type\" parameter to select the preconditioner");
231 45 }
232
233 #if HAVE_MPI
234 1062 void solveSequentialOrParallel_(Matrix& A, Vector& x, Vector& b)
235 {
236 // Dune::MultiTypeBlockMatrix does not provide a ColIterator which is needed by Dune::NonoverlappingSchwarzOperator.
237 // We therefore can only solve these types of systems sequentially.
238 // TODO: This can be adapted once the situation in Dune ISTL changes.
239 if constexpr (LinearSolverTraits::canCommunicate && !isMultiTypeBlockMatrix<Matrix>::value)
240 {
241
2/2
✓ Branch 0 taken 724 times.
✓ Branch 1 taken 287 times.
1011 if (isParallel_)
242 {
243
2/3
✓ Branch 1 taken 196 times.
✓ Branch 2 taken 98 times.
✗ Branch 0 not taken.
724 if (LinearSolverTraits::isNonOverlapping(parallelHelper_->gridView()))
244 {
245 using PTraits = typename LinearSolverTraits::template ParallelNonoverlapping<Matrix, Vector>;
246 286 solveParallel_<PTraits>(A, x, b);
247 }
248 else
249 {
250 using PTraits = typename LinearSolverTraits::template ParallelOverlapping<Matrix, Vector>;
251 438 solveParallel_<PTraits>(A, x, b);
252 }
253 }
254 else
255 287 solveSequential_(A, x, b);
256 }
257 else
258 {
259 51 solveSequential_(A, x, b);
260 }
261 1011 }
262
263 template<class ParallelTraits>
264 912 void solveParallel_(Matrix& A, Vector& x, Vector& b)
265 {
266 using LinearOperator = typename ParallelTraits::LinearOperator;
267
268
2/2
✓ Branch 0 taken 30 times.
✓ Branch 1 taken 694 times.
912 if (firstCall_)
269 initSolverFactories<Matrix, LinearOperator>();
270
271 912 prepareLinearAlgebraParallel<LinearSolverTraits, ParallelTraits>(A, b, *parallelHelper_);
272 912 auto linearOperator = std::make_shared<LinearOperator>(A, *comm_);
273
274 // solve linear system
275
1/2
✓ Branch 1 taken 724 times.
✗ Branch 2 not taken.
912 apply_(linearOperator, x, b);
276 912 }
277 #endif // HAVE_MPI
278
279 338 void solveSequential_(Matrix& A, Vector& x, Vector& b)
280 {
281 // construct linear operator
282 using Traits = typename LinearSolverTraits::template Sequential<Matrix, Vector>;
283 using LinearOperator = typename Traits::LinearOperator;
284 338 auto linearOperator = std::make_shared<LinearOperator>(A);
285
286
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 323 times.
338 if (firstCall_)
287 initSolverFactories<Matrix, LinearOperator>();
288
289 // solve linear system
290
1/2
✓ Branch 1 taken 338 times.
✗ Branch 2 not taken.
338 apply_(linearOperator, x, b);
291 338 }
292
293 template<class LinearOperator>
294 2073 auto getSolverFromFactory_(std::shared_ptr<LinearOperator>& fop)
295 {
296 try {
297
1/4
✓ Branch 2 taken 1062 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
4146 return Dune::getSolverFromFactory(fop, params_);
298 } catch(Dune::Exception& e) {
299 std::cerr << "Dune::Exception during solver construction: " << e.what() << std::endl;
300 return std::decay_t<decltype(Dune::getSolverFromFactory(fop, params_))>();
301 }
302 }
303
304 template<class LinearOperator>
305 2073 void apply_(std::shared_ptr<LinearOperator>& fop, Vector& x, Vector& b)
306 {
307 2073 auto solver = getSolverFromFactory_(fop);
308
309 // make solver constructor failure recoverable by throwing an exception
310 // on all processes if one or more processes fail
311 2073 bool success = static_cast<bool>(solver);
312 #if HAVE_MPI
313 2073 int successRemote = success;
314
2/2
✓ Branch 0 taken 724 times.
✓ Branch 1 taken 338 times.
2073 if (isParallel_)
315
1/2
✓ Branch 1 taken 724 times.
✗ Branch 2 not taken.
1448 successRemote = comm_->communicator().min(success);
316
317
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1062 times.
2073 if (!success)
318 DUNE_THROW(Dune::Exception, "Could not create ISTL solver");
319
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1062 times.
2073 else if (!successRemote)
320
1/24
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✗ Branch 31 not taken.
✗ Branch 32 not taken.
✓ Branch 37 taken 1062 times.
✗ Branch 38 not taken.
2073 DUNE_THROW(Dune::Exception, "Could not create ISTL solver on remote process");
321 #else
322 if (!success)
323 DUNE_THROW(Dune::Exception, "Could not create ISTL solver");
324 #endif
325
326 // solve linear system (here we assume that either all processes are successful or all fail)
327
1/2
✓ Branch 1 taken 1062 times.
✗ Branch 2 not taken.
2073 solver->apply(x, b, result_);
328 2073 }
329
330 const std::string paramGroup_;
331 #if HAVE_MPI
332 std::unique_ptr<ParallelISTLHelper<LinearSolverTraits>> parallelHelper_;
333 std::shared_ptr<Comm> comm_;
334 #endif
335 bool isParallel_ = false;
336 bool firstCall_;
337
338 std::shared_ptr<ScalarProduct> scalarProduct_;
339 Dune::SolverCategory::Category solverCategory_;
340 Dune::InverseOperatorResult result_;
341 Dune::ParameterTree params_;
342 std::string name_;
343 };
344
345 } // end namespace Dumux
346
347 #endif
348