version 3.10-dev
stokes_solver.hh
Go to the documentation of this file.
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-FileCopyrightInfo: Copyright © DuMux Project contributors, see AUTHORS.md in root folder
5// SPDX-License-Identifier: GPL-3.0-or-later
6//
12#ifndef DUMUX_LINEAR_STOKES_SOLVER_HH
13#define DUMUX_LINEAR_STOKES_SOLVER_HH
14
15#include <type_traits>
16#include <memory>
17#include <tuple>
18
19#include <dune/common/parametertree.hh>
20#include <dune/common/hybridutilities.hh>
21#include <dune/common/exceptions.hh>
22
23#include <dune/istl/matrixindexset.hh>
24#include <dune/istl/preconditioner.hh>
25#include <dune/istl/preconditioners.hh>
26#include <dune/istl/solvers.hh>
27#include <dune/istl/paamg/amg.hh>
28
29#include <dumux/common/math.hh>
40
41namespace Dumux::Detail {
42
60template<class M, class X, class Y, int l = 2>
61class StokesPreconditioner : public Dune::Preconditioner<X,Y>
62{
63 static_assert(Dumux::isMultiTypeBlockMatrix<M>::value && M::M() == 2 && M::N() == 2, "Expects a 2x2 MultiTypeBlockMatrix.");
64 static_assert(l == 2, "StokesPreconditioner expects a block level of 2 for Matrix type M.");
65
66 using A = std::decay_t<decltype(std::declval<M>()[Dune::Indices::_0][Dune::Indices::_0])>;
67 using U = std::decay_t<decltype(std::declval<X>()[Dune::Indices::_0])>;
68
69 using P = std::decay_t<decltype(std::declval<M>()[Dune::Indices::_1][Dune::Indices::_1])>;
70 using V = std::decay_t<decltype(std::declval<X>()[Dune::Indices::_1])>;
71
72 enum class Mode { symmetric, triangular, diagonal };
73
74public:
76 using matrix_type = M;
78 using domain_type = X;
80 using range_type = Y;
82 using field_type = typename X::field_type;
84 using scalar_field_type = Dune::Simd::Scalar<field_type>;
86 using PressureLinearOperator = Dune::MatrixAdapter<P,V,V>;
87
95 const std::shared_ptr<const Dune::AssembledLinearOperator<M,X,Y>>& fullLinearOperator,
96 const std::shared_ptr<const PressureLinearOperator>& pressureLinearOperator,
97 const Dune::ParameterTree& params
98 )
99 : matrix_(fullLinearOperator->getmat())
100 , pmatrix_(pressureLinearOperator->getmat())
101 , verbosity_(params.get<int>("verbosity"))
102 , paramGroup_(params.get<std::string>("ParameterGroup"))
103 {
104 const auto mode = getParamFromGroup<std::string>(
105 paramGroup_, "LinearSolver.Preconditioner.Mode", "Diagonal"
106 );
107
108 if (mode == "Symmetric")
109 mode_ = Mode::symmetric;
110 else if (mode == "Triangular")
111 mode_ = Mode::triangular;
112 else
113 mode_ = Mode::diagonal;
114
115 initPreconditioner_(params);
116 }
117
121 void pre(X& update, Y& currentDefect) override {}
122
134 void apply(X& update, const Y& currentDefect) override
135 {
136 using namespace Dune::Indices;
137
138 if (mode_ == Mode::symmetric)
139 {
140 update = applyRightBlock_(currentDefect);
141 update = applyDiagBlock_(update);
142 update = applyLeftBlock_(update);
143 }
144 else if (mode_ == Mode::triangular)
145 {
146 update = applyRightBlock_(currentDefect);
147 update = applyDiagBlock_(update);
148 }
149 else
150 update = applyDiagBlock_(currentDefect);
151 }
152
156 void post(X& update) override {}
157
159 Dune::SolverCategory::Category category() const override
160 {
161 return Dune::SolverCategory::sequential;
162 }
163
164private:
165 X applyRightBlock_(const Y& d)
166 {
167 using namespace Dune::Indices;
168
169 // right bit of LDU decomposition
170 // applied to d
171 //
172 // | I 0 |
173 // | -C*inv(A) I |
174 //
175
176 auto dTmp0 = d[_0];
177 auto vTmp = d; vTmp = 0.0;
178
179 // invert velocity block (apply to first block of d)
180 applyPreconditionerForA_(vTmp[_0], dTmp0);
181
182 // then multiply with C
183 matrix_[_1][_0].mv(vTmp[_0], vTmp[_1]);
184
185 // and subtract from d
186 auto v = d;
187 v[_0] = vTmp[_0]; // already do A^-1 d of the diagonal block because we already computed it here
188 v[_1] -= vTmp[_1];
189 return v;
190 }
191
192 X applyDiagBlock_(const Y& d)
193 {
194 using namespace Dune::Indices;
195
196 // diagonal middle bit of LDU decomposition
197 auto dTmp = d;
198 auto v = d; v = 0.0;
199
200 // invert velocity block
201 if (mode_ == Mode::diagonal)
202 applyPreconditionerForA_(v[_0], dTmp[_0]);
203
204 // reuse the already computed A^-1 d (see applyRightBlock_)
205 else
206 v[_0] = dTmp[_0];
207
208 // invert pressure block
209 applyPreconditionerForP_(v[_1], dTmp[_1]);
210
211 return v;
212 }
213
214 X applyLeftBlock_(const Y& d)
215 {
216 using namespace Dune::Indices;
217
218 // left bit of LDU decomposition
219 // applied to d
220 //
221 // | I -inv(A)*B |
222 // | 0 I |
223 //
224
225 auto dTmp = d;
226 auto vTmp = d; vTmp = 0.0;
227
228 // multiply with B
229 matrix_[_0][_1].umv(dTmp[_1], vTmp[_0]);
230
231 // invert velocity block (apply to first block of d)
232 auto vTmp0 = vTmp[_0]; vTmp0 = 0.0;
233 applyPreconditionerForA_(vTmp0, vTmp[_0]);
234
235 // and subtract from d
236 auto v = d;
237 v[_0] -= vTmp0;
238
239 return v;
240 }
241
242 void initPreconditioner_(const Dune::ParameterTree& params)
243 {
244 using namespace Dune::Indices;
245
246 if (getParamFromGroup<bool>(paramGroup_, "LinearSolver.DirectSolverForVelocity", false))
247 {
248#if HAVE_UMFPACK
249 directSolverForA_ = std::make_shared<Dune::UMFPack<A>>(matrix_[_0][_0], verbosity_);
250 using Wrap = Dune::InverseOperator2Preconditioner<Dune::InverseOperator<U, U>>;
251 preconditionerForA_ = std::make_shared<Wrap>(*directSolverForA_);
252#else
253 DUNE_THROW(Dune::InvalidStateException, "Selected direct solver but UMFPack is not available.");
254#endif
255 }
256 else
257 {
258 using VelLinearOperator = Dune::MatrixAdapter<A, U, U>;
259 auto lopV = std::make_shared<VelLinearOperator>(matrix_[_0][_0]);
260 preconditionerForA_ = std::make_shared<
261 Dune::Amg::AMG<VelLinearOperator, U, Dumux::ParMTSSOR<A,U,U>>
262 >(lopV, params);
263 }
264
265 if (getParamFromGroup<bool>(paramGroup_, "LinearSolver.DirectSolverForPressure", false))
266 {
267#if HAVE_UMFPACK
268 directSolverForP_ = std::make_shared<Dune::UMFPack<P>>(pmatrix_, verbosity_);
269 using Wrap = Dune::InverseOperator2Preconditioner<Dune::InverseOperator<V, V>>;
270 preconditionerForP_ = std::make_shared<Wrap>(*directSolverForP_);
271#else
272 DUNE_THROW(Dune::InvalidStateException, "Selected direct solver but UMFPack is not available.");
273#endif
274 }
275 else
276 {
277 using PressJacobi = Dumux::ParMTJac<P, V, V>;
278 std::size_t numIterations = pmatrix_.nonzeroes() == pmatrix_.N() ? 1 : 10;
279 preconditionerForP_ = std::make_shared<PressJacobi>(pmatrix_, numIterations, 1.0);
280 }
281 }
282
283 template<class Sol, class Rhs>
284 void applyPreconditionerForA_(Sol& sol, Rhs& rhs) const
285 {
286 preconditionerForA_->pre(sol, rhs);
287 preconditionerForA_->apply(sol, rhs);
288 preconditionerForA_->post(sol);
289 }
290
291 template<class Sol, class Rhs>
292 void applyPreconditionerForP_(Sol& sol, Rhs& rhs) const
293 {
294 preconditionerForP_->pre(sol, rhs);
295 preconditionerForP_->apply(sol, rhs);
296 preconditionerForP_->post(sol);
297 }
298
300 const M& matrix_;
302 const P& pmatrix_;
304 const int verbosity_;
305
306 std::shared_ptr<Dune::Preconditioner<U, U>> preconditionerForA_;
307 std::shared_ptr<Dune::Preconditioner<V, V>> preconditionerForP_;
308 std::shared_ptr<Dune::InverseOperator<U, U>> directSolverForA_;
309 std::shared_ptr<Dune::InverseOperator<V, V>> directSolverForP_;
310
311 const std::string paramGroup_;
312 Mode mode_;
313};
314
315} // end namespace Detail
316
317namespace Dumux {
318
325template<class Matrix, class Vector, class VelocityGG, class PressureGG>
327: public LinearSolver
328{
330public:
339 StokesSolver(std::shared_ptr<const VelocityGG> vGridGeometry,
340 std::shared_ptr<const PressureGG> pGridGeometry,
341 const Vector& dirichletDofs,
342 const std::string& paramGroup = "")
344 , vGridGeometry_(std::move(vGridGeometry))
345 , pGridGeometry_(std::move(pGridGeometry))
346 , dirichletDofs_(dirichletDofs)
347 {
348 params_ = LinearSolverParameters<LinearSolverTraits<VelocityGG>>::createParameterTree(this->paramGroup());
349 density_ = getParamFromGroup<double>(this->paramGroup(), "Component.LiquidDensity");
350
351 if (hasParamInGroup(this->paramGroup(), "Component.LiquidDynamicViscosity"))
352 viscosity_ = getParamFromGroup<double>(this->paramGroup(), "Component.LiquidDynamicViscosity");
353 else if (hasParamInGroup(this->paramGroup(), "Component.LiquidKinematicViscosity"))
354 viscosity_ = getParamFromGroup<double>(this->paramGroup(), "Component.LiquidKinematicViscosity") * density_;
355 else
356 {
357 const std::string group = this->paramGroup() == "" ? "Component" : this->paramGroup() + ".Component";
358 DUNE_THROW(ParameterException, "Stokes solver requires the parameters"
359 << " LiquidDynamicViscosity or LiquidKinematicViscosity"
360 << " in parameter group [" << group << "]");
361 }
362
363 weight_ = getParamFromGroup<double>(this->paramGroup(), "LinearSolver.Preconditioner.MassMatrixWeight", 1.0);
364 solverType_ = getParamFromGroup<std::string>(this->paramGroup(), "LinearSolver.Type", "gmres");
365 scalarProduct_ = std::make_shared<Dune::ScalarProduct<Vector>>();
366 }
367
368 bool solve(const Matrix& A, Vector& x, const Vector& b)
369 {
370 auto bTmp = b;
371 auto ATmp = A;
372
373 return applyIterativeSolver_(ATmp, x, bTmp);
374 }
375
376 Scalar norm(const Vector& b) const
377 {
378 return scalarProduct_->norm(b);
379 }
380
381 std::string name() const
382 {
383 return "Block-preconditioned Stokes solver";
384 }
385
386 const Dune::InverseOperatorResult& result() const
387 {
388 return result_;
389 }
390
391private:
392 bool applyIterativeSolver_(Matrix& A, Vector& x, Vector& b)
393 {
394 // make Dirichlet boundary conditions symmetric
395 if (getParamFromGroup<bool>(this->paramGroup(), "LinearSolver.SymmetrizeDirichlet", true))
396 symmetrizeConstraints(A, b, dirichletDofs_);
397
398 // make Matrix symmetric on the block-scale
399 using namespace Dune::Indices;
400 A[_1] *= -1.0/density_;
401 b[_1] *= -1.0/density_;
402
403 auto op = std::make_shared<Dumux::ParallelMultiTypeMatrixAdapter<Matrix, Vector, Vector>>(A);
404 auto pop = makePressureLinearOperator_<typename Preconditioner::PressureLinearOperator>();
405 auto preconditioner = std::make_shared<Preconditioner>(op, pop, params_.sub("preconditioner"));
406 params_["verbose"] = pGridGeometry_->gridView().comm().rank() == 0 ? params_["verbose"] : "0";
407
408 // defaults to restarted GMRes
409 std::unique_ptr<Dune::InverseOperator<Vector, Vector>> solver;
410 if (solverType_ == "minres")
411 solver = std::make_unique<Dune::MINRESSolver<Vector>>(op, scalarProduct_, preconditioner, params_);
412 else if (solverType_ == "bicgstab")
413 solver = std::make_unique<Dune::BiCGSTABSolver<Vector>>(op, scalarProduct_, preconditioner, params_);
414 else if (solverType_ == "gmres")
415 solver = std::make_unique<Dune::RestartedGMResSolver<Vector>>(op, scalarProduct_, preconditioner, params_);
416 else
417 DUNE_THROW(Dune::NotImplemented, "Solver choice " << solverType_ << " is not implemented");
418
419 solver->apply(x, b, result_);
420
421 return result_.converged;
422 }
423
424 template<class LinearOperator>
425 std::shared_ptr<LinearOperator> makePressureLinearOperator_()
426 {
427 using M = typename LinearOperator::matrix_type;
428 auto massMatrix = createMassMatrix_<M>();
429 return std::make_shared<LinearOperator>(massMatrix);
430 }
431
432 template<class M, class DM = typename PressureGG::DiscretizationMethod>
433 std::shared_ptr<M> createMassMatrix_()
434 {
435 auto massMatrix = std::make_shared<M>();
436 massMatrix->setBuildMode(M::random);
437 const auto numDofs = pGridGeometry_->numDofs();
438
439 if constexpr (DM{} == DiscretizationMethods::cctpfa || DM{} == DiscretizationMethods::ccmpfa)
440 {
441 Dune::MatrixIndexSet pattern;
442 pattern.resize(numDofs, numDofs);
443 for (unsigned int globalI = 0; globalI < numDofs; ++globalI)
444 pattern.add(globalI, globalI);
445 pattern.exportIdx(*massMatrix);
446
447 const auto& gv = pGridGeometry_->gridView();
448 auto fvGeometry = localView(*pGridGeometry_);
449 for (const auto& element : elements(gv))
450 {
451 fvGeometry.bindElement(element);
452 for (const auto& scv : scvs(fvGeometry))
453 {
454 using Extrusion = Extrusion_t<PressureGG>;
455 const auto dofIndex = scv.dofIndex();
456 if (element.partitionType() == Dune::GhostEntity) // do not modify ghosts
457 (*massMatrix)[dofIndex][dofIndex] = 1.0;
458 else
459 (*massMatrix)[dofIndex][dofIndex] += weight_*Extrusion::volume(fvGeometry, scv)/(2.0*viscosity_);
460 }
461 }
462
463 return massMatrix;
464 }
465 else if constexpr (DM{} == DiscretizationMethods::box || DM{} == DiscretizationMethods::fcdiamond)
466 {
467 getJacobianPattern<true>(*pGridGeometry_).exportIdx(*massMatrix);
468 (*massMatrix) = 0.0;
469
470 const auto& gv = pGridGeometry_->gridView();
471 auto fvGeometry = localView(*pGridGeometry_);
472 std::vector<Dune::FieldVector<double, 1>> values;
473
474 for (const auto& element : elements(gv))
475 {
476 const auto geometry = element.geometry();
477 const auto& localBasis = pGridGeometry_->feCache().get(element.type()).localBasis();
478 const auto numLocalDofs = localBasis.size();
479 values.resize(numLocalDofs);
480
481 fvGeometry.bindElement(element);
482 for (const auto& scvJ : scvs(fvGeometry))
483 {
484 // Use mid-point rule (only works for linear ansatz functions)
485 const auto globalJ = scvJ.dofIndex();
486 const auto qWeightJ = PressureGG::Extrusion::volume(fvGeometry, scvJ);
487 const auto quadPos = geometry.local(scvJ.center());
488 localBasis.evaluateFunction(quadPos, values);
489
490 for (const auto& scvI : scvs(fvGeometry))
491 {
492 const auto valueIJ = values[scvI.localDofIndex()]*qWeightJ/(2.0*viscosity_);
493 (*massMatrix)[scvI.dofIndex()][globalJ][0][0] += valueIJ;
494 }
495 }
496 }
497
498 return massMatrix;
499 }
500
501 DUNE_THROW(Dune::NotImplemented, "Mass matrix for discretization method not implemented");
502 }
503
504 double density_, viscosity_, weight_;
505 Dune::InverseOperatorResult result_;
506 Dune::ParameterTree params_;
507 std::shared_ptr<const VelocityGG> vGridGeometry_;
508 std::shared_ptr<const PressureGG> pGridGeometry_;
509 const Vector& dirichletDofs_;
510 std::string solverType_;
511
512 std::shared_ptr<Dune::ScalarProduct<Vector>> scalarProduct_;
513};
514
515} // end namespace Dumux
516
517#endif
A Stokes preconditioner (saddle-point problem) for the problem .
Definition: stokes_solver.hh:62
Dune::SolverCategory::Category category() const override
Category of the preconditioner (see SolverCategory::Category)
Definition: stokes_solver.hh:159
X domain_type
The domain type of the preconditioner.
Definition: stokes_solver.hh:78
void post(X &update) override
Clean up.
Definition: stokes_solver.hh:156
typename X::field_type field_type
The field type of the preconditioner.
Definition: stokes_solver.hh:82
Dune::Simd::Scalar< field_type > scalar_field_type
Scalar type underlying the field_type.
Definition: stokes_solver.hh:84
M matrix_type
The matrix type the preconditioner is for.
Definition: stokes_solver.hh:76
Dune::MatrixAdapter< P, V, V > PressureLinearOperator
the type of the pressure operator
Definition: stokes_solver.hh:86
Y range_type
The range type of the preconditioner.
Definition: stokes_solver.hh:80
void pre(X &update, Y &currentDefect) override
Prepare the preconditioner.
Definition: stokes_solver.hh:121
StokesPreconditioner(const std::shared_ptr< const Dune::AssembledLinearOperator< M, X, Y > > &fullLinearOperator, const std::shared_ptr< const PressureLinearOperator > &pressureLinearOperator, const Dune::ParameterTree &params)
Constructor.
Definition: stokes_solver.hh:94
void apply(X &update, const Y &currentDefect) override
Apply the preconditioner.
Definition: stokes_solver.hh:134
Base class for linear solvers.
Definition: solver.hh:27
const std::string & paramGroup() const
the parameter group for getting parameter from the parameter tree
Definition: solver.hh:78
double Scalar
Definition: solver.hh:31
Generates a parameter tree required for the linear solvers and precondioners of the Dune ISTL.
Definition: linearsolverparameters.hh:40
Multi-threaded Jacobi preconditioner.
Definition: preconditioners.hh:331
Exception thrown if a run-time parameter is not specified correctly.
Definition: exceptions.hh:48
Preconditioned iterative solver for the incompressible Stokes problem.
Definition: stokes_solver.hh:328
const Dune::InverseOperatorResult & result() const
Definition: stokes_solver.hh:386
bool solve(const Matrix &A, Vector &x, const Vector &b)
Definition: stokes_solver.hh:368
StokesSolver(std::shared_ptr< const VelocityGG > vGridGeometry, std::shared_ptr< const PressureGG > pGridGeometry, const Vector &dirichletDofs, const std::string &paramGroup="")
Constructor.
Definition: stokes_solver.hh:339
std::string name() const
Definition: stokes_solver.hh:381
Scalar norm(const Vector &b) const
Definition: stokes_solver.hh:376
Some exceptions thrown in DuMux
Helper classes to compute the integration elements.
GridCache::LocalView localView(const GridCache &gridCache)
Free function to get the local view of a grid cache object.
Definition: localview.hh:26
auto volume(const Geometry &geo, unsigned int integrationOrder=4)
The volume of a given geometry.
Definition: volume.hh:159
void symmetrizeConstraints(Dune::BCRSMatrix< MBlock > &A, Vector &b, const Vector &constrainedRows)
Symmetrize the constrained system Ax=b.
Definition: symmetrize_constraints.hh:117
bool hasParamInGroup(const std::string &paramGroup, const std::string &param)
Check whether a key exists in the parameter tree with a model group prefix.
Definition: parameters.hh:165
Helper function to generate Jacobian pattern for different discretization methods.
Generates a parameter tree required for the linear solvers and precondioners of the Dune ISTL.
Define traits for linear solvers.
Define some often used mathematical functions.
Distance implementation details.
Definition: cvfelocalresidual.hh:25
constexpr CCMpfa ccmpfa
Definition: method.hh:146
constexpr FCDiamond fcdiamond
Definition: method.hh:152
constexpr CCTpfa cctpfa
Definition: method.hh:145
constexpr Box box
Definition: method.hh:147
Definition: adapt.hh:17
Provides a helper class for nonoverlapping decomposition.
A parallel version of a linear operator.
Dumux preconditioners for iterative solvers.
Base class for linear solvers.
Helper type to determine whether a given type is a Dune::MultiTypeBlockMatrix.
Definition: matrix.hh:37
Helper function to symmetrize row-constraints in constrained linear systems.