ExaDG
Loading...
Searching...
No Matches
iterative_solvers_dealii_wrapper.h
1/* ______________________________________________________________________
2 *
3 * ExaDG - High-Order Discontinuous Galerkin for the Exa-Scale
4 *
5 * Copyright (C) 2021 by the ExaDG authors
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 * ______________________________________________________________________
20 */
21
22#ifndef EXADG_SOLVERS_AND_PRECONDITIONERS_SOLVERS_ITERATIVE_SOLVERS_DEALII_WRAPPER_H_
23#define EXADG_SOLVERS_AND_PRECONDITIONERS_SOLVERS_ITERATIVE_SOLVERS_DEALII_WRAPPER_H_
24
25// deal.II
26#include <deal.II/base/timer.h>
27#include <deal.II/lac/precondition.h>
28#include <deal.II/lac/solver_cg.h>
29#include <deal.II/lac/solver_gmres.h>
30#include <deal.II/lac/solver_selector.h>
31
32// ExaDG
33#include <exadg/solvers_and_preconditioners/solvers/solver_data.h>
34#include <exadg/solvers_and_preconditioners/utilities/compute_eigenvalues.h>
35#include <exadg/utilities/timer_tree.h>
36
37namespace ExaDG
38{
39namespace Krylov
40{
41/*
42 * Krylov solver base class agnostic of the operator used.
43 */
44template<typename VectorType>
45class SolverBase
46{
47public:
48 SolverBase() : l2_0(1.0), l2_n(1.0), n(0), rho(0.0), n10(0)
49 {
50 timer_tree = std::make_shared<TimerTree>();
51 }
52
53 virtual unsigned int
54 solve(VectorType & dst, VectorType const & rhs) const = 0;
55
56 virtual ~SolverBase()
57 {
58 }
59
60 virtual void
61 update_preconditioner(bool const update_preconditioner) const = 0;
62
63 template<typename Control>
64 void
65 do_compute_performance_metrics(Control const & solver_control) const
66 {
67 // get some statistics related to convergence
68 this->l2_0 = solver_control.initial_value();
69 this->l2_n = solver_control.last_value();
70 this->n = solver_control.last_step();
71
72 // compute some derived performance metrics
73 if(n > 0)
74 {
75 this->rho = std::pow(l2_n / l2_0, 1.0 / n);
76 this->n10 = -10.0 * std::log(10.0) / std::log(rho);
77 }
78 }
79
80 virtual std::shared_ptr<TimerTree>
81 get_timings() const
82 {
83 return timer_tree;
84 }
85
86 // performance metrics
87 mutable double l2_0; // norm of initial residual
88 mutable double l2_n; // norm of final residual
89 mutable unsigned int n; // number of iterations
90 mutable double rho; // average convergence rate
91 mutable double n10; // number of iterations needed to reduce the residual by 1e10
92
93protected:
94 std::shared_ptr<TimerTree> timer_tree;
95};
96
97/*
98 * Wrapper around `dealii::SolverSelector` with extended functionality.
99 */
100template<typename Operator, typename Preconditioner, typename VectorType>
101class KrylovSolver : public SolverBase<VectorType>
102{
103public:
104 KrylovSolver(Operator const & underlying_operator_in,
105 Preconditioner & preconditioner_in,
106 SolverData const & solver_data_in,
107 bool const use_preconditioner_in,
108 bool const compute_performance_metrics_in = false,
109 bool const compute_eigenvalues_in = false)
110 : underlying_operator(underlying_operator_in),
111 preconditioner(preconditioner_in),
112 solver_data(solver_data_in),
113 solver_name(linear_solver_to_string(solver_data.linear_solver)),
114 use_preconditioner(use_preconditioner_in),
115 compute_performance_metrics(compute_performance_metrics_in),
116 compute_eigenvalues(compute_eigenvalues_in)
117 {
118 }
119
120 virtual ~KrylovSolver()
121 {
122 }
123
124 void
125 update_preconditioner(bool const update_preconditioner) const override
126 {
127 if(use_preconditioner)
128 {
129 if(preconditioner.needs_update() or update_preconditioner)
130 {
131 preconditioner.update();
132 }
133 }
134 }
135
136 unsigned int
137 solve(VectorType & dst, VectorType const & rhs) const override
138 {
139 dealii::Timer timer;
140
141 dealii::ReductionControl solver_control(solver_data.max_iter,
142 solver_data.abs_tol,
143 solver_data.rel_tol);
144
145 dealii::SolverSelector<VectorType> solver(solver_name, solver_control);
146
147 // Additional settings depending on requested solver type.
148 if(solver_name == "gmres")
149 {
150 typename dealii::SolverGMRES<VectorType>::AdditionalData additional_data;
151 additional_data.max_n_tmp_vectors = solver_data.max_krylov_size;
152 additional_data.right_preconditioning = true;
153
154 solver.set_data(additional_data);
155 }
156 else if(solver_name == "fgmres")
157 {
158 typename dealii::SolverFGMRES<VectorType>::AdditionalData additional_data;
159 additional_data.max_basis_size = solver_data.max_krylov_size;
160 // FGMRES always uses right preconditioning
161
162 solver.set_data(additional_data);
163 }
164
165 // Store the initial guess for a *second* system solve during which eigenvalues are estimated.
166 VectorType initial_guess;
167 if(compute_eigenvalues == true)
168 {
169 initial_guess.reinit(dst, true /* omit_zeroing_entries */);
170 initial_guess.copy_locally_owned_data_from(dst);
171 }
172
173 // Iterative solvers might be brittle for matching `src` and `dst` depending on the FE space
174 // chosen.
175 if(dealii::PointerComparison::equal(&dst, &rhs) == true)
176 {
177 // Start from a zero initial guess.
178 VectorType tmp_dst;
179 tmp_dst.reinit(dst, false /* omit_zeroing_entries */);
180 if(use_preconditioner == false)
181 {
182 solver.solve(underlying_operator, tmp_dst, rhs, dealii::PreconditionIdentity());
183 }
184 else
185 {
186 solver.solve(underlying_operator, tmp_dst, rhs, preconditioner);
187 }
188 dst.copy_locally_owned_data_from(tmp_dst);
189 }
190 else
191 {
192 if(use_preconditioner == false)
193 {
194 solver.solve(underlying_operator, dst, rhs, dealii::PreconditionIdentity());
195 }
196 else
197 {
198 solver.solve(underlying_operator, dst, rhs, preconditioner);
199 }
200 }
201
202 // Estimate eigenvalues using a *second* system solve using GMRES. This approach should *only*
203 // be used to compute eigenvalues for debugging.
204 if(compute_eigenvalues == true)
205 {
206 estimate_eigenvalues_gmres(
207 underlying_operator, preconditioner, initial_guess, rhs, solver_data, true /* print */);
208 }
209
210 AssertThrow(std::isfinite(solver_control.last_value()),
211 dealii::ExcMessage("Last iteration step contained NaN or Inf values."));
212
213 if(compute_performance_metrics)
214 {
215 this->do_compute_performance_metrics(solver_control);
216 }
217
218 this->timer_tree->insert({"Solver (" + solver_name + ")"}, timer.wall_time());
219
220 return solver_control.last_step();
221 }
222
223 std::shared_ptr<TimerTree>
224 get_timings() const override
225 {
226 if(use_preconditioner)
227 {
228 this->timer_tree->insert({"Solver (" + solver_name + ")"}, preconditioner.get_timings());
229 }
230
231 return this->timer_tree;
232 }
233
234private:
235 Operator const & underlying_operator;
236 Preconditioner & preconditioner;
237 SolverData const solver_data;
238
239 // `LinearSolver` enum converted to `std::string` identifying the linear solver type
240 // `dealii::SolverSelector::solver_name`.
241 std::string const solver_name;
242
243 bool use_preconditioner;
244 bool compute_performance_metrics;
245 bool compute_eigenvalues;
246};
247
248} // namespace Krylov
249} // namespace ExaDG
250
251#endif /* EXADG_SOLVERS_AND_PRECONDITIONERS_SOLVERS_ITERATIVE_SOLVERS_DEALII_WRAPPER_H_ */
Definition driver.cpp:33
Definition solver_data.h:92