FV3 Bundle
QNewtonLMP.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_QNEWTONLMP_H_
12 #define OOPS_ASSIMILATION_QNEWTONLMP_H_
13 
14 #include <algorithm>
15 #include <string>
16 #include <vector>
17 
18 #include "eckit/config/LocalConfiguration.h"
19 #include "oops/util/dot_product.h"
20 #include "oops/util/Logger.h"
21 
22 #include <boost/scoped_ptr.hpp>
23 
24 namespace oops {
25 
26  /* The QUASI-NEWTON Limited Memory preconditioner matrix \f$ C \f$
27  * for the system matrix \f$ AB = (I + Ht Rinv H B) \f$
28  *
29  * It is defined as
30  * \f$ C_(k+1) = (I - rho_k ph_k q_k^T) C_k (I - rho_k q_k p_k^T) + rho_k ph_k p_k^T\f$
31  *
32  * For details, we refer to S. Gratton, A. Sartenaer and J. Tshimanga,
33  * SIAM J. Optim.,21(3),912–935,2011 and S. Gurol, PhD Manuscript, 2013.
34  *
35  * The solvers represent matrices as objects that implement a "multiply"
36  * method. This class defines objects that apply the preconditioner matrix \f$ P \f$.
37  */
38 
39 // -----------------------------------------------------------------------------
40 
41 template<typename VECTOR, typename BMATRIX> class QNewtonLMP {
42  public:
43  explicit QNewtonLMP(const eckit::Configuration &);
45 
46  void push(const VECTOR &, const VECTOR &, const VECTOR &, const double &);
47  void update(const BMATRIX & B);
48 
49  void multiply(const VECTOR &, VECTOR &) const;
50 
51  // Matrix-vector products with the transpose of the preconditioner
52  void tmultiply(const VECTOR &, VECTOR &) const;
53 
54  private:
55  unsigned maxpairs_;
56  unsigned maxnewpairs_;
58  int maxouter_;
59  int update_;
60 
61  std::vector<VECTOR> P_;
62  std::vector<VECTOR> Ph_;
63  std::vector<VECTOR> AP_;
64  std::vector<VECTOR> BAP_;
65  std::vector<double> rhos_;
66  std::vector<unsigned> usedpairIndx_;
67 
68  std::vector<VECTOR> savedP_;
69  std::vector<VECTOR> savedPh_;
70  std::vector<VECTOR> savedAP_;
71  std::vector<double> savedrhos_;
72 };
73 
74 // =============================================================================
75 
76 template<typename VECTOR, typename BMATRIX>
77 QNewtonLMP<VECTOR, BMATRIX>::QNewtonLMP(const eckit::Configuration & conf)
78  : maxpairs_(0), maxnewpairs_(0), useoldpairs_(false), maxouter_(0), update_(1)
79 {
80  maxouter_ = conf.getInt("nouter");
81  Log::info() << "QNewtonLMP: maxouter : " << maxouter_ << std::endl;
82 
83  if (conf.has("preconditioner")) {
84  const eckit::LocalConfiguration precond(conf, "preconditioner");
85  if (precond.has("maxpairs")) maxpairs_ = precond.getInt("maxpairs");
86  if (maxpairs_ > 0) {
87  std::string old;
88  if (precond.has("useoldpairs")) old = precond.getString("useoldpairs");
89  useoldpairs_ = (old == "on" || old == "true");
90  if (useoldpairs_ && precond.has("maxnewpairs")) {
91  maxnewpairs_ = precond.getInt("maxnewpairs");
92  maxnewpairs_ = std::min(maxpairs_, maxnewpairs_);
93  } else {
94  maxnewpairs_ = maxpairs_;
95  }
96  }
97  }
98 }
99 
100 // -----------------------------------------------------------------------------
101 
102 template<typename VECTOR, typename BMATRIX>
103 void QNewtonLMP<VECTOR, BMATRIX>::push(const VECTOR & p, const VECTOR & ph,
104  const VECTOR & ap, const double & rho) {
105  ASSERT(savedP_.size() <= maxnewpairs_);
106  if (maxnewpairs_ > 0 && update_ < maxouter_) {
107  if (savedP_.size() == maxnewpairs_) {
108  savedP_.erase(savedP_.begin());
109  savedPh_.erase(savedPh_.begin());
110  savedAP_.erase(savedAP_.begin());
111  savedrhos_.erase(savedrhos_.begin());
112  }
113  savedP_.push_back(p);
114  savedPh_.push_back(ph);
115  savedAP_.push_back(ap);
116  savedrhos_.push_back(1.0/rho);
117  }
118 }
119 
120 // -----------------------------------------------------------------------------
121 
122 template<typename VECTOR, typename BMATRIX>
123 void QNewtonLMP<VECTOR, BMATRIX>::update(const BMATRIX & Bmat) {
124  Log::debug() << "QNewtonLMP size saved Ph = " << savedPh_.size() << std::endl;
125  Log::debug() << "QNewtonLMP size saved P = " << savedP_.size() << std::endl;
126  Log::debug() << "QNewtonLMP size saved AP = " << savedAP_.size() << std::endl;
127  Log::debug() << "QNewtonLMP size saved rhos = " << savedrhos_.size() << std::endl;
128 
129  const unsigned nvec = savedPh_.size();
130  ASSERT(savedP_.size() == nvec);
131  ASSERT(savedAP_.size() == nvec);
132  ASSERT(savedrhos_.size() == nvec);
133 
134  if (!useoldpairs_ || nvec >= maxpairs_) {
135  Ph_.clear();
136  P_.clear();
137  AP_.clear();
138  BAP_.clear();
139  rhos_.clear();
140  for (unsigned kiter = 0; kiter < usedpairIndx_.size(); ++kiter) {
141  usedpairIndx_[kiter] = 0;
142  }
143  }
144 
145  if (nvec > 0 && update_ < maxouter_) {
146  Log::info() << "QNewtonLMP: update " << update_ << ", max = " << maxouter_-1 << std::endl;
147  unsigned newpairs = std::min(nvec, maxpairs_);
148  unsigned oldpairs = P_.size();
149  unsigned rmpairs = 0;
150 // First remove pairs we no longer need.
151  if (oldpairs + newpairs > maxpairs_) {
152  rmpairs = oldpairs + newpairs - maxpairs_;
153  for (unsigned jv = 0; jv < rmpairs; ++jv) {
154  Ph_.erase(Ph_.begin());
155  P_.erase(P_.begin());
156  AP_.erase(AP_.begin());
157  BAP_.erase(BAP_.begin());
158  rhos_.erase(rhos_.begin());
159  }
160  oldpairs -= rmpairs;
161 // Keep information on how many pairs are used at each minimization loop
162  unsigned removed = rmpairs;
163  for (unsigned kiter = 0; kiter < usedpairIndx_.size(); ++kiter) {
164  unsigned rmiter = std::min(usedpairIndx_[kiter], removed);
165  usedpairIndx_[kiter] -= rmiter;
166  removed -= rmiter;
167  }
168  ASSERT(removed == 0);
169  }
170  ASSERT(P_.size() == oldpairs);
171  ASSERT(oldpairs + newpairs <= maxpairs_);
172 
173 // Add the new pairs.
174  for (unsigned jv = nvec - newpairs; jv < nvec; ++jv) {
175  P_.push_back(savedP_[jv]);
176  Ph_.push_back(savedPh_[jv]);
177  AP_.push_back(savedAP_[jv]);
178  rhos_.push_back(savedrhos_[jv]);
179  // Save B*ap
180  VECTOR ww(savedAP_[jv]);
181  Bmat.multiply(savedAP_[jv], ww);
182  BAP_.push_back(ww);
183  }
184  ASSERT(P_.size() == oldpairs + newpairs);
185 
186  Log::info() << "Number of inner iterations : " << nvec << std::endl;
187  Log::info() << "Number of maximum pairs : " << maxpairs_ << std::endl;
188  Log::info() << "Number of used total pairs : " << oldpairs + newpairs << std::endl;
189  Log::info() << "Number of new pairs : " << newpairs << std::endl;
190  Log::info() << "Number of removed old pairs : " << rmpairs << std::endl;
191  for (unsigned kiter = 0; kiter < usedpairIndx_.size(); ++kiter) {
192  Log::info() << "Number of used pairs from outer loop " << kiter + 1
193  << " : " << usedpairIndx_[kiter];
194  }
195  }
196 
197  ++update_;
198  savedP_.clear();
199  savedPh_.clear();
200  savedAP_.clear();
201  savedrhos_.clear();
202 }
203 
204 // -----------------------------------------------------------------------------
205 
206 template<typename VECTOR, typename BMATRIX>
207 void QNewtonLMP<VECTOR, BMATRIX>::multiply(const VECTOR & a, VECTOR & b) const {
208  b = a;
209  const unsigned nvec = P_.size();
210  if (nvec != 0) {
211  std::vector<double> etas;
212  etas.clear();
213  for (int iiter = nvec-1; iiter >= 0; iiter--) {
214  double eta = dot_product(b, P_[iiter]);
215  eta *= rhos_[iiter];
216  etas.push_back(eta);
217  b.axpy(-eta, AP_[iiter]);
218  }
219  b *= dot_product(AP_[nvec-1], AP_[nvec-1])/dot_product(AP_[nvec-1], Ph_[nvec-1]);
220  for (unsigned iiter = 0; iiter < nvec; ++iiter) {
221  double sigma = dot_product(b, BAP_[iiter]);
222  sigma *= rhos_[iiter];
223  sigma -= etas[iiter];
224  b.axpy(-sigma, Ph_[iiter]);
225  }
226  }
227 }
228 
229 // -----------------------------------------------------------------------------
230 
231 template<typename VECTOR, typename BMATRIX>
232 void QNewtonLMP<VECTOR, BMATRIX>::tmultiply(const VECTOR & a, VECTOR & b) const {
233  b = a;
234  const unsigned nvec = P_.size();
235  if (nvec != 0) {
236  std::vector<double> etas;
237  etas.clear();
238  for (int iiter = nvec-1; iiter >= 0; iiter--) {
239  double eta = dot_product(b, Ph_[iiter]);
240  eta *= rhos_[iiter];
241  etas.push_back(eta);
242  b.axpy(-eta, BAP_[iiter]);
243  }
244  b *= dot_product(AP_[nvec-1], AP_[nvec-1])/dot_product(AP_[nvec-1], Ph_[nvec-1]);
245  for (unsigned iiter = 0; iiter < nvec; ++iiter) {
246  double sigma = dot_product(b, AP_[iiter]);
247  sigma *= rhos_[iiter];
248  sigma -= etas[iiter];
249  b.axpy(-sigma, P_[iiter]);
250  }
251  }
252 }
253 
254 // -----------------------------------------------------------------------------
255 
256 } // namespace oops
257 
258 #endif // OOPS_ASSIMILATION_QNEWTONLMP_H_
void push(const VECTOR &, const VECTOR &, const VECTOR &, const double &)
Definition: QNewtonLMP.h:103
std::vector< double > rhos_
Definition: QNewtonLMP.h:65
std::vector< VECTOR > BAP_
Definition: QNewtonLMP.h:64
void update(const BMATRIX &B)
Definition: QNewtonLMP.h:123
std::vector< VECTOR > P_
Definition: QNewtonLMP.h:61
Definition: conf.py:1
unsigned maxnewpairs_
Definition: QNewtonLMP.h:56
QNewtonLMP(const eckit::Configuration &)
Definition: QNewtonLMP.h:77
integer, parameter sigma
Flags to indicate where climatology pressure levels are pressure or sigma levels. ...
real(r8), dimension(cast_m, cast_n) p
integer(long), parameter false
unsigned maxpairs_
Definition: QNewtonLMP.h:55
void tmultiply(const VECTOR &, VECTOR &) const
Definition: QNewtonLMP.h:232
The namespace for the main oops code.
logical debug
Definition: mpp.F90:1297
void multiply(const VECTOR &, VECTOR &) const
Definition: QNewtonLMP.h:207
std::vector< VECTOR > savedP_
Definition: QNewtonLMP.h:68
std::vector< VECTOR > savedAP_
Definition: QNewtonLMP.h:70
subroutine, public multiply(self, geom, xctl, xmod)
std::vector< VECTOR > AP_
Definition: QNewtonLMP.h:63
subroutine, public info(self)
std::vector< double > savedrhos_
Definition: QNewtonLMP.h:71
real(kind_real), parameter eta
************************************************************************GNU Lesser General Public License **This file is part of the GFDL Flexible Modeling System(FMS). ! *! *FMS is free software without even the implied warranty of MERCHANTABILITY or *FITNESS FOR A PARTICULAR PURPOSE See the GNU General Public License *for more details **You should have received a copy of the GNU Lesser General Public *License along with FMS If see< http:! ***********************************************************************!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !! MPP_TRANSMIT !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! subroutine MPP_TRANSMIT_(put_data, put_len, to_pe, get_data, get_len, from_pe, block, tag, recv_request, send_request)!a message-passing routine intended to be reminiscent equally of both MPI and SHMEM!put_data and get_data are contiguous MPP_TYPE_ arrays!at each call, your put_data array is put to to_pe 's get_data! your get_data array is got from from_pe 's put_data!i.e we assume that typically(e.g updating halo regions) each PE performs a put _and_ a get!special PE designations:! NULL_PE:to disable a put or a get(e.g at boundaries)! ANY_PE:if remote PE for the put or get is to be unspecific! ALL_PES:broadcast and collect operations(collect not yet implemented)!ideally we would not pass length, but this f77-style call performs better(arrays passed by address, not descriptor)!further, this permits< length > contiguous words from an array of any rank to be passed(avoiding f90 rank conformance check)!caller is responsible for completion checks(mpp_sync_self) before and after integer, intent(in) ::put_len, to_pe, get_len, from_pe MPP_TYPE_, intent(in) ::put_data(*) MPP_TYPE_, intent(out) ::get_data(*) logical, intent(in), optional ::block integer, intent(in), optional ::tag integer, intent(out), optional ::recv_request, send_request logical ::block_comm integer ::i MPP_TYPE_, allocatable, save ::local_data(:) !local copy used by non-parallel code(no SHMEM or MPI) integer ::comm_tag integer ::rsize if(.NOT.module_is_initialized) call mpp_error(FATAL, 'MPP_TRANSMIT:You must first call mpp_init.') if(to_pe.EQ.NULL_PE .AND. from_pe.EQ.NULL_PE) return block_comm=.true. if(PRESENT(block)) block_comm=block if(debug) then call SYSTEM_CLOCK(tick) write(stdout_unit,'(a, i18, a, i6, a, 2i6, 2i8)')&'T=', tick, ' PE=', pe, ' MPP_TRANSMIT begin:to_pe, from_pe, put_len, get_len=', to_pe, from_pe, put_len, get_len end if comm_tag=DEFAULT_TAG if(present(tag)) comm_tag=tag!do put first and then get if(to_pe.GE.0 .AND. to_pe.LT.npes) then!use non-blocking sends if(debug .and.(current_clock.NE.0)) call SYSTEM_CLOCK(start_tick)!z1l:truly non-blocking send.! if(request(to_pe).NE.MPI_REQUEST_NULL) then !only one message from pe-> to_pe in queue *PE waiting for to_pe ! call error else get_len so only do gets but you cannot have a pure get with MPI call a get means do a wait to ensure put on remote PE is complete error call increase mpp_nml request_multiply call MPP_TRANSMIT get_len end if return end subroutine MPP_TRANSMIT_ ! MPP_BROADCAST ! subroutine but that doesn t allow !broadcast to a subset of PEs This version and mpp_transmit will remain !backward compatible intent(inout) a
std::vector< unsigned > usedpairIndx_
Definition: QNewtonLMP.h:66
#define min(a, b)
Definition: mosaic_util.h:32
std::vector< VECTOR > Ph_
Definition: QNewtonLMP.h:62
std::vector< VECTOR > savedPh_
Definition: QNewtonLMP.h:69