OGS
EigenLinearSolver.cpp
Go to the documentation of this file.
1
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 const linear_solver_behaviour)
67 {
68#ifdef USE_EIGEN_UNSUPPORTED
69 if (opt.scaling)
70 {
71 INFO("-> scale");
72 scaling_ = std::make_unique<
73 Eigen::IterScaling<EigenMatrix::RawMatrixType>>();
74 scaling_->computeRef(A);
75 }
76#endif
77
78 return computeImpl(A, opt, linear_solver_behaviour);
79 }
80
81protected:
82 virtual bool solveImpl(Vector const& b, Vector& x, EigenOption& opt) = 0;
83
84 virtual bool computeImpl(
85 Matrix& A, EigenOption& opt,
86 MathLib::LinearSolverBehaviour const linear_solver_behaviour) = 0;
87
88private:
89#ifdef USE_EIGEN_UNSUPPORTED
90 std::unique_ptr<Eigen::IterScaling<EigenMatrix::RawMatrixType>> scaling_;
91#endif
92};
93
94namespace details
95{
97template <class T_SOLVER>
99{
100public:
101 bool solveImpl(Vector const& b, Vector& x, EigenOption& opt) override
102 {
103 INFO("-> solve with Eigen direct linear solver {:s}",
105
106 x = solver_.solve(b);
107 if (solver_.info() != Eigen::Success || x.hasNaN())
108 {
109 ERR("Failed during Eigen linear solve");
110 return false;
111 }
112
113 return true;
114 }
115
117 [[maybe_unused]] MathLib::LinearSolverBehaviour const
118 linear_solver_behaviour) override
119 {
120 INFO("-> compute with Eigen direct linear solver {:s}",
122 if (!A.isCompressed())
123 {
124 A.makeCompressed();
125 }
126
127 solver_.compute(A);
128 if (solver_.info() != Eigen::Success)
129 {
130 ERR("Failed during Eigen linear solver initialization");
131 return false;
132 }
133
134 return true;
135 }
136
137private:
138 T_SOLVER solver_;
139};
140
141#ifdef USE_EIGEN_UNSUPPORTED
142// implementations for some iterative linear solver methods --------------------
143
144// restart
145template <typename Solver>
146void setRestartImpl(Solver&, int const)
147{
148 DBUG("-> restart is not implemented for this linear solver.");
149}
150
151template <typename Matrix, typename Precon>
152void setRestartImpl(Eigen::GMRES<Matrix, Precon>& solver, int const restart)
153{
154 solver.set_restart(restart);
155 INFO("-> set restart value: {:d}", solver.get_restart());
156}
157
158// L
159template <typename Solver>
160void setLImpl(Solver&, int const)
161{
162 DBUG("-> setL() is not implemented for this linear solver.");
163}
164
165template <typename Matrix, typename Precon>
166void setLImpl(Eigen::BiCGSTABL<Matrix, Precon>& solver, int const l)
167{
168 solver.setL(l);
169}
170
171template <typename Matrix, typename Precon>
172void setLImpl(Eigen::IDRSTABL<Matrix, Precon>& solver, int const l)
173{
174 solver.setL(l);
175}
176
177// S
178template <typename Solver>
179void setSImpl(Solver&, int const)
180{
181 DBUG("-> setS() is not implemented for this linear solver.");
182}
183
184template <typename Matrix, typename Precon>
185void setSImpl(Eigen::IDRS<Matrix, Precon>& solver, int const s)
186{
187 solver.setS(s);
188}
189
190template <typename Matrix, typename Precon>
191void setSImpl(Eigen::IDRSTABL<Matrix, Precon>& solver, int const s)
192{
193 solver.setS(s);
194}
195
196// angle
197template <typename Solver>
198void setAngleImpl(Solver&, double const)
199{
200 DBUG("-> setAngle() is not implemented for this linear solver.");
201}
202
203template <typename Matrix, typename Precon>
204void setAngleImpl(Eigen::IDRS<Matrix, Precon>& solver, double const angle)
205{
206 solver.setAngle(angle);
207}
208
209// smoothing
210template <typename Solver>
211void setSmoothingImpl(Solver&, bool const)
212{
213 DBUG("-> setSmoothing() is not implemented for this linear solver.");
214}
215
216template <typename Matrix, typename Precon>
217void setSmoothingImpl(Eigen::IDRS<Matrix, Precon>& solver, bool const smoothing)
218{
219 solver.setSmoothing(smoothing);
220}
221
222// residual update
223template <typename Solver>
224void setResidualUpdateImpl(Solver&, bool const)
225{
226 DBUG("-> setResidualUpdate() is not implemented for this linear solver.");
227}
228
229template <typename Matrix, typename Precon>
230void setResidualUpdateImpl(Eigen::IDRS<Matrix, Precon>& solver,
231 bool const residual_update)
232{
233 solver.setResidualUpdate(residual_update);
234}
235
236// -----------------------------------------------------------------------------
237#endif
238
240template <class T_SOLVER>
242{
243public:
245 Matrix& A, EigenOption& opt,
246 MathLib::LinearSolverBehaviour const linear_solver_behaviour) override
247 {
248 INFO("-> compute with Eigen iterative linear solver {:s} (precon {:s})",
251 solver_.setTolerance(opt.error_tolerance);
252 solver_.setMaxIterations(opt.max_iterations);
253
254#ifdef USE_EIGEN_UNSUPPORTED
256 opt.restart);
260 opt.smoothing);
262 opt.angle);
264 T_SOLVER>::setResidualUpdate(opt.residualupdate);
265#endif
266
267 auto compute = [this](Matrix& m)
268 {
269 if (!m.isCompressed())
270 {
271 m.makeCompressed();
272 }
273
274 solver_.compute(m);
275 };
276
277 switch (linear_solver_behaviour)
278 {
280 {
281 // matrix must be copied, because Eigen's linear solver stores a
282 // reference to it cf.
283 // https://libeigen.gitlab.io/docs/classEigen_1_1IterativeSolverBase.html#a7dfa55c55e82d697bde227696a630914
284 A_ = A;
285 compute(A_);
286 break;
287 }
289 {
290 compute(A);
291 break;
292 }
294 OGS_FATAL(
295 "If NumLib::LinearSolverBehaviour::REUSE is set then "
296 "EigenLinearSolver::compute() should never be executed");
297 };
298
299 if (solver_.info() != Eigen::Success)
300 {
301 ERR("Failed during Eigen linear solver initialization");
302 return false;
303 }
304
305 return true;
306 }
307
308 bool solveImpl(Vector const& b, Vector& x, EigenOption& opt) override
309 {
310 INFO("-> solve with Eigen iterative linear solver {:s} (precon {:s})",
313
314 x = solver_.solveWithGuess(b, x);
315 INFO("\t iteration: {:d}/{:d}", solver_.iterations(),
316 opt.max_iterations);
317 INFO("\t residual: {:e}\n", solver_.error());
318
319 if (solver_.info() != Eigen::Success)
320 {
321 ERR("Failed during Eigen linear solve");
322 return false;
323 }
324
325 return true;
326 }
327
328private:
329 T_SOLVER solver_;
331#ifdef USE_EIGEN_UNSUPPORTED
332 void setRestart(int const restart) { setRestartImpl(solver_, restart); }
333 void setL(int const l) { setLImpl(solver_, l); }
334 void setS(int const s) { setSImpl(solver_, s); }
335 void setAngle(double const angle) { setAngleImpl(solver_, angle); }
336 void setSmoothing(bool const smoothing)
337 {
338 setSmoothingImpl(solver_, smoothing);
339 }
340 void setResidualUpdate(bool const residual_update)
341 {
342 setResidualUpdateImpl(solver_, residual_update);
343 }
344#endif
345};
346
347template <template <typename, typename> class Solver, typename Precon>
348std::unique_ptr<EigenLinearSolverBase> createIterativeSolver()
349{
350 using Slv =
352 return std::make_unique<Slv>();
353}
354
355template <template <typename, typename> class Solver>
356std::unique_ptr<EigenLinearSolverBase> createIterativeSolver(
357 EigenOption::PreconType precon_type)
358{
359 switch (precon_type)
360 {
362 return createIterativeSolver<Solver,
363 Eigen::IdentityPreconditioner>();
366 Solver, Eigen::DiagonalPreconditioner<double>>();
368 // TODO for this preconditioner further options can be passed.
369 // see
370 // https://libeigen.gitlab.io/docs/classEigen_1_1IncompleteLUT.html
371 return createIterativeSolver<Solver,
372 Eigen::IncompleteLUT<double>>();
373 default:
374 OGS_FATAL("Invalid Eigen preconditioner type.");
375 }
376}
377
378template <typename Mat, typename Precon>
379using EigenCGSolver = Eigen::ConjugateGradient<Mat, Eigen::Lower, Precon>;
380
381std::unique_ptr<EigenLinearSolverBase> createIterativeSolver(
382 EigenOption::SolverType solver_type, EigenOption::PreconType precon_type)
383{
384 switch (solver_type)
385 {
387 {
388 return createIterativeSolver<Eigen::BiCGSTAB>(precon_type);
389 }
391 {
392#ifdef USE_EIGEN_UNSUPPORTED
393#if EIGEN_VERSION_AT_LEAST(3, 4, 90)
394 return createIterativeSolver<Eigen::BiCGSTABL>(precon_type);
395#else
396 OGS_FATAL("BiCGSTABL requires at least Eigen version 3.4.90");
397#endif
398#else
399 OGS_FATAL(
400 "The code is not compiled with the Eigen unsupported modules. "
401 "Linear solver type BiCGSTABL is not available.");
402#endif
403 }
405 {
406 return createIterativeSolver<EigenCGSolver>(precon_type);
407 }
409 {
410#ifdef USE_EIGEN_UNSUPPORTED
411 return createIterativeSolver<Eigen::GMRES>(precon_type);
412#else
413 OGS_FATAL(
414 "The code is not compiled with the Eigen unsupported modules. "
415 "Linear solver type GMRES is not available.");
416#endif
417 }
419 {
420#ifdef USE_EIGEN_UNSUPPORTED
421#if EIGEN_VERSION_AT_LEAST(3, 4, 90)
422 return createIterativeSolver<Eigen::IDRS>(precon_type);
423#else
424 OGS_FATAL("IDRS requires at least Eigen version 3.4.90");
425#endif
426#else
427 OGS_FATAL(
428 "The code is not compiled with the Eigen unsupported modules. "
429 "Linear solver type IDRS is not available.");
430#endif
431 }
433 {
434#ifdef USE_EIGEN_UNSUPPORTED
435#if EIGEN_VERSION_AT_LEAST(3, 4, 90)
436 return createIterativeSolver<Eigen::IDRSTABL>(precon_type);
437#else
438 OGS_FATAL("IDRSTABL requires at least Eigen version 3.4.90");
439#endif
440#else
441 OGS_FATAL(
442 "The code is not compiled with the Eigen unsupported modules. "
443 "Linear solver type IDRSTABL is not available.");
444#endif
445 }
446 default:
447 OGS_FATAL("Invalid Eigen iterative linear solver type. Aborting.");
448 }
449}
450
451} // namespace details
452
453EigenLinearSolver::EigenLinearSolver(std::string const& /*solver_name*/,
454 EigenOption const& option)
455 : option_(option)
456{
457 using Matrix = EigenMatrix::RawMatrixType;
458
459 switch (option_.solver_type)
460 {
462 {
463 using SolverType =
464 Eigen::SparseLU<Matrix, Eigen::COLAMDOrdering<int>>;
465 solver_ = std::make_unique<
467 return;
468 }
477 return;
479 {
480#ifdef USE_MKL
481 using SolverType = Eigen::PardisoLU<EigenMatrix::RawMatrixType>;
483 return;
484#else
485 OGS_FATAL(
486 "The code is not compiled with Intel MKL. Linear solver type "
487 "PardisoLU is not available.");
488#endif
489 }
490 }
491
492 OGS_FATAL("Invalid Eigen linear solver type. Aborting.");
493}
494
496
498 EigenMatrix& A,
499 MathLib::LinearSolverBehaviour const linear_solver_behaviour)
500{
501 INFO("------------------------------------------------------------------");
502 INFO("*** Eigen solver compute()");
503
504 return solver_->compute(A.getRawMatrix(), option_, linear_solver_behaviour);
505}
506
508{
509 INFO("------------------------------------------------------------------");
510 INFO("*** Eigen solver solve()");
511
512 return solver_->solve(b.getRawVector(), x.getRawVector(), option_);
513}
514
517 MathLib::LinearSolverBehaviour const linear_solver_behaviour)
518{
519 return solver_->compute(A.getRawMatrix(), option_,
520 linear_solver_behaviour) &&
521 solver_->solve(b.getRawVector(), x.getRawVector(), option_);
522}
523
524} // namespace MathLib
#define OGS_FATAL(...)
Definition Error.h:26
void INFO(fmt::format_string< Args... > fmt, Args &&... args)
Definition Logging.h:35
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:45
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(Matrix &A, EigenOption &opt, MathLib::LinearSolverBehaviour const linear_solver_behaviour)
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)
std::unique_ptr< EigenLinearSolverBase > solver_
Eigen::SparseMatrix< double, Eigen::RowMajor > RawMatrixType
Definition EigenMatrix.h:30
RawMatrixType & getRawMatrix()
Global vector based on Eigen vector.
Definition EigenVector.h:25
Eigen::VectorXd RawVectorType
Definition EigenVector.h:27
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
std::unique_ptr< EigenLinearSolverBase > createIterativeSolver()
Eigen::ConjugateGradient< Mat, Eigen::Lower, Precon > EigenCGSolver
static const double s
Option for Eigen sparse solver.
Definition EigenOption.h:19
PreconType precon_type
Preconditioner type.
Definition EigenOption.h:44
static std::string getSolverName(SolverType const solver_type)
return a linear solver name from the solver type
PreconType
Preconditioner type.
Definition EigenOption.h:35
SolverType solver_type
Linear solver type.
Definition EigenOption.h:42
double error_tolerance
Error tolerance.
Definition EigenOption.h:48
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:46