Block-Structured AMR Software Framework
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 
13 namespace amrex {
14 
77 template <typename V, typename M>
78 class GMRES
79 {
80 public:
81 
82  using RT = typename M::RT; // double or float
83 
84  GMRES ();
85 
89  void define (M& linop);
90 
100  void solve (V& a_sol, V const& a_rhs, RT a_tol_rel, RT a_tol_abs, int a_its=-1);
101 
103  void setVerbose (int v) { m_verbose = v; }
104 
106  void setRestartLength (int rl);
107 
109  void setMaxIters (int niters) { m_maxiter = niters; }
110 
112  [[nodiscard]] int getNumIters () const { return m_its; }
113 
115  [[nodiscard]] int getStatus () const { return m_status; }
116 
118  [[nodiscard]] RT getResidualNorm () const { return m_res; }
119 
120 private:
121  void clear ();
123  void cycle (V& a_xx, int& a_status, int& a_itcount, RT& a_rnorm0);
124  void build_solution (V& a_xx, int it);
125  void compute_residual (V& a_rr, V const& a_xx, V const& a_bb);
126 
127  bool converged (RT r0, RT r) const;
128 
130  void update_hessenberg (int it, bool happyend, RT& res);
131 
132  int m_verbose = 0;
133  int m_maxiter = 2000;
134  int m_its = 0;
135  int m_status = -1;
136  int m_restrtlen = 30;
138  RT m_rtol = RT(0);
139  RT m_atol = RT(0);
147  std::unique_ptr<V> m_v_tmp_rhs;
148  std::unique_ptr<V> m_v_tmp_lhs;
150  M* m_linop = nullptr;
151 };
152 
153 template <typename V, typename M>
155 {
156  allocate_scratch();
157 }
158 
159 template <typename V, typename M>
161 {
162  int rs = m_restrtlen;
163 
164  m_hh_1d.resize(std::size_t(rs + 2) * (rs + 1));
165  m_hh = Table2D<RT>(m_hh_1d.data(), {0,0}, {rs+1,rs}); // (0:rs+1,0:rs)
166 
167  m_hes_1d.resize(std::size_t(rs + 2) * (rs + 1));
168  m_hes = Table2D<RT>(m_hes_1d.data(), {0,0}, {rs+1,rs}); // (0:rs+1,0:rs)
169 
170  m_grs.resize(rs + 2);
171  m_cc.resize(rs + 1);
172  m_ss.resize(rs + 1);
173 }
174 
175 template <typename V, typename M>
177 {
178  if (m_restrtlen != rl) {
179  m_restrtlen = rl;
180  allocate_scratch();
181  m_vv.clear();
182  }
183 }
184 
185 template <typename V, typename M>
186 void GMRES<V,M>::define (M& linop)
187 {
188  clear();
189  m_linop = &linop;
190 }
191 
192 template <typename V, typename M>
194 {
195  m_its = 0;
196  m_status = -1;
198  m_v_tmp_rhs.reset();
199  m_v_tmp_lhs.reset();
200  m_vv.clear();
201  m_linop = nullptr;
202 }
203 
204 template <typename V, typename M>
205 bool GMRES<V,M>::converged (RT r0, RT r) const
206 {
207  return (r < r0*m_rtol) || (r < m_atol);
208 }
209 
210 template <typename V, typename M>
211 void GMRES<V,M>::solve (V& a_sol, V const& a_rhs, RT a_tol_rel, RT a_tol_abs, int a_its)
212 {
213  BL_PROFILE("GMRES::solve()");
214 
215  AMREX_ALWAYS_ASSERT(m_linop != nullptr);
216 
217  auto t0 = amrex::second();
218 
219  if (m_v_tmp_rhs == nullptr) {
220  m_v_tmp_rhs = std::make_unique<V>(m_linop->makeVecRHS());
221  }
222  if (m_v_tmp_lhs == nullptr) {
223  m_v_tmp_lhs = std::make_unique<V>(m_linop->makeVecLHS());
224  }
225  if (m_vv.empty()) {
226  m_vv.reserve(m_restrtlen+1);
227  for (int i = 0; i < 2; ++i) { // to save space, start with just 2
228  m_vv.emplace_back(m_linop->makeVecRHS());
229  }
230  }
231 
232  m_rtol = a_tol_rel;
233  m_atol = a_tol_abs;
234 
235  if (a_its < 0) { a_its = m_maxiter; }
236 
237  auto rnorm0 = RT(0);
238 
239  m_linop->assign(m_vv[0], a_rhs);
240  m_linop->setToZero(a_sol);
241 
242  m_its = 0;
243  m_status = -1;
244  cycle(a_sol, m_status, m_its, rnorm0);
245 
246  while (m_status == -1 && m_its < a_its) {
247  compute_residual(m_vv[0], a_sol, a_rhs);
248  cycle(a_sol, m_status, m_its, rnorm0);
249  }
250 
251  if (m_status == -1 && m_its >= a_its) { m_status = 1; }
252 
253  m_v_tmp_rhs.reset();
254  m_v_tmp_lhs.reset();
255  m_vv.clear();
256 
257  auto t1 = amrex::second();
258  if (m_verbose > 0) {
259  amrex::Print() << "GMRES: Solve Time = " << t1-t0 << '\n';
260  }
261 }
262 
263 template <typename V, typename M>
264 void GMRES<V,M>::cycle (V& a_xx, int& a_status, int& a_itcount, RT& a_rnorm0)
265 {
266  BL_PROFILE("GMREA::cycle()");
267 
268  m_res = m_linop->norm2(m_vv[0]);
269  m_grs[0] = m_res;
270 
271  if (m_res == RT(0.0)) {
272  a_status = 0;
273  return;
274  }
275 
276  m_linop->scale(m_vv[0], RT(1.0)/m_res);
277 
278  if (a_itcount == 0) { a_rnorm0 = m_res; }
279 
280  a_status = converged(a_rnorm0,m_res) ? 0 : -1;
281 
282  int it = 0;
283  while (it < m_restrtlen && a_itcount < m_maxiter)
284  {
285  if (m_verbose > 1) {
286  amrex::Print() << "GMRES: iter = " << a_itcount
287  << ", residual = " << m_res << ", " << m_res/a_rnorm0
288  << " (rel.)\n";
289  }
290 
291  if (a_status == 0) { break; }
292 
293  while (m_vv.size() < it+2) {
294  m_vv.emplace_back(m_linop->makeVecRHS());
295  }
296 
297  auto const& vv_it = m_vv[it ];
298  auto & vv_it1 = m_vv[it+1];
299 
300  m_linop->precond(*m_v_tmp_lhs, vv_it);
301  m_linop->apply(vv_it1, *m_v_tmp_lhs);
302 
303  gram_schmidt_orthogonalization(it);
304 
305  auto tt = m_linop->norm2(vv_it1);
306 
307  auto const small = RT((sizeof(RT) == 8) ? 1.e-99 : 1.e-30);
308  bool happyend = (tt < small);
309  if (!happyend) {
310  m_linop->scale(vv_it1, RT(1.0)/tt);
311  }
312 
313  m_hh (it+1,it) = tt;
314  m_hes(it+1,it) = tt;
315 
316  update_hessenberg(it, happyend, m_res);
317 
318  ++it;
319  ++a_itcount;
320  a_status = converged(a_rnorm0, m_res) ? 0 : -1;
321  if (happyend) { break; }
322  }
323 
324  if ((m_verbose > 1) && (a_status != 0 || a_itcount >= m_maxiter)) {
325  amrex::Print() << "GMRES: iter = " << a_itcount
326  << ", residual = " << m_res << ", " << m_res/a_rnorm0
327  << " (rel.)\n";
328  }
329 
330  build_solution(a_xx, it-1);
331 }
332 
333 template <typename V, typename M>
335 {
336  // Two unmodified Gram-Schmidt Orthogonalization
337 
338  BL_PROFILE("GMRES::GramSchmidt");
339 
340  auto& vv_1 = m_vv[it+1];
341 
342  Vector<RT> lhh(it+1);
343 
344  for (int j = 0; j <= it; ++j) {
345  m_hh (j,it) = RT(0.0);
346  m_hes(j,it) = RT(0.0);
347  }
348 
349  for (int ncnt = 0; ncnt < 2 ; ++ncnt)
350  {
351  for (int j = 0; j <= it; ++j) {
352  lhh[j] = m_linop->dotProduct(vv_1, m_vv[j]);
353  }
354 
355  for (int j = 0; j <= it; ++j) {
356  m_linop->increment(vv_1, m_vv[j], -lhh[j]);
357  m_hh (j,it) += lhh[j];
358  m_hes(j,it) -= lhh[j];
359  }
360  }
361 }
362 
363 template <typename V, typename M>
364 void GMRES<V,M>::update_hessenberg (int it, bool happyend, RT& res)
365 {
366  BL_PROFILE("GMRES::update_hessenberg()");
367 
368  for (int j = 1; j <= it; ++j) {
369  auto tt = m_hh(j-1,it);
370  m_hh(j-1,it) = m_cc[j-1] * tt + m_ss[j-1] * m_hh(j,it);
371  m_hh(j ,it) = m_cc[j-1] * m_hh(j,it) - m_ss[j-1] * tt;
372  }
373 
374  if (!happyend)
375  {
376  auto tt = std::sqrt(m_hh(it,it)*m_hh(it,it) + m_hh(it+1,it)*m_hh(it+1,it));
377  m_cc[it] = m_hh(it ,it) / tt;
378  m_ss[it] = m_hh(it+1,it) / tt;
379  m_grs[it+1] = - (m_ss[it] * m_grs[it]);
380  m_grs[it ] = m_cc[it] * m_grs[it];
381  m_hh(it,it) = m_cc[it] * m_hh(it,it) + m_ss[it] * m_hh(it+1,it);
382  res = std::abs(m_grs[it+1]);
383  }
384  else
385  {
386  res = RT(0.0);
387  }
388 }
389 
390 template <typename V, typename M>
391 void GMRES<V,M>::build_solution (V& a_xx, int const it)
392 {
393  BL_PROFILE("GMRES:build_solution()");
394 
395  if (it < 0) { return; }
396 
397  if (m_hh(it,it) != RT(0.0)) {
398  m_grs[it] /= m_hh(it,it);
399  } else {
400  m_grs[it] = RT(0.0);
401  }
402 
403  for (int ii = 1; ii <= it; ++ii) {
404  int k = it - ii;
405  auto tt = m_grs[k];
406  for (int j = k+1; j <= it; ++j) {
407  tt -= m_hh(k,j) * m_grs[j];
408  }
409  m_grs[k] = tt / m_hh(k,k);
410  }
411 
412  m_linop->setToZero(*m_v_tmp_rhs);
413  for (int ii = 0; ii < it+1; ++ii) {
414  m_linop->increment(*m_v_tmp_rhs, m_vv[ii], m_grs[ii]);
415  }
416 
417  m_linop->precond(*m_v_tmp_lhs, *m_v_tmp_rhs);
418  m_linop->increment(a_xx, *m_v_tmp_lhs, RT(1.0));
419 }
420 
421 template <typename V, typename M>
422 void GMRES<V,M>::compute_residual (V& a_rr, V const& a_xx, V const& a_bb)
423 {
424  BL_PROFILE("GMRES::compute_residual()");
425  m_linop->assign(*m_v_tmp_lhs, a_xx);
426  m_linop->apply(*m_v_tmp_rhs, *m_v_tmp_lhs);
427  m_linop->linComb(a_rr, RT(1.0), a_bb, RT(-1.0), *m_v_tmp_rhs);
428 }
429 
430 }
431 #endif
#define BL_PROFILE(a)
Definition: AMReX_BLProfiler.H:551
#define AMREX_ALWAYS_ASSERT(EX)
Definition: AMReX_BLassert.H:50
GMRES.
Definition: AMReX_GMRES.H:79
int getNumIters() const
Gets the number of iterations.
Definition: AMReX_GMRES.H:112
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:211
int m_status
Definition: AMReX_GMRES.H:135
void build_solution(V &a_xx, int it)
Definition: AMReX_GMRES.H:391
void allocate_scratch()
Definition: AMReX_GMRES.H:160
int m_verbose
Definition: AMReX_GMRES.H:132
void define(M &linop)
Definition: AMReX_GMRES.H:186
RT m_rtol
Definition: AMReX_GMRES.H:138
bool converged(RT r0, RT r) const
Definition: AMReX_GMRES.H:205
Vector< RT > m_hh_1d
Definition: AMReX_GMRES.H:140
RT m_atol
Definition: AMReX_GMRES.H:139
int m_maxiter
Definition: AMReX_GMRES.H:133
void cycle(V &a_xx, int &a_status, int &a_itcount, RT &a_rnorm0)
Definition: AMReX_GMRES.H:264
Table2D< RT > m_hes
Definition: AMReX_GMRES.H:143
Table2D< RT > m_hh
Definition: AMReX_GMRES.H:142
void setVerbose(int v)
Sets verbosity.
Definition: AMReX_GMRES.H:103
void compute_residual(V &a_rr, V const &a_xx, V const &a_bb)
Definition: AMReX_GMRES.H:422
Vector< RT > m_cc
Definition: AMReX_GMRES.H:145
M * m_linop
Definition: AMReX_GMRES.H:150
int m_its
Definition: AMReX_GMRES.H:134
int m_restrtlen
Definition: AMReX_GMRES.H:136
void setMaxIters(int niters)
Sets the max number of iterations.
Definition: AMReX_GMRES.H:109
std::unique_ptr< V > m_v_tmp_lhs
Definition: AMReX_GMRES.H:148
Vector< RT > m_hes_1d
Definition: AMReX_GMRES.H:141
void setRestartLength(int rl)
Sets restart length. The default is 30.
Definition: AMReX_GMRES.H:176
void update_hessenberg(int it, bool happyend, RT &res)
Definition: AMReX_GMRES.H:364
void gram_schmidt_orthogonalization(int it)
Definition: AMReX_GMRES.H:334
RT getResidualNorm() const
Gets the 2-norm of the residual.
Definition: AMReX_GMRES.H:118
GMRES()
Definition: AMReX_GMRES.H:154
Vector< V > m_vv
Definition: AMReX_GMRES.H:149
Vector< RT > m_grs
Definition: AMReX_GMRES.H:144
Vector< RT > m_ss
Definition: AMReX_GMRES.H:146
void clear()
Definition: AMReX_GMRES.H:193
int getStatus() const
Gets the solver status.
Definition: AMReX_GMRES.H:115
std::unique_ptr< V > m_v_tmp_rhs
Definition: AMReX_GMRES.H:147
RT m_res
Definition: AMReX_GMRES.H:137
typename M::RT RT
Definition: AMReX_GMRES.H:82
This class provides the user with a few print options.
Definition: AMReX_Print.H:35
@ max
Definition: AMReX_ParallelReduce.H:17
static constexpr int M
Definition: AMReX_OpenBC.H:13
Definition: AMReX_Amr.cpp:49
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE T abs(const GpuComplex< T > &a_z) noexcept
Return the absolute value of a complex number.
Definition: AMReX_GpuComplex.H:356
double second() noexcept
Definition: AMReX_Utility.cpp:922
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE GpuComplex< T > sqrt(const GpuComplex< T > &a_z) noexcept
Return the square root of a complex number.
Definition: AMReX_GpuComplex.H:373