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 |