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