FV3 Bundle
FGMRES.h
Go to the documentation of this file.
1 /*
2  * (C) Copyright 2009-2016 ECMWF.
3  *
4  * This software is licensed under the terms of the Apache Licence Version 2.0
5  * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
6  * In applying this licence, ECMWF does not waive the privileges and immunities
7  * granted to it by virtue of its status as an intergovernmental organisation nor
8  * does it submit to any jurisdiction.
9  */
10 
11 #ifndef OOPS_ASSIMILATION_FGMRES_H_
12 #define OOPS_ASSIMILATION_FGMRES_H_
13 
14 #include <cmath>
15 #include <iostream>
16 #include <vector>
17 
20 #include "oops/util/dot_product.h"
21 #include "oops/util/formats.h"
22 #include "oops/util/Logger.h"
23 
24 namespace oops {
25 
26 /*! \file FGMRES.h
27  * \brief FGMRES solver for Ax=b.
28  *
29  * Flexible GMRES solver for Ax=b.(based on implementation following
30  * Youcef Saad,SIAM Journal on Scientific Computing, Volume 14 Issue 2,
31  * March 1993)
32 
33  * A must be square, but need not be symmetric.
34  * A preconditioner must be supplied that, given a
35  * vector q, returns an approximate solution of Ap=q.
36  *
37  * On entry:
38  * - x = starting point, \f$ X_0 \f$.
39  * - b = right hand side.
40  * - A = \f$ A \f$.
41  * - precond = preconditioner \f$ F_k \approx (A)^{-1} \f$.
42  * - maxiter = maximum number of iterations
43  * - tol = error tolerance
44  *
45  * On exit, x will contain the solution.
46  * The return value is the achieved reduction in residual norm.
47  *
48  * Iteration will stop if the maximum iteration limit "maxiter" is reached
49  * or if the residual norm reduces by a factor of "tolerance".
50  *
51  * VECTOR must implement:
52  * - dot_product
53  * - operator(=)
54  * - operator(+=),
55  * - operator(-=)
56  * - operator(*=) [double * VECTOR],
57  * - axpy
58  *
59  * AMATRIX and PMATRIX must implement a method:
60  * - void multiply(const VECTOR&, VECTOR&) const
61  *
62  * which applies the matrix to the first argument, and returns the
63  * matrix-vector product in the second. (Note: the const is optional, but
64  * recommended.)
65  */
66 
67 template <typename VECTOR, typename AMATRIX, typename PMATRIX>
68 double FGMRES(VECTOR & x, const VECTOR & b,
69  const AMATRIX & A, const PMATRIX & precond,
70  const int maxiter, const double tolerance) {
71  std::vector<VECTOR> V;
72  std::vector<VECTOR> Z;
73  std::vector< std::vector<double> > H;
74  std::vector<double> cs(maxiter+1, 0);
75  std::vector<double> sn(maxiter+1, 0);
76  std::vector<double> s;
77  std::vector<double> y(maxiter+1, 0);
78 
79  VECTOR r(x);
80  VECTOR z(x);
81  VECTOR work(x);
82  VECTOR work2(x);
83  VECTOR w(x);
84  VECTOR xinit(x);
85 
86  r = b;
87  double xnrm2 = dot_product(x, x);
88  if (xnrm2 != 0) {
89  A.multiply(x, work);
90  r -= work; // r = b - Ax
91  }
92 
93  double bnrm2 = sqrt(dot_product(b, b));
94  double rnrm2 = sqrt(dot_product(r, r));
95  double normReduction = rnrm2 / bnrm2;
96 
97  // Initialiaze (maxiter + 1) by maxiter matrix H
98  H.resize(maxiter);
99  for (int ii = 0; ii <= maxiter-1; ii++) {
100  H[ii].resize(maxiter + 1);
101  for (int jj = 0; jj <= maxiter; jj++) {
102  H[ii][jj] = 0;
103  }
104  }
105  work = r;
106  work *= 1/rnrm2;
107  V.push_back(work);
108  s.push_back(rnrm2);
109 
110  int jiter;
111  // FGMRES iteration
112  Log::info() << std::endl;
113  for (jiter = 0; jiter < maxiter; ++jiter) {
114  Log::info() << " FGMRES Starting Iteration " << jiter+1 << std::endl;
115 
116  precond.multiply(V[jiter], z);
117  Z.push_back(z);
118  A.multiply(z, w);
119 
120  double avnrm2 = sqrt(dot_product(w, w));
121 
122  // Modified Gram-Schmidt
123  for (int jj = 0; jj <= jiter; ++jj) {
124  H[jiter][jj] = dot_product(V[jj], w);
125  w.axpy(-H[jiter][jj], V[jj]);
126  }
127  H[jiter][jiter+1] = sqrt(dot_product(w, w));
128  double av2nrm2 = H[jiter][jiter+1];
129 
130  // Re-orthogonalize if necessary
131  if (avnrm2 + 0.001*av2nrm2 == avnrm2) {
132  for (int jj = 0; jj <= jiter; ++jj) {
133  double hr = dot_product(V[jj], w);
134  H[jiter][jj] += hr;
135  w.axpy(-hr, V[jj]);
136  }
137  H[jiter][jiter+1] = sqrt(dot_product(w, w));
138  }
139 
140  // Check breakdown
141  if (H[jiter][jiter+1] != 0) {
142  w *= 1/H[jiter][jiter+1];
143  }
144  V.push_back(w);
145 
146  if (jiter > 0) {
147  // Apply Givens Rotation
148  for (int jj = 0; jj < jiter; ++jj) {
149  double temp = cs[jj]*H[jiter][jj] + sn[jj]*H[jiter][jj+1];
150  H[jiter][jj+1] = -sn[jj]*H[jiter][jj] + cs[jj]*H[jiter][jj+1];
151  H[jiter][jj] = temp;
152  }
153  }
154 
155  // Compute Givens rotation matrix parameters
156  rotmat(H[jiter][jiter], H[jiter][jiter+1], cs[jiter], sn[jiter]);
157 
158  H[jiter][jiter] = cs[jiter]*H[jiter][jiter] + sn[jiter]*H[jiter][jiter+1];
159  H[jiter][jiter+1] = 0.0;
160 
161  double temp = cs[jiter]*s[jiter];
162  s.push_back(-sn[jiter]*s[jiter]);
163  s[jiter] = temp;
164 
165  // Calculate the solution
166  UpTriSolve(H, s, y, jiter); // H s = y
167  x = xinit;
168  for (int jj = 0; jj < jiter+1; ++jj) {
169  x.axpy(y[jj], Z[jj]);
170  }
171 
172  normReduction = std::abs(s[jiter+1])/bnrm2;
173  Log::info() << "FGMRES end of iteration " << jiter+1 << ". PNorm reduction= "
174  << util::full_precision(normReduction) << std::endl << std::endl;
175 
176  if (normReduction <= tolerance) {
177  Log::info() << "FGMRES: Achieved required reduction in residual norm." << std::endl;
178  jiter += 1;
179  break;
180  }
181  }
182 
183  // Calculate the solution
184  UpTriSolve(H, s, y, jiter); // H s = y
185  x = xinit;
186  for (int jj = 0; jj < jiter; ++jj) {
187  x.axpy(y[jj], Z[jj]);
188  }
189 
190  Log::info() << "FGMRES: end" << std::endl;
191 
192  return normReduction;
193 }
194 
195 } // namespace oops
196 
197 #endif // OOPS_ASSIMILATION_FGMRES_H_
Compute the Givens rotation matrix parameters.
void rotmat(const double &a, const double &b, double &c, double &s)
Definition: rotmat.h:22
double FGMRES(VECTOR &x, const VECTOR &b, const AMATRIX &A, const PMATRIX &precond, const int maxiter, const double tolerance)
Definition: FGMRES.h:68
Solves the upper triangular system sol = H \ rhs.
The namespace for the main oops code.
subroutine, public info(self)
real(fp), parameter, public tolerance
void UpTriSolve(const std::vector< std::vector< double > > &H, const std::vector< double > &rhs, std::vector< double > &sol, const int &dim)
Definition: UpTriSolve.h:22