OGS
EigenLinearSolver.cpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: Copyright (c) OpenGeoSys Community (opengeosys.org)
2// SPDX-License-Identifier: BSD-3-Clause
3
4#include "EigenLinearSolver.h"
5
6#include <Eigen/Sparse>
7
8#include "BaseLib/Error.h"
9#include "BaseLib/Logging.h"
10
11#ifdef USE_MKL
12#include <Eigen/PardisoSupport>
13#endif
14
15#ifdef USE_EIGEN_UNSUPPORTED
16// TODO(naumov); Don't include header files directly from Eigen's source dir
17// clang-format off
18#include <unsupported/Eigen/IterativeSolvers>
19#include <unsupported/Eigen/src/IterativeSolvers/Scaling.h>
20// clang-format on
21#endif
22
23#include "EigenMatrix.h"
24#include "EigenVector.h"
25
26namespace MathLib
27{
28// TODO change to LinearSolver
30{
31public:
34
35 virtual ~EigenLinearSolverBase() = default;
36
37 bool solve(Vector& b, Vector& x, EigenOption& opt)
38 {
39#ifdef USE_EIGEN_UNSUPPORTED
40 if (scaling_)
41 {
42 b = scaling_->LeftScaling().cwiseProduct(b);
43 }
44#endif
45
46 auto const success = solveImpl(b, x, opt);
47
48#ifdef USE_EIGEN_UNSUPPORTED
49 if (scaling_)
50 {
51 x = scaling_->RightScaling().cwiseProduct(x);
52 }
53#endif
54
55 return success;
56 }
57
58 bool compute(Matrix& A, EigenOption& opt,
59 MathLib::LinearSolverBehaviour linear_solver_behaviour)
60 {
61 if (linear_solver_behaviour == MathLib::LinearSolverBehaviour::REUSE)
62 {
63 // Checking if this is the first compute() call should work both for
64 // direct solvers (that factorize the matrix A) and for iterative
65 // solvers (that store a reference to A in the preconditioner).
67 {
68 // There is nothing there, yet, to be reused. Hence, we compute
69 // and store the result.
70 linear_solver_behaviour =
72 }
73 else
74 {
75 return true;
76 }
77 }
78
79 // TODO (CL) That might not work under all circumstances, e.g., if the
80 // linear solver fails subsequently. Time will tell.
82
83#ifdef USE_EIGEN_UNSUPPORTED
84 if (opt.scaling)
85 {
86 INFO("-> scale");
87 scaling_ = std::make_unique<
88 Eigen::IterScaling<EigenMatrix::RawMatrixType>>();
89 scaling_->computeRef(A);
90 }
91#endif
92
93 return computeImpl(A, opt, linear_solver_behaviour);
94 }
95
97
98protected:
99 virtual bool solveImpl(Vector const& b, Vector& x, EigenOption& opt) = 0;
100
101 virtual bool computeImpl(
102 Matrix& A, EigenOption& opt,
103 MathLib::LinearSolverBehaviour const linear_solver_behaviour) = 0;
104
105private:
106#ifdef USE_EIGEN_UNSUPPORTED
107 std::unique_ptr<Eigen::IterScaling<EigenMatrix::RawMatrixType>> scaling_;
108#endif
109
111};
112
113namespace details
114{
116template <class T_SOLVER>
118{
119public:
120 bool solveImpl(Vector const& b, Vector& x, EigenOption& opt) override
121 {
122 INFO("-> solve with Eigen direct linear solver {:s}",
124
125 x = solver_.solve(b);
126 if (solver_.info() != Eigen::Success || x.hasNaN())
127 {
128 ERR("Failed during Eigen linear solve");
129 return false;
130 }
131
132 return true;
133 }
134
136 [[maybe_unused]] MathLib::LinearSolverBehaviour const
137 linear_solver_behaviour) override
138 {
139 INFO("-> compute with Eigen direct linear solver {:s}",
141 if (!A.isCompressed())
142 {
143 A.makeCompressed();
144 }
145
146 solver_.compute(A);
147 if (solver_.info() != Eigen::Success)
148 {
149 ERR("Failed during Eigen linear solver initialization");
150 return false;
151 }
152
153 return true;
154 }
155
156private:
157 T_SOLVER solver_;
158};
159
160#ifdef USE_EIGEN_UNSUPPORTED
161// implementations for some iterative linear solver methods --------------------
162
163// restart
164template <typename Solver>
165void setRestartImpl(Solver&, int const)
166{
167 DBUG("-> restart is not implemented for this linear solver.");
168}
169
170template <typename Matrix, typename Precon>
171void setRestartImpl(Eigen::GMRES<Matrix, Precon>& solver, int const restart)
172{
173 solver.set_restart(restart);
174 INFO("-> set restart value: {:d}", solver.get_restart());
175}
176
177// L
178template <typename Solver>
179void setLImpl(Solver&, int const)
180{
181 DBUG("-> setL() is not implemented for this linear solver.");
182}
183
184template <typename Matrix, typename Precon>
185void setLImpl(Eigen::BiCGSTABL<Matrix, Precon>& solver, int const l)
186{
187 solver.setL(l);
188}
189
190template <typename Matrix, typename Precon>
191void setLImpl(Eigen::IDRSTABL<Matrix, Precon>& solver, int const l)
192{
193 solver.setL(l);
194}
195
196// S
197template <typename Solver>
198void setSImpl(Solver&, int const)
199{
200 DBUG("-> setS() is not implemented for this linear solver.");
201}
202
203template <typename Matrix, typename Precon>
204void setSImpl(Eigen::IDRS<Matrix, Precon>& solver, int const s)
205{
206 solver.setS(s);
207}
208
209template <typename Matrix, typename Precon>
210void setSImpl(Eigen::IDRSTABL<Matrix, Precon>& solver, int const s)
211{
212 solver.setS(s);
213}
214
215// angle
216template <typename Solver>
217void setAngleImpl(Solver&, double const)
218{
219 DBUG("-> setAngle() is not implemented for this linear solver.");
220}
221
222template <typename Matrix, typename Precon>
223void setAngleImpl(Eigen::IDRS<Matrix, Precon>& solver, double const angle)
224{
225 solver.setAngle(angle);
226}
227
228// smoothing
229template <typename Solver>
230void setSmoothingImpl(Solver&, bool const)
231{
232 DBUG("-> setSmoothing() is not implemented for this linear solver.");
233}
234
235template <typename Matrix, typename Precon>
236void setSmoothingImpl(Eigen::IDRS<Matrix, Precon>& solver, bool const smoothing)
237{
238 solver.setSmoothing(smoothing);
239}
240
241// residual update
242template <typename Solver>
243void setResidualUpdateImpl(Solver&, bool const)
244{
245 DBUG("-> setResidualUpdate() is not implemented for this linear solver.");
246}
247
248template <typename Matrix, typename Precon>
249void setResidualUpdateImpl(Eigen::IDRS<Matrix, Precon>& solver,
250 bool const residual_update)
251{
252 solver.setResidualUpdate(residual_update);
253}
254
255// -----------------------------------------------------------------------------
256#endif
257
259template <class T_SOLVER>
261{
262public:
264 Matrix& A, EigenOption& opt,
265 MathLib::LinearSolverBehaviour const linear_solver_behaviour) override
266 {
267 INFO("-> compute with Eigen iterative linear solver {:s} (precon {:s})",
270 solver_.setTolerance(opt.error_tolerance);
271 solver_.setMaxIterations(opt.max_iterations);
272
273#ifdef USE_EIGEN_UNSUPPORTED
275 opt.restart);
279 opt.smoothing);
281 opt.angle);
283 T_SOLVER>::setResidualUpdate(opt.residualupdate);
284#endif
285
286 auto compute = [this](Matrix& m)
287 {
288 if (!m.isCompressed())
289 {
290 m.makeCompressed();
291 }
292
293 solver_.compute(m);
294 };
295
296 switch (linear_solver_behaviour)
297 {
299 {
300 // matrix must be copied, because Eigen's linear solver stores a
301 // reference to it cf.
302 // https://libeigen.gitlab.io/docs/classEigen_1_1IterativeSolverBase.html#a7dfa55c55e82d697bde227696a630914
303 A_ = A;
304 compute(A_);
305 break;
306 }
308 {
309 compute(A);
310 break;
311 }
313 OGS_FATAL(
314 "If NumLib::LinearSolverBehaviour::REUSE is set then "
315 "EigenLinearSolver::compute() should never be executed");
316 };
317
318 if (solver_.info() != Eigen::Success)
319 {
320 ERR("Failed during Eigen linear solver initialization");
321 return false;
322 }
323
324 return true;
325 }
326
327 bool solveImpl(Vector const& b, Vector& x, EigenOption& opt) override
328 {
329 INFO("-> solve with Eigen iterative linear solver {:s} (precon {:s})",
332
333 x = solver_.solveWithGuess(b, x);
334 INFO("\t iteration: {:d}/{:d}", solver_.iterations(),
335 opt.max_iterations);
336 INFO("\t residual: {:e}\n", solver_.error());
337
338 if (solver_.info() != Eigen::Success)
339 {
340 ERR("Failed during Eigen linear solve");
341 return false;
342 }
343
344 return true;
345 }
346
347private:
348 T_SOLVER solver_;
350#ifdef USE_EIGEN_UNSUPPORTED
351 void setRestart(int const restart) { setRestartImpl(solver_, restart); }
352 void setL(int const l) { setLImpl(solver_, l); }
353 void setS(int const s) { setSImpl(solver_, s); }
354 void setAngle(double const angle) { setAngleImpl(solver_, angle); }
355 void setSmoothing(bool const smoothing)
356 {
357 setSmoothingImpl(solver_, smoothing);
358 }
359 void setResidualUpdate(bool const residual_update)
360 {
361 setResidualUpdateImpl(solver_, residual_update);
362 }
363#endif
364};
365
366template <template <typename, typename> class Solver, typename Precon>
367std::unique_ptr<EigenLinearSolverBase> createIterativeSolver()
368{
369 using Slv =
371 return std::make_unique<Slv>();
372}
373
374template <template <typename, typename> class Solver>
375std::unique_ptr<EigenLinearSolverBase> createIterativeSolver(
376 EigenOption::PreconType precon_type)
377{
378 switch (precon_type)
379 {
381 return createIterativeSolver<Solver,
382 Eigen::IdentityPreconditioner>();
385 Solver, Eigen::DiagonalPreconditioner<double>>();
388 Solver, Eigen::LeastSquareDiagonalPreconditioner<double>>();
390 // TODO for this preconditioner further options can be passed.
391 // see
392 // https://libeigen.gitlab.io/docs/classEigen_1_1IncompleteLUT.html
393 return createIterativeSolver<Solver,
394 Eigen::IncompleteLUT<double>>();
395 default:
396 OGS_FATAL("Invalid Eigen preconditioner type.");
397 }
398}
399
400template <typename Mat, typename Precon>
401using EigenCGSolverL = Eigen::ConjugateGradient<Mat, Eigen::Lower, Precon>;
402
403template <typename Mat, typename Precon>
404using EigenCGSolverU = Eigen::ConjugateGradient<Mat, Eigen::Upper, Precon>;
405
406template <typename Mat, typename Precon>
408 Eigen::ConjugateGradient<Mat, Eigen::Lower | Eigen::Upper, Precon>;
409
410std::unique_ptr<EigenLinearSolverBase> createIterativeSolver(
411 EigenOption::SolverType solver_type, EigenOption::PreconType precon_type,
412 EigenOption::TriangularMatrixType triangular_matrix_type)
413{
414 switch (solver_type)
415 {
417 {
418 return createIterativeSolver<Eigen::BiCGSTAB>(precon_type);
419 }
421 {
422#ifdef USE_EIGEN_UNSUPPORTED
423#if EIGEN_VERSION_AT_LEAST(3, 4, 90)
424 return createIterativeSolver<Eigen::BiCGSTABL>(precon_type);
425#else
426 OGS_FATAL("BiCGSTABL requires at least Eigen version 3.4.90");
427#endif
428#else
429 OGS_FATAL(
430 "The code is not compiled with the Eigen unsupported modules. "
431 "Linear solver type BiCGSTABL is not available.");
432#endif
433 }
435 {
436 switch (triangular_matrix_type)
437 {
439 return createIterativeSolver<EigenCGSolverU>(precon_type);
441 return createIterativeSolver<EigenCGSolverLU>(precon_type);
442 default:
443 return createIterativeSolver<EigenCGSolverL>(precon_type);
444 }
445 }
447 {
449 precon_type);
450 }
452 {
453#ifdef USE_EIGEN_UNSUPPORTED
454 return createIterativeSolver<Eigen::GMRES>(precon_type);
455#else
456 OGS_FATAL(
457 "The code is not compiled with the Eigen unsupported modules. "
458 "Linear solver type GMRES is not available.");
459#endif
460 }
462 {
463#ifdef USE_EIGEN_UNSUPPORTED
464#if EIGEN_VERSION_AT_LEAST(3, 4, 90)
465 return createIterativeSolver<Eigen::IDRS>(precon_type);
466#else
467 OGS_FATAL("IDRS requires at least Eigen version 3.4.90");
468#endif
469#else
470 OGS_FATAL(
471 "The code is not compiled with the Eigen unsupported modules. "
472 "Linear solver type IDRS is not available.");
473#endif
474 }
476 {
477#ifdef USE_EIGEN_UNSUPPORTED
478#if EIGEN_VERSION_AT_LEAST(3, 4, 90)
479 return createIterativeSolver<Eigen::IDRSTABL>(precon_type);
480#else
481 OGS_FATAL("IDRSTABL requires at least Eigen version 3.4.90");
482#endif
483#else
484 OGS_FATAL(
485 "The code is not compiled with the Eigen unsupported modules. "
486 "Linear solver type IDRSTABL is not available.");
487#endif
488 }
489 default:
490 OGS_FATAL("Invalid Eigen iterative linear solver type. Aborting.");
491 }
492}
493
494} // namespace details
495
496EigenLinearSolver::EigenLinearSolver(std::string const& /*solver_name*/,
497 EigenOption const& option)
498 : option_(option)
499{
500 using Matrix = EigenMatrix::RawMatrixType;
501
502 switch (option_.solver_type)
503 {
504 case EigenOption::SolverType::SparseLU:
505 {
506 using SolverType =
507 Eigen::SparseLU<Matrix, Eigen::COLAMDOrdering<int>>;
508 solver_ = std::make_unique<
509 details::EigenDirectLinearSolver<SolverType>>();
510 can_solve_rectangular_ = false;
511 return;
512 }
519 solver_ =
521 option_.precon_type,
522 option_.triangular_matrix_type);
524 return;
526 solver_ =
528 option_.precon_type,
529 option_.triangular_matrix_type);
531 return;
533 {
534#ifdef USE_MKL
535 using SolverType = Eigen::PardisoLU<EigenMatrix::RawMatrixType>;
536 solver_.reset(new details::EigenDirectLinearSolver<SolverType>);
537 can_solve_rectangular_ = false;
538 return;
539#else
540 OGS_FATAL(
541 "The code is not compiled with Intel MKL. Linear solver type "
542 "PardisoLU is not available.");
543#endif
544 }
545 }
546
547 OGS_FATAL("Invalid Eigen linear solver type. Aborting.");
548}
549
550EigenLinearSolver::~EigenLinearSolver() = default;
551
553 EigenMatrix& A,
554 MathLib::LinearSolverBehaviour const linear_solver_behaviour)
555{
556 INFO("------------------------------------------------------------------");
557 INFO("*** Eigen solver compute()");
558
559 return solver_->compute(A.getRawMatrix(), option_, linear_solver_behaviour);
560}
561
563{
564 INFO("------------------------------------------------------------------");
565 INFO("*** Eigen solver solve()");
566
567 return solver_->solve(b.getRawVector(), x.getRawVector(), option_);
568}
569
572 MathLib::LinearSolverBehaviour const linear_solver_behaviour)
573{
574 return solver_->compute(A.getRawMatrix(), option_,
575 linear_solver_behaviour) &&
576 solver_->solve(b.getRawVector(), x.getRawVector(), option_);
577}
578
580 MathLib::LinearSolverBehaviour const linear_solver_behaviour) const
581{
582 return linear_solver_behaviour != MathLib::LinearSolverBehaviour::REUSE ||
583 !solver_->didComputeAtLeastOnce();
584}
585
586} // namespace MathLib
#define OGS_FATAL(...)
Definition Error.h:19
void INFO(fmt::format_string< Args... > fmt, Args &&... args)
Definition Logging.h:28
void DBUG(fmt::format_string< Args... > fmt, Args &&... args)
Definition Logging.h:22
void ERR(fmt::format_string< Args... > fmt, Args &&... args)
Definition Logging.h:40
bool compute(Matrix &A, EigenOption &opt, MathLib::LinearSolverBehaviour linear_solver_behaviour)
EigenMatrix::RawMatrixType Matrix
EigenVector::RawVectorType Vector
virtual bool computeImpl(Matrix &A, EigenOption &opt, MathLib::LinearSolverBehaviour const linear_solver_behaviour)=0
virtual bool solveImpl(Vector const &b, Vector &x, EigenOption &opt)=0
virtual ~EigenLinearSolverBase()=default
bool solve(Vector &b, Vector &x, EigenOption &opt)
bool compute(EigenMatrix &A, MathLib::LinearSolverBehaviour const linear_solver_behaviour)
bool solve(EigenVector &b, EigenVector &x)
EigenLinearSolver(std::string const &solver_name, EigenOption const &option)
bool willCompute(MathLib::LinearSolverBehaviour const linear_solver_behaviour) const
std::unique_ptr< EigenLinearSolverBase > solver_
Eigen::SparseMatrix< double, Eigen::RowMajor > RawMatrixType
Definition EigenMatrix.h:24
RawMatrixType & getRawMatrix()
Global vector based on Eigen vector.
Definition EigenVector.h:19
Eigen::VectorXd RawVectorType
Definition EigenVector.h:21
RawVectorType & getRawVector()
return a raw Eigen vector object
Template class for Eigen direct linear solvers.
bool solveImpl(Vector const &b, Vector &x, EigenOption &opt) override
bool computeImpl(Matrix &A, EigenOption &opt, MathLib::LinearSolverBehaviour const linear_solver_behaviour) override
Template class for Eigen iterative linear solvers.
bool solveImpl(Vector const &b, Vector &x, EigenOption &opt) override
bool computeImpl(Matrix &A, EigenOption &opt, MathLib::LinearSolverBehaviour const linear_solver_behaviour) override
Eigen::ConjugateGradient< Mat, Eigen::Lower, Precon > EigenCGSolverL
Eigen::ConjugateGradient< Mat, Eigen::Lower|Eigen::Upper, Precon > EigenCGSolverLU
std::unique_ptr< EigenLinearSolverBase > createIterativeSolver()
Eigen::ConjugateGradient< Mat, Eigen::Upper, Precon > EigenCGSolverU
static const double s
Option for Eigen sparse solver.
Definition EigenOption.h:12
TriangularMatrixType
triangular matrix type
Definition EigenOption.h:38
PreconType precon_type
Preconditioner type.
Definition EigenOption.h:47
static std::string getSolverName(SolverType const solver_type)
return a linear solver name from the solver type
PreconType
Preconditioner type.
Definition EigenOption.h:29
SolverType solver_type
Linear solver type.
Definition EigenOption.h:45
double error_tolerance
Error tolerance.
Definition EigenOption.h:53
SolverType
Solver type.
Definition EigenOption.h:15
static std::string getPreconName(PreconType const precon_type)
return a preconditioner name from the preconditioner type
int max_iterations
Maximum iteration count.
Definition EigenOption.h:51