FV3 Bundle
PLanczos.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_PLANCZOS_H_
12 #define OOPS_ASSIMILATION_PLANCZOS_H_
13 
14 #include <cmath>
15 #include <vector>
16 
18 #include "oops/util/dot_product.h"
19 #include "oops/util/formats.h"
20 #include "oops/util/Logger.h"
21 
22 namespace oops {
23 
24 /*! \file PLanczos.h
25  * \brief Preconditioned Lanczos solver.
26  *
27  * This algorihtm is based on the standard Preconditioned Lanczos
28  * method for solving the linear system Ax = b
29  *
30  * A must be square and symmetric.
31  *
32  * On entry:
33  * - x = starting point, \f$ x_0 \f$.
34  * - b = right hand side.
35  * - A = \f$ A \f$.
36  * - precond = preconditioner \f$ P_k \f$.
37  *
38  * On exit, x will contain the solution \f$ x \f$
39  *
40  * The return value is the achieved reduction in preconditioned residual norm.
41  *
42  * Iteration will stop if the maximum iteration limit "maxiter" is reached
43  * or if the residual norm reduces by a factor of "tolerance".
44  *
45  * VECTOR must implement:
46  * - dot_product
47  * - operator(=)
48  * - operator(+=),
49  * - operator(-=)
50  * - operator(*=) [double * VECTOR],
51  * - axpy
52  *
53  * Each of AMATRIX and PMATRIX must implement a method:
54  * - void multiply(const VECTOR&, VECTOR&) const
55  *
56  * which applies the matrix to the first argument, and returns the
57  * matrix-vector product in the second. (Note: the const is optional, but
58  * recommended.)
59  */
60 
61 template <typename VECTOR, typename AMATRIX, typename PMATRIX>
62 double PLanczos(VECTOR & xx, const VECTOR & bb,
63  const AMATRIX & A, const PMATRIX & precond,
64  const int maxiter, const double tolerance) {
65  VECTOR zz(xx);
66  VECTOR ww(xx);
67 
68  std::vector<VECTOR> vVEC;
69  std::vector<VECTOR> zVEC;
70  std::vector<double> alphas;
71  std::vector<double> betas;
72  std::vector<double> dd;
73  std::vector<double> yy;
74 
75  // Initial residual r = b - Ax
76  VECTOR rr(bb);
77  double xnrm2 = dot_product(xx, xx);
78  if (xnrm2 != 0) {
79  A.multiply(xx, zz);
80  rr -= zz;
81  }
82 
83  // z = precond r
84  precond.multiply(rr, zz);
85 
86  double normReduction = 1.0;
87  double beta0 = sqrt(dot_product(rr, zz));
88  double beta = 0.0;
89 
90  VECTOR vv(rr);
91  vv *= 1/beta0;
92  zz *= 1/beta0;
93 
94  zVEC.push_back(zz); // zVEC[0] = z_1 ---> required for re-orthogonalization
95  vVEC.push_back(vv); // vVEC[0] = v_1 ---> required for re-orthogonalization
96 
97  int jiter;
98  Log::info() << std::endl;
99  for (jiter = 0; jiter < maxiter; ++jiter) {
100  Log::info() << " PLanczos Starting Iteration " << jiter+1 << std::endl;
101 
102  // w = A z - beta * vold
103  A.multiply(zz, ww); // w = A z
104  if (jiter > 0) ww.axpy(-beta, vVEC[jiter-1]);
105 
106  double alpha = dot_product(zz, ww);
107 
108  ww.axpy(-alpha, vv); // w = w - alpha * v
109 
110  // Re-orthogonalization
111  for (int iiter = 0; iiter < jiter; ++iiter) {
112  double proj = dot_product(ww, zVEC[iiter]);
113  ww.axpy(-proj, vVEC[iiter]);
114  }
115 
116  precond.multiply(ww, zz); // z = precond w
117 
118  beta = sqrt(dot_product(zz, ww));
119 
120  vv = ww;
121  vv *= 1/beta;
122  zz *= 1/beta;
123 
124  zVEC.push_back(zz); // zVEC[jiter+1] = z_jiter
125  vVEC.push_back(vv); // vVEC[jiter+1] = v_jiter
126 
127  alphas.push_back(alpha);
128 
129  if (jiter == 0) {
130  yy.push_back(beta0/alpha);
131  dd.push_back(beta0);
132  } else {
133  // Solve the tridiagonal system T_jiter y_jiter = beta0 * e_1
134  dd.push_back(beta0*dot_product(zVEC[0], vv));
135  TriDiagSolve(alphas, betas, dd, yy);
136  }
137 
138  // Gradient norm in precond metric --> sqrt(r'z) --> beta * y(jiter)
139  double rznorm = beta*std::abs(yy[jiter]);
140 
141  normReduction = rznorm/beta0;
142 
143  betas.push_back(beta);
144 
145  Log::info() << "PLanczos end of iteration " << jiter+1 << ". Norm reduction= "
146  << util::full_precision(normReduction) << std::endl << std::endl;
147 
148  if (normReduction < tolerance) {
149  Log::info() << "PLanczos: Achieved required reduction in residual norm." << std::endl;
150  break;
151  }
152  }
153 
154  // Calculate the solution (xh = Binv x)
155  for (int iiter = 0; iiter < jiter; ++iiter) {
156  xx.axpy(yy[iiter], zVEC[iiter]);
157  }
158 
159  Log::info() << "PLanczos: end" << std::endl;
160 
161  return normReduction;
162 }
163 
164 } // namespace oops
165 
166 #endif // OOPS_ASSIMILATION_PLANCZOS_H_
real, dimension(:,:), allocatable, private bb
Definition: tridiagonal.F90:75
double PLanczos(VECTOR &xx, const VECTOR &bb, const AMATRIX &A, const PMATRIX &precond, const int maxiter, const double tolerance)
Definition: PLanczos.h:62
void TriDiagSolve(const std::vector< double > &diag, const std::vector< double > &sub, const std::vector< double > &rhs, std::vector< double > &sol)
Definition: TriDiagSolve.h:18
The namespace for the main oops code.
subroutine, public info(self)
real(fp), parameter, public tolerance