FV3 Bundle
PCG.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_PCG_H_
12 #define OOPS_ASSIMILATION_PCG_H_
13 
14 #include <cmath>
15 #include <vector>
16 
17 #include "oops/util/dot_product.h"
18 #include "oops/util/formats.h"
19 #include "oops/util/Logger.h"
20 
21 namespace oops {
22 
23 /*! \file PCG.h
24  * \brief Preconditioned Conjugate Gradients solver.
25  *
26  * This solver is based on the standard Preconditioned Conjugate
27  * Gradients solver for Ax=b
28  *
29  * A must be square, symmetric, positive definite.
30  * A preconditioner must be supplied that, given a vector q, returns an
31  * approximate solution of Ap=q.
32  *
33  * On entry:
34  * - x = starting point, \f$ x_0 \f$.
35  * - b = right hand side.
36  * - A = \f$ A \f$.
37  * - precond = preconditioner \f$ P \approx (A)^{-1} \f$.
38  *
39  * On exit, x will contain the solution \f$ x \f$
40 
41  * The return value is the achieved reduction in preconditioned residual norm.
42  *
43  * Iteration will stop if the maximum iteration limit "maxiter" is reached
44  * or if the preconditioned residual norm reduces by a factor of "tolerance".
45  *
46  * VECTOR must implement:
47  * - dot_product
48  * - operator(=)
49  * - operator(+=),
50  * - operator(-=)
51  * - operator(*=) [double * VECTOR],
52  * - axpy
53  *
54  * Each of AMATRIX and PMATRIX must implement a method:
55  * - void multiply(const VECTOR&, VECTOR&) const
56  *
57  * which applies the matrix to the first argument, and returns the
58  * matrix-vector product in the second. (Note: the const is optional, but
59  * recommended.)
60  */
61 
62 template <typename VECTOR, typename AMATRIX, typename PMATRIX>
63 double PCG(VECTOR & x, const VECTOR & b,
64  const AMATRIX & A, const PMATRIX & precond,
65  const int maxiter, const double tolerance ) {
66  VECTOR ap(x);
67  VECTOR p(x);
68  VECTOR r(x);
69  VECTOR s(x);
70  VECTOR v(x);
71  VECTOR z(x);
72 
73  std::vector<VECTOR> vVEC; // required for re-orthogonalization
74  std::vector<VECTOR> zVEC; // required for re-orthogonalization
75 
76  // Initial residual r = b - Ax
77  r = b;
78  double xnrm2 = dot_product(x, x);
79  if (xnrm2 != 0) {
80  A.multiply(x, s);
81  r -= s;
82  }
83 
84  // s = precond r
85  precond.multiply(r, s);
86 
87  double dotRr0 = dot_product(r, s);
88  double normReduction = 1.0;
89  double rdots = dotRr0;
90  double rdots_old = 0.0;
91 
92  v = r;
93  v *= 1/sqrt(dotRr0);
94  z = s;
95  z *= 1/sqrt(dotRr0);
96 
97  vVEC.push_back(v);
98  zVEC.push_back(z);
99 
100  Log::info() << std::endl;
101  for (int jiter = 0; jiter < maxiter; ++jiter) {
102  Log::info() << " PCG Starting Iteration " << jiter+1 << std::endl;
103 
104  if (jiter == 0) {
105  p = s;
106  } else {
107  double beta = dot_product(s, r)/rdots_old;
108  Log::debug() << "PCG beta = " << beta << std::endl;
109 
110  p *= beta;
111  p += s; // p = s + beta*p
112  }
113 
114  A.multiply(p, ap); // ap = A*p
115 
116  double alpha = rdots/dot_product(p, ap);
117 
118  x.axpy(alpha, p); // x = x + alpha*p
119  r.axpy(-alpha, ap); // r = r - alpha*ap
120 
121  // Re-orthogonalization
122  for (int iiter = 0; iiter < jiter; ++iiter) {
123  double proj = dot_product(r, zVEC[iiter]);
124  r.axpy(-proj, vVEC[iiter]);
125  }
126 
127  precond.multiply(r, s);
128 
129  rdots_old = rdots;
130  rdots = dot_product(r, s);
131 
132  v = r;
133  v *= 1/sqrt(rdots);
134  z = s;
135  z *= 1/sqrt(rdots);
136  vVEC.push_back(v);
137  zVEC.push_back(z);
138 
139  normReduction = sqrt(rdots/dotRr0);
140 
141  Log::info() << "PCG end of iteration " << jiter+1 << ". Norm reduction= "
142  << util::full_precision(normReduction) << std::endl << std::endl;
143 
144  if (normReduction < tolerance) {
145  Log::info() << "PCG: Achieved required reduction in residual norm." << std::endl;
146  break;
147  }
148  }
149 
150  Log::info() << "PCG: end" << std::endl;
151 
152  return normReduction;
153 }
154 
155 } // namespace oops
156 
157 #endif // OOPS_ASSIMILATION_PCG_H_
real(r8), dimension(cast_m, cast_n) p
The namespace for the main oops code.
logical debug
Definition: mpp.F90:1297
subroutine, public info(self)
real(fp), parameter, public tolerance
double PCG(VECTOR &x, const VECTOR &b, const AMATRIX &A, const PMATRIX &precond, const int maxiter, const double tolerance)
Definition: PCG.h:63