FV3 Bundle
DRPFOMMinimizer.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_DRPFOMMINIMIZER_H_
12 #define OOPS_ASSIMILATION_DRPFOMMINIMIZER_H_
13 
14 #include <cmath>
15 #include <string>
16 #include <vector>
17 
18 #include <boost/ptr_container/ptr_vector.hpp>
19 
27 #include "oops/util/dot_product.h"
28 #include "oops/util/formats.h"
29 #include "oops/util/Logger.h"
30 
31 namespace oops {
32 
33 /// DRPFOM Minimizer
34 /*!
35  * \brief Preconditioned Full Orthogonal Method (FOM) solver.
36  *
37  * This solver is the generalization of the Lanczos method to the
38  * unsymmetric case.
39  * It solves \f$ Ax=b\f$ for the particular case \f$ A=B^{-1}+C\f$,
40  * without requiring the application of \f$ B^{-1}\f$.
41  *
42  * On entry:
43  * - dx = starting point.
44  * - dxh = starting point, \f$ B^{-1} dx_{0}\f$.
45  * - rr = residual at starting point.
46  * - B = \f$ B \f$.
47  * - C = \f$ C \f$.
48  * - precond = preconditioner \f$ F_k \approx (AB)^{-1} \f$.
49  *
50  * On exit, dxh will contain \f$ B^{-1} x\f$ where x is the solution.
51  * The return value is the achieved reduction in residual norm.
52  *
53  * Iteration will stop if the maximum iteration limit "maxiter" is reached
54  * or if the residual norm reduces by a factor of "tolerance".
55  *
56  * Each matrix must implement a method:
57  * - void multiply(const VECTOR&, VECTOR&) const
58  *
59  * which applies the matrix to the first argument, and returns the
60  * matrix-vector product in the second. (Note: the const is optional, but
61  * recommended.)
62  *
63  */
64 
65 // -----------------------------------------------------------------------------
66 
67 template<typename MODEL> class DRPFOMMinimizer : public DRMinimizer<MODEL> {
72 
73  public:
74  const std::string classname() const override {return "DRPFOMMinimizer";}
75  DRPFOMMinimizer(const eckit::Configuration &, const CostFct_ &);
77 
78  private:
79  double solve(CtrlInc_ &, CtrlInc_ &, CtrlInc_ &, const Bmat_ &, const HtRinvH_ &,
80  const double, const double, const int, const double) override;
81 
83  // !!!!! Needs to be generalized for Hessenberg Matrix.
84 
85  boost::ptr_vector<CtrlInc_> hvecs_;
86  boost::ptr_vector<CtrlInc_> vvecs_;
87  boost::ptr_vector<CtrlInc_> zvecs_;
88  std::vector<double> alphas_;
89  std::vector<double> betas_;
90 };
91 
92 // =============================================================================
93 
94 template<typename MODEL>
95 DRPFOMMinimizer<MODEL>::DRPFOMMinimizer(const eckit::Configuration & conf,
96  const CostFct_ & J)
97  : DRMinimizer<MODEL>(J), lmp_(conf),
98  hvecs_(), vvecs_(), zvecs_(), alphas_(), betas_() {}
99 
100 // -----------------------------------------------------------------------------
101 
102 template<typename MODEL>
104  const Bmat_ & B, const HtRinvH_ & HtRinvH,
105  const double costJ0Jb, const double costJ0JoJc,
106  const int maxiter, const double tolerance) {
107  // dx increment
108  // dxh B^{-1} dx
109  // rr (sum B^{-1} dx_i^{b} +) G^T R^{-1} d
110 
111  CtrlInc_ zz(dxh);
112  CtrlInc_ pr(dxh);
113  CtrlInc_ vv(rr);
114 
115  std::vector<double> ss;
116  std::vector<double> dd;
117  std::vector< std::vector<double> > Hess;
118  std::vector< std::vector<double> > UpHess;
119 
120  // J0
121  const double costJ0 = costJ0Jb + costJ0JoJc;
122 
123  // lmp_.update(vvecs_, hvecs_, zvecs_, alphas_, betas_);
124  hvecs_.clear();
125  zvecs_.clear();
126  vvecs_.clear();
127  // !!!!! Needs to be generalized for Hessenberg Matrix.
128 
129  // z_{0} = B LMP r_{0}
130  // lmp_.multiply(vv, pr);
131  pr = vv;
132  B.multiply(pr, zz);
133 
134  // beta_{0} = sqrt( z_{0}^T r_{0} )
135  double beta = sqrt(dot_product(zz, vv));
136  const double beta0 = beta;
137 
138  // v_{1} = r_{0} / beta_{0}
139  vv *= 1/beta;
140  // pr_{1} = LMP r_{0} / beta_{0}
141  pr *= 1/beta;
142  // z_{1} = z_{0} / beta_{0}
143  zz *= 1/beta;
144 
145  // hvecs[0] = pr_{1} --> required for solution
146  hvecs_.push_back(new CtrlInc_(pr));
147  // zvecs[0] = z_{1} ---> for re-orthogonalization
148  zvecs_.push_back(new CtrlInc_(zz));
149  // vvecs[0] = v_{1} ---> for re-orthogonalization
150  vvecs_.push_back(new CtrlInc_(vv));
151 
152  // Initialiaze (maxiter + 1) by maxiter matrix H
153  Hess.resize(maxiter);
154  for (int ii = 0; ii <= maxiter-1; ii++) {
155  Hess[ii].resize(maxiter + 1);
156  for (int jj = 0; jj <= maxiter; jj++) {
157  Hess[ii][jj] = 0;
158  }
159  }
160 
161  double normReduction = 1.0;
162 
163  Log::info() << std::endl;
164  for (int jiter = 0; jiter < maxiter; ++jiter) {
165  Log::info() << "DRPFOM Starting Iteration " << jiter+1 << std::endl;
166 
167  // v_{i+1} = ( pr_{i} + H^T R^{-1} H z_{i} )
168  HtRinvH.multiply(zz, vv);
169  vv += pr;
170  // Arnoldi Process
171  for (int jj = 0; jj <= jiter; ++jj) {
172  Hess[jiter][jj] = dot_product(zvecs_[jj], vv);
173  vv.axpy(-Hess[jiter][jj], vvecs_[jj]);
174  }
175 
176  // z_{i+1} = B LMP v_{i+1}
177  // lmp_.multiply(vv, pr);
178  pr = vv;
179  B.multiply(pr, zz);
180 
181  // beta_{i+1} = sqrt( zz_{i+1}^t, vv_{i+1} )
182  beta = sqrt(dot_product(zz, vv));
183 
184  Hess[jiter][jiter+1] = beta;
185 
186  // v_{i+1} = v_{i+1} / beta_{i+1}
187  vv *= 1/beta;
188  // pr_{i+1} = pr_{i+1} / beta_{i+1}
189  pr *= 1/beta;
190  // z_{i+1} = z_{i+1} / beta_{i+1}
191  zz *= 1/beta;
192 
193  // hvecs[i+1] =pr_{i+1}
194  hvecs_.push_back(new CtrlInc_(pr));
195  // zvecs[i+1] = z_{i+1}
196  zvecs_.push_back(new CtrlInc_(zz));
197  // vvecs[i+1] = v_{i+1}
198  vvecs_.push_back(new CtrlInc_(vv));
199 
200  if (jiter == 0) {
201  ss.push_back(beta0/Hess[0][0]);
202  dd.push_back(beta0);
203  } else {
204  // Solve the upper Hessenberg system H_{i} s_{i} = beta0 * e_1
205  dd.push_back(beta0*dot_product(zvecs_[0], vv));
206  UpHess = Hess;
207  UpHess.resize(jiter+1);
208  for (int ii = 0; ii <= jiter; ii++) {
209  UpHess[ii].resize(jiter+1);
210  for (int jj = 0; jj <= jiter; jj++) {
211  UpHess[ii][jj] = Hess[ii][jj];
212  }
213  }
214  UpHessSolve(UpHess, dd, ss);
215  }
216 
217  betas_.push_back(beta);
218 
219  // Compute the quadratic cost function
220  // J[du_{i}] = J[0] - 0.5 s_{i}^T Z_{i}^T r_{0}
221  // Jb[du_{i}] = 0.5 s_{i}^T V_{i}^T Z_{i} s_{i}
222  double costJ = costJ0;
223  double costJb = costJ0Jb;
224  for (int jj = 0; jj < jiter+1; ++jj) {
225  costJ -= 0.5 * ss[jj] * dot_product(zvecs_[jj], rr);
226  costJb += 0.5 * ss[jj] * dot_product(vvecs_[jj], zvecs_[jj]) * ss[jj];
227  }
228  double costJoJc = costJ - costJb;
229 
230  // Gradient norm in precond metric --> sqrt(r'z) --> beta * s_{i}
231  double rznorm = beta*std::abs(ss[jiter]);
232  normReduction = rznorm/beta0;
233 
234  Log::info() << "DRPFOM end of iteration " << jiter+1 << std::endl
235  << " Norm reduction (" << std::setw(2) << jiter+1 << ") = "
236  << util::full_precision(normReduction) << std::endl
237  << " Quadratic cost function: J (" << std::setw(2) << jiter+1 << ") = "
238  << util::full_precision(costJ) << std::endl
239  << " Quadratic cost function: Jb (" << std::setw(2) << jiter+1 << ") = "
240  << util::full_precision(costJb) << std::endl
241  << " Quadratic cost function: JoJc(" << std::setw(2) << jiter+1 << ") = "
242  << util::full_precision(costJoJc) << std::endl << std::endl;
243 
244  if (normReduction < tolerance) {
245  Log::info() << "DRPFOM: Achieved required reduction in residual norm." << std::endl;
246  break;
247  }
248  }
249 
250  // Calculate the solution (dxh = Binv dx)
251  for (unsigned int jj = 0; jj < ss.size(); ++jj) {
252  dx.axpy(ss[jj], zvecs_[jj]);
253  dxh.axpy(ss[jj], hvecs_[jj]);
254  }
255 
256  return normReduction;
257 }
258 
259 // -----------------------------------------------------------------------------
260 
261 } // namespace oops
262 
263 #endif // OOPS_ASSIMILATION_DRPFOMMINIMIZER_H_
boost::ptr_vector< CtrlInc_ > vvecs_
const std::string classname() const override
ControlIncrement< MODEL > CtrlInc_
real(double_kind), dimension(numint), parameter pr
Definition: mpp_efp.F90:47
Definition: conf.py:1
DRPFOM Minimizer.
double solve(CtrlInc_ &, CtrlInc_ &, CtrlInc_ &, const Bmat_ &, const HtRinvH_ &, const double, const double, const int, const double) override
Cost Function.
Definition: CostFunction.h:56
std::vector< double > betas_
The solvers represent matrices as objects that implement a "multiply" method.
Definition: SpectralLMP.h:51
The namespace for the main oops code.
void multiply(const CtrlInc_ &dx, CtrlInc_ &dz) const
Definition: HtRinvHMatrix.h:64
void UpHessSolve(std::vector< std::vector< double > > &UpHess, const std::vector< double > &rhs, std::vector< double > &sol)
Definition: UpHessSolve.h:28
boost::ptr_vector< CtrlInc_ > zvecs_
subroutine, public info(self)
real(fp), parameter, public tolerance
std::vector< double > alphas_
HtRinvHMatrix< MODEL > HtRinvH_
The matrix.
Definition: BMatrix.h:27
SpectralLMP< CtrlInc_ > lmp_
boost::ptr_vector< CtrlInc_ > hvecs_
BMatrix< MODEL > Bmat_
DRPFOMMinimizer(const eckit::Configuration &, const CostFct_ &)
DR (Derber and Rosati) Minimizers.
Definition: DRMinimizer.h:44
void axpy(const double, const ControlIncrement &)
void multiply(const CtrlInc_ &x, CtrlInc_ &bx) const
Definition: BMatrix.h:33
CostFunction< MODEL > CostFct_