Block-Structured AMR Software Framework
Loading...
Searching...
No Matches
AMReX_GMRES.H
Go to the documentation of this file.
1#ifndef AMREX_GMRES_H_
2#define AMREX_GMRES_H_
3#include <AMReX_Config.H>
4
5#include <AMReX_BLProfiler.H>
6#include <AMReX_Print.H>
7#include <AMReX_TableData.H>
8#include <AMReX_Vector.H>
9#include <cmath>
10#include <limits>
11#include <memory>
12
13namespace amrex {
14
85template <typename V, typename M>
86class GMRES
87{
88public:
89
90 using RT = typename M::RT; // double or float
91
94
103 void define (M& linop);
104
118 void solve (V& a_sol, V const& a_rhs, RT a_tol_rel, RT a_tol_abs, int a_its=-1);
119
122 void setInitialGuessNonzero (bool flag) { m_nonzero_guess = flag; }
123
125 void setVerbose (int v) { m_verbose = v; }
126
132 void setRestartLength (int rl);
133
139 void setMaxIters (int niters) { m_maxiter = niters; }
140
142 [[nodiscard]] int getNumIters () const { return m_its; }
143
145 [[nodiscard]] int getStatus () const { return m_status; }
146
148 [[nodiscard]] RT getResidualNorm () const { return m_res; }
149
151 [[nodiscard]] RT getInitialResidualNorm () const { return m_rnorm0; }
152
153private:
154 void clear ();
155 void allocate_scratch ();
156 void cycle (V& a_xx, int& a_status, int& a_itcount, RT& a_rnorm0, int a_maxiter);
157 void build_solution (V& a_xx, int it);
158 void compute_residual (V& a_rr, V const& a_xx, V const& a_bb);
159
160 [[nodiscard]] bool converged (RT r0, RT r) const;
161
162 void gram_schmidt_orthogonalization (int it);
163 void update_hessenberg (int it, bool happyend, RT& res);
164
165 int m_verbose = 0;
166 int m_maxiter = 2000;
167 int m_its = 0;
168 int m_status = -1;
169 int m_restrtlen = 30;
170 bool m_nonzero_guess = false;
171 RT m_res = std::numeric_limits<RT>::max();
172 RT m_rnorm0 = std::numeric_limits<RT>::max();
173 RT m_rtol = RT(0);
174 RT m_atol = RT(0);
175 Vector<RT> m_hh_1d;
176 Vector<RT> m_hes_1d;
177 Table2D<RT> m_hh;
178 Table2D<RT> m_hes;
179 Vector<RT> m_grs;
180 Vector<RT> m_cc;
181 Vector<RT> m_ss;
182 std::unique_ptr<V> m_v_tmp_rhs;
183 std::unique_ptr<V> m_v_tmp_lhs;
184 Vector<V> m_vv;
185 M* m_linop = nullptr;
186};
187
188template <typename V, typename M>
190{
191 allocate_scratch();
192}
193
194template <typename V, typename M>
196{
197 int rs = m_restrtlen;
198
199 m_hh_1d.resize(std::size_t(rs + 2) * (rs + 1));
200 m_hh = Table2D<RT>(m_hh_1d.data(), {0,0}, {rs+1,rs}); // (0:rs+1,0:rs)
201
202 m_hes_1d.resize(std::size_t(rs + 2) * (rs + 1));
203 m_hes = Table2D<RT>(m_hes_1d.data(), {0,0}, {rs+1,rs}); // (0:rs+1,0:rs)
204
205 m_grs.resize(rs + 2);
206 m_cc.resize(rs + 1);
207 m_ss.resize(rs + 1);
208}
209
210template <typename V, typename M>
212{
213 if (m_restrtlen != rl) {
214 m_restrtlen = rl;
215 allocate_scratch();
216 m_vv.clear();
217 }
218}
219
220template <typename V, typename M>
221void GMRES<V,M>::define (M& linop)
222{
223 clear();
224 m_linop = &linop;
225}
226
227template <typename V, typename M>
228void GMRES<V,M>::clear ()
229{
230 m_its = 0;
231 m_status = -1;
232 m_res = std::numeric_limits<RT>::max();
233 m_v_tmp_rhs.reset();
234 m_v_tmp_lhs.reset();
235 m_vv.clear();
236 m_linop = nullptr;
237}
238
239template <typename V, typename M>
240bool GMRES<V,M>::converged (RT r0, RT r) const
241{
242 return (r < r0*m_rtol) || (r < m_atol);
243}
244
245template <typename V, typename M>
246void GMRES<V,M>::solve (V& a_sol, V const& a_rhs, RT a_tol_rel, RT a_tol_abs, int a_its)
247{
248 BL_PROFILE("GMRES::solve()");
249
250 AMREX_ALWAYS_ASSERT(m_linop != nullptr);
251
252 auto t0 = amrex::second();
253
254 if (m_v_tmp_rhs == nullptr) {
255 m_v_tmp_rhs = std::make_unique<V>(m_linop->makeVecRHS());
256 }
257 if (m_v_tmp_lhs == nullptr) {
258 m_v_tmp_lhs = std::make_unique<V>(m_linop->makeVecLHS());
259 }
260 if (m_vv.empty()) {
261 m_vv.reserve(m_restrtlen+1);
262 for (int i = 0; i < 2; ++i) { // to save space, start with just 2
263 m_vv.emplace_back(m_linop->makeVecRHS());
264 }
265 }
266
267 m_rtol = a_tol_rel;
268 m_atol = a_tol_abs;
269
270 if (a_its < 0) { a_its = m_maxiter; }
271
272 auto rnorm0 = RT(0);
273
274 if (m_nonzero_guess) {
275 compute_residual(m_vv[0], a_sol, a_rhs);
276 } else {
277 m_linop->assign(m_vv[0], a_rhs);
278 m_linop->setToZero(a_sol);
279 }
280
281 m_its = 0;
282 m_status = -1;
283 cycle(a_sol, m_status, m_its, rnorm0, a_its);
284
285 while (m_status == -1 && m_its < a_its) {
286 compute_residual(m_vv[0], a_sol, a_rhs);
287 cycle(a_sol, m_status, m_its, rnorm0, a_its);
288 }
289
290 if (m_status == -1 && m_its >= a_its) { m_status = 1; }
291 m_rnorm0 = rnorm0;
292
293 m_v_tmp_rhs.reset();
294 m_v_tmp_lhs.reset();
295 m_vv.clear();
296
297 auto t1 = amrex::second();
298 if (m_verbose > 0) {
299 amrex::Print() << "GMRES: Solve Time = " << t1-t0 << '\n';
300 }
301}
302
303template <typename V, typename M>
304void GMRES<V,M>::cycle (V& a_xx, int& a_status, int& a_itcount, RT& a_rnorm0, int a_maxiter)
305{
306 BL_PROFILE("GMRES::cycle()");
307
308 m_res = m_linop->norm2(m_vv[0]);
309 m_grs[0] = m_res;
310
311 if (m_res == RT(0.0)) {
312 a_status = 0;
313 return;
314 }
315
316 m_linop->scale(m_vv[0], RT(1.0)/m_res);
317
318 if (a_itcount == 0) { a_rnorm0 = m_res; }
319
320 a_status = converged(a_rnorm0,m_res) ? 0 : -1;
321
322 int it = 0;
323 while (it < m_restrtlen && a_itcount < a_maxiter)
324 {
325 if (m_verbose > 1) {
326 amrex::Print() << "GMRES: iter = " << a_itcount
327 << ", residual = " << m_res << ", " << m_res/a_rnorm0
328 << " (rel.)\n";
329 }
330
331 if (a_status == 0) { break; }
332
333 while (m_vv.size() < it+2) {
334 m_vv.emplace_back(m_linop->makeVecRHS());
335 }
336
337 auto const& vv_it = m_vv[it ];
338 auto & vv_it1 = m_vv[it+1];
339
340 m_linop->precond(*m_v_tmp_lhs, vv_it);
341 m_linop->apply(vv_it1, *m_v_tmp_lhs);
342
343 gram_schmidt_orthogonalization(it);
344
345 auto tt = m_linop->norm2(vv_it1);
346
347 auto const sml = RT((sizeof(RT) == 8) ? 1.e-99 : 1.e-30);
348 bool happyend = (tt < sml);
349 if (!happyend) {
350 m_linop->scale(vv_it1, RT(1.0)/tt);
351 }
352
353 m_hh (it+1,it) = tt;
354 m_hes(it+1,it) = tt;
355
356 update_hessenberg(it, happyend, m_res);
357
358 ++it;
359 ++a_itcount;
360 a_status = converged(a_rnorm0, m_res) ? 0 : -1;
361 if (happyend) { break; }
362 }
363
364 if ((m_verbose > 1) && (a_status != 0 || a_itcount >= a_maxiter)) {
365 amrex::Print() << "GMRES: iter = " << a_itcount
366 << ", residual = " << m_res << ", " << m_res/a_rnorm0
367 << " (rel.)\n";
368 }
369
370 build_solution(a_xx, it-1);
371}
372
373template <typename V, typename M>
374void GMRES<V,M>::gram_schmidt_orthogonalization (int const it)
375{
376 // Two unmodified Gram-Schmidt Orthogonalization
377
378 BL_PROFILE("GMRES::GramSchmidt");
379
380 auto& vv_1 = m_vv[it+1];
381
382 Vector<RT> lhh(it+1);
383
384 for (int j = 0; j <= it; ++j) {
385 m_hh (j,it) = RT(0.0);
386 m_hes(j,it) = RT(0.0);
387 }
388
389 for (int ncnt = 0; ncnt < 2 ; ++ncnt)
390 {
391 for (int j = 0; j <= it; ++j) {
392 lhh[j] = m_linop->dotProduct(vv_1, m_vv[j]);
393 }
394
395 for (int j = 0; j <= it; ++j) {
396 m_linop->increment(vv_1, m_vv[j], -lhh[j]);
397 m_hh (j,it) += lhh[j];
398 m_hes(j,it) -= lhh[j];
399 }
400 }
401}
402
403template <typename V, typename M>
404void GMRES<V,M>::update_hessenberg (int it, bool happyend, RT& res)
405{
406 BL_PROFILE("GMRES::update_hessenberg()");
407
408 for (int j = 1; j <= it; ++j) {
409 auto tt = m_hh(j-1,it);
410 m_hh(j-1,it) = m_cc[j-1] * tt + m_ss[j-1] * m_hh(j,it);
411 m_hh(j ,it) = m_cc[j-1] * m_hh(j,it) - m_ss[j-1] * tt;
412 }
413
414 if (!happyend)
415 {
416 auto tt = std::sqrt(m_hh(it,it)*m_hh(it,it) + m_hh(it+1,it)*m_hh(it+1,it));
417 m_cc[it] = m_hh(it ,it) / tt;
418 m_ss[it] = m_hh(it+1,it) / tt;
419 m_grs[it+1] = - (m_ss[it] * m_grs[it]);
420 m_grs[it ] = m_cc[it] * m_grs[it];
421 m_hh(it,it) = m_cc[it] * m_hh(it,it) + m_ss[it] * m_hh(it+1,it);
422 res = std::abs(m_grs[it+1]);
423 }
424 else
425 {
426 res = RT(0.0);
427 }
428}
429
430template <typename V, typename M>
431void GMRES<V,M>::build_solution (V& a_xx, int const it)
432{
433 BL_PROFILE("GMRES:build_solution()");
434
435 if (it < 0) { return; }
436
437 if (m_hh(it,it) != RT(0.0)) {
438 m_grs[it] /= m_hh(it,it);
439 } else {
440 m_grs[it] = RT(0.0);
441 }
442
443 for (int ii = 1; ii <= it; ++ii) {
444 int k = it - ii;
445 auto tt = m_grs[k];
446 for (int j = k+1; j <= it; ++j) {
447 tt -= m_hh(k,j) * m_grs[j];
448 }
449 m_grs[k] = tt / m_hh(k,k);
450 }
451
452 m_linop->setToZero(*m_v_tmp_rhs);
453 for (int ii = 0; ii < it+1; ++ii) {
454 m_linop->increment(*m_v_tmp_rhs, m_vv[ii], m_grs[ii]);
455 }
456
457 m_linop->precond(*m_v_tmp_lhs, *m_v_tmp_rhs);
458 m_linop->increment(a_xx, *m_v_tmp_lhs, RT(1.0));
459}
460
461template <typename V, typename M>
462void GMRES<V,M>::compute_residual (V& a_rr, V const& a_xx, V const& a_bb)
463{
464 BL_PROFILE("GMRES::compute_residual()");
465 m_linop->assign(*m_v_tmp_lhs, a_xx);
466 m_linop->apply(*m_v_tmp_rhs, *m_v_tmp_lhs);
467 m_linop->linComb(a_rr, RT(1.0), a_bb, RT(-1.0), *m_v_tmp_rhs);
468}
469
470}
471#endif
#define BL_PROFILE(a)
Definition AMReX_BLProfiler.H:562
#define AMREX_ALWAYS_ASSERT(EX)
Definition AMReX_BLassert.H:50
GMRES.
Definition AMReX_GMRES.H:87
int getNumIters() const
Number of iterations executed by the last solve().
Definition AMReX_GMRES.H:142
void solve(V &a_sol, V const &a_rhs, RT a_tol_rel, RT a_tol_abs, int a_its=-1)
Solve the linear system.
Definition AMReX_GMRES.H:246
RT getInitialResidualNorm() const
Initial residual 2-norm from the last solve().
Definition AMReX_GMRES.H:151
void define(M &linop)
Bind the solver to a linear operator.
Definition AMReX_GMRES.H:221
void setVerbose(int v)
Set verbosity level v (0 = silent).
Definition AMReX_GMRES.H:125
void setMaxIters(int niters)
Cap the number of iterations performed by solve().
Definition AMReX_GMRES.H:139
void setRestartLength(int rl)
Set the Krylov restart length.
Definition AMReX_GMRES.H:211
RT getResidualNorm() const
Final residual 2-norm from the last solve().
Definition AMReX_GMRES.H:148
GMRES()
Construct a GMRES solver with the default restart length.
Definition AMReX_GMRES.H:189
void setInitialGuessNonzero(bool flag)
Definition AMReX_GMRES.H:122
int getStatus() const
Status flag from the last solve() (0 success, >0 failure).
Definition AMReX_GMRES.H:145
typename M::RT RT
Definition AMReX_GMRES.H:90
This class provides the user with a few print options.
Definition AMReX_Print.H:35
This class is a thin wrapper around std::vector. Unlike vector, Vector::operator[] provides bound che...
Definition AMReX_Vector.H:29
Definition AMReX_Amr.cpp:50
double second() noexcept
Definition AMReX_Utility.cpp:919
Definition AMReX_TableData.H:97