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