OOPS
GETKFSolver.h
Go to the documentation of this file.
1 /*
2  * (C) Copyright 2020 UCAR.
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  */
7 
8 #ifndef OOPS_ASSIMILATION_GETKFSOLVER_H_
9 #define OOPS_ASSIMILATION_GETKFSOLVER_H_
10 
11 #include <Eigen/Dense>
12 #include <cfloat>
13 #include <string>
14 #include <vector>
15 
16 #include "eckit/config/LocalConfiguration.h"
21 #include "oops/base/Departures.h"
25 #include "oops/base/ObsEnsemble.h"
26 #include "oops/base/ObsErrors.h"
27 #include "oops/base/Observations.h"
28 #include "oops/base/ObsSpaces.h"
33 #include "oops/util/Logger.h"
34 #include "oops/util/Timer.h"
35 
36 namespace oops {
37 
38 /*!
39  * An implementation of the GETKF from Lei 2018 JAMES
40  *
41  * Lei, L., Whitaker, J. S., & Bishop, C. ( 2018). Improving assimilation
42  * of radiance observations by implementing model space localization in an
43  * ensemble Kalman filter. Journal of Advances in Modeling Earth Systems, 10,
44  * 3221– 3232. https://doi.org/10.1029/2018MS001468
45  */
46 template <typename MODEL, typename OBS>
47 class GETKFSolver : public LocalEnsembleSolver<MODEL, OBS> {
60 
61  public:
62  static const std::string classname() {return "oops::GETKFSolver";}
63 
64  /// Constructor (allocates Wa, wa, HZb_,
65  /// saves options from the config, computes VerticalLocEV_)
66  GETKFSolver(ObsSpaces_ &, const Geometry_ &, const eckit::Configuration &, size_t);
67 
68  Observations_ computeHofX(const StateEnsemble4D_ &, size_t, bool) override;
69 
70  /// entire KF update (computeWeights+applyWeights) for a grid point GeometryIterator_
72  IncrementEnsemble4D_ &) override;
73 
74  private:
75  /// Computes weights
76  void computeWeights(const Departures_ &, const DeparturesEnsemble_ &,
77  const DeparturesEnsemble_ &, const ObsErrors_ &);
78 
79  /// Applies weights and adds posterior inflation
81  const GeometryIterator_ &);
82 
83  private:
85 
86  // parameters
87  size_t nens_;
90  size_t neig_;
91  size_t nanal_;
92 
94 
95  Eigen::MatrixXd Wa_; // transformation matrix for ens. perts. Xa_=Xf*Wa
96  Eigen::VectorXd wa_; // transformation matrix for ens. mean xa_=xf*wa
97 };
98 
99 // -----------------------------------------------------------------------------
100 
101 template <typename MODEL, typename OBS>
103  const eckit::Configuration & config, size_t nens)
104  : LocalEnsembleSolver<MODEL, OBS>(obspaces, geometry, config, nens),
105  nens_(nens), geometry_(geometry),
106  vertloc_(geometry_, config.getSubConfiguration("local ensemble DA.vertical localization")),
107  neig_(vertloc_.neig()), nanal_(neig_*nens_), HZb_(obspaces, nanal_)
108 {
109  options_.deserialize(config);
110  const LETKFInflationParameters & inflopt = options_.infl;
111 
112  // parse inflation options
113  Log::info() << "Multiplicative inflation multCoeff=" <<
114  inflopt.mult << std::endl;
115 
116  if (inflopt.dortpp()) {
117  Log::info() << "RTPP inflation will be applied with rtppCoeff=" <<
118  inflopt.rtpp << std::endl;
119  } else {
120  Log::info() << "RTPP inflation is not applied rtppCoeff is out of bounds (0,1], rtppCoeff="
121  << inflopt.rtpp << std::endl;
122  }
123 
124  if (inflopt.dortps()) {
125  Log::info() << "RTPS inflation will be applied with rtpsCoeff=" <<
126  inflopt.rtps << std::endl;
127  } else {
128  Log::info() << "RTPS inflation is not applied rtpsCoeff is out of bounds (0,1], rtpsCoeff="
129  << inflopt.rtps << std::endl;
130  }
131 
132  // pre-allocate transformation matrices
133  Wa_.resize(nanal_, nens);
134  wa_.resize(nanal_);
135 }
136 
137 // -----------------------------------------------------------------------------
138 template <typename MODEL, typename OBS>
140  size_t iteration, bool readFromFile) {
141  util::Timer timer(classname(), "computeHofX");
142 
143  // compute/read H(x) for the original ensemble members
144  // also computes omb_
145  Observations_ yb_mean =
146  LocalEnsembleSolver<MODEL, OBS>::computeHofX(ens_xx, iteration, readFromFile);
147  if (readFromFile) {
148  Log::debug() << "Read H(X) from disk" << std::endl;
149  // read modulated ensemble
150  Observations_ ytmp(yb_mean);
151  size_t ii = 0;
152  for (size_t iens = 0; iens < nens_; ++iens) {
153  for (size_t ieig = 0; ieig < neig_; ++ieig) {
154  ytmp.read("hofxm"+std::to_string(iteration)+"_"+std::to_string(ieig+1)+
155  "_"+std::to_string(iens+1));
156  HZb_[ii] = ytmp - yb_mean;
157  Log::test() << "H(Zx) - ymean for member " << iens+1 << " eig "<< ieig+1
158  << " :" << std::endl << HZb_[ii] << std::endl;
159  ii = ii + 1;
160  }
161  }
162  } else {
163  Log::debug() << "Computing H(X) online" << std::endl;
164  // modulate ensemble of obs
165  State4D_ xx_mean(ens_xx.mean());
166  IncrementEnsemble4D_ dx(ens_xx, xx_mean, xx_mean[0].variables());
167  IncrementEnsemble4D_ Ztmp(geometry_, xx_mean[0].variables(), ens_xx[0].validTimes(), neig_);
168  size_t ii = 0;
169  for (size_t iens = 0; iens < nens_; ++iens) {
170  vertloc_.modulateIncrement(dx[iens], Ztmp);
171  for (size_t ieig = 0; ieig < neig_; ++ieig) {
172  State4D_ tmpState = xx_mean;
173  tmpState += Ztmp[ieig];
174  Observations_ tmpObs = this->hofx_.compute(tmpState);
175  HZb_[ii] = tmpObs - yb_mean;
176  tmpObs.save("hofxm"+std::to_string(iteration)+"_"+std::to_string(ieig+1)+
177  "_"+std::to_string(iens+1));
178  Log::test() << "H(Zx) - ymean for member " << iens+1 << " eig "<< ieig+1 << " :"
179  << std::endl << HZb_[ii] << std::endl;
180  ii = ii + 1;
181  }
182  }
183  }
184  return yb_mean;
185 }
186 
187 // -----------------------------------------------------------------------------
188 
189 template <typename MODEL, typename OBS>
191  const DeparturesEnsemble_ & Yb,
192  const DeparturesEnsemble_ & YbOrig,
193  const ObsErrors_ & R) {
194  // compute transformation matrix, save in Wa_, wa_
195  // Yb(nobs,neig*nens), YbOrig(nobs,nens)
196  // uses GSI GETKF code
197  util::Timer timer(classname(), "computeWeights");
198 
199  const int nobsl = dy.nobs();
200 
201  // cast oops objects to eigen<double> obejects
202  // then cast eigen<doble> to eigen<float>
203  Eigen::MatrixXd edy = dy.packEigen();
204  Eigen::MatrixXf edy_f = edy.cast<float>();
205 
206  Eigen::MatrixXd eYb = Yb.packEigen();
207  Eigen::MatrixXf eYb_f = eYb.cast<float>();
208 
209  Eigen::MatrixXd eYb2 = YbOrig.packEigen();
210  Eigen::MatrixXf eYb2_f = eYb2.cast<float>();
211 
212  Eigen::MatrixXd eR = R.packInverseVarianceEigen();
213  Eigen::MatrixXf eR_f = eR.cast<float>();
214 
215  Eigen::MatrixXf Wa_f(this->nanal_, this->nens_);
216  Eigen::VectorXf wa_f(this->nanal_);
217 
218  // call into GSI interface to compute Wa and wa
219  const int getkf_inflation = 0;
220  const int denkf = 0;
221  const int getkf = 1;
222  letkf_core_f90(nobsl, eYb_f.data(), eYb2_f.data(), edy_f.data(),
223  wa_f.data(), Wa_f.data(),
224  eR_f.data(), nanal_, neig_,
225  getkf_inflation, denkf, getkf);
226  this->Wa_ = Wa_f.cast<double>();
227  this->wa_ = wa_f.cast<double>();
228 }
229 
230 // -----------------------------------------------------------------------------
231 
232 template <typename MODEL, typename OBS>
234  IncrementEnsemble4D_ & ana_pert,
235  const GeometryIterator_ & i) {
236  // apply Wa_, wa_
237  util::Timer timer(classname(), "applyWeights");
238 
239  const LETKFInflationParameters & inflopt = options_.infl;
240 
241  // allocate tmp arrays
242  LocalIncrement gptmpl = bkg_pert[0][0].getLocal(i);
243  std::vector<double> tmp = gptmpl.getVals();
244  size_t ngp = tmp.size();
245  Eigen::MatrixXd Xb(ngp, nens_);
246 
247  // loop through analysis times and ens. members
248  for (unsigned itime=0; itime < bkg_pert[0].size(); ++itime) {
249  // cast bkg_pert ensemble at grid point i as an Eigen matrix Xb
250  // modulates Xb
251  Xb = vertloc_.modulateIncrement(bkg_pert, i, itime);
252 
253  // postmulptiply
254  Eigen::VectorXd xa = Xb*wa_; // ensemble mean update
255  Eigen::MatrixXd Xa = Xb*Wa_; // ensemble perturbation update
256 
257  // compute non-modulated Xb for RTPP and RTPS
258  if (inflopt.dortpp() || inflopt.dortps()) {
259  Xb.resize(ngp, nens_);
260  for (size_t iens=0; iens < nens_; ++iens) {
261  LocalIncrement gp = bkg_pert[iens][itime].getLocal(i);
262  std::vector<double> tmp1 = gp.getVals();
263  for (size_t iv=0; iv < ngp; ++iv) {
264  Xb(iv, iens) = tmp1[iv];
265  }
266  }
267  }
268 
269  // RTPP inflation
270  if (inflopt.dortpp()) {
271  Xa = (1-inflopt.rtpp)*Xa+inflopt.rtpp*Xb;
272  }
273 
274  // RTPS inflation
275  double eps = DBL_EPSILON;
276  if (inflopt.dortps()) {
277  // posterior spread
278  Eigen::ArrayXd asprd = Xa.array().square().rowwise().sum()/(nens_-1);
279  asprd = asprd.sqrt();
280  asprd = (asprd < eps).select(eps, asprd); // avoid nan overflow for vars with no spread
281 
282  // prior spread
283  Eigen::ArrayXd fsprd = Xb.array().square().rowwise().sum()/(nens_-1);
284  fsprd = fsprd.sqrt();
285  fsprd = (fsprd < eps).select(eps, fsprd);
286 
287  // rtps inflation factor
288  Eigen::ArrayXd rtpsInfl = inflopt.rtps*((fsprd-asprd)/asprd) + 1;
289  rtpsInfl = (rtpsInfl < inflopt.rtpsInflMin()).select(inflopt.rtpsInflMin(), rtpsInfl);
290  rtpsInfl = (rtpsInfl > inflopt.rtpsInflMax()).select(inflopt.rtpsInflMax(), rtpsInfl);
291 
292  // inlfate perturbation matrix
293  Xa.array().colwise() *= rtpsInfl;
294  }
295 
296  // assign Xa_ to ana_pert
297  for (size_t iens=0; iens < nens_; ++iens) {
298  for (size_t iv=0; iv < ngp; ++iv) {
299  tmp[iv] = Xa(iv, iens)+xa(iv);
300  }
301  gptmpl.setVals(tmp);
302  ana_pert[iens][itime].setLocal(gptmpl, i);
303  }
304  }
305 }
306 
307 // -----------------------------------------------------------------------------
308 
309 template <typename MODEL, typename OBS>
311  const GeometryIterator_ & i,
312  IncrementEnsemble4D_ & ana_pert) {
313  util::Timer timer(classname(), "measurementUpdate");
314 
315  // create the local subset of observations
316  ObsSpaces_ local_obs(this->obspaces_, *i, this->obsconf_);
317  Departures_ local_omb(local_obs, this->omb_);
318 
319  if (local_omb.nobs() == 0) {
320  // no obs. so no need to update Wa_ and wa_
321  // ana_pert[i]=bkg_pert[i]
322  this->copyLocalIncrement(bkg_pert, i, ana_pert);
323  } else {
324  // if obs are present do normal KF update
325  DeparturesEnsemble_ local_Yb(local_obs, this->Yb_);
326  DeparturesEnsemble_ local_HZ(local_obs, HZb_);
327  // create local obs errors
328  ObsErrors_ local_R(this->obsconf_, local_obs);
329  computeWeights(local_omb, local_HZ, local_Yb, local_R);
330  applyWeights(bkg_pert, ana_pert, i);
331  }
332 }
333 
334 } // namespace oops
335 #endif // OOPS_ASSIMILATION_GETKFSOLVER_H_
oops::LETKFInflationParameters::dortps
bool dortps() const
Definition: LETKFSolverParameters.h:46
oops::GETKFSolver::options_
LETKFSolverParameters options_
Definition: GETKFSolver.h:84
oops
The namespace for the main oops code.
Definition: ErrorCovarianceL95.cc:22
oops::DeparturesEnsemble::packEigen
Eigen::MatrixXd packEigen() const
pack ensemble of dep. as contiguous block of memory
Definition: oops/base/DeparturesEnsemble.h:73
oops::LocalIncrement::setVals
void setVals(std::vector< double > &)
Definition: LocalIncrement.cc:38
oops::LETKFInflationParameters::dortpp
bool dortpp() const
Definition: LETKFSolverParameters.h:45
oops::GeometryIterator
Definition: oops/interface/GeometryIterator.h:32
DeparturesEnsemble.h
oops::LETKFInflationParameters::mult
Parameter< double > mult
Definition: LETKFSolverParameters.h:22
oops::LETKFInflationParameters::rtpsInflMax
double rtpsInflMax() const
Definition: LETKFSolverParameters.h:43
StateEnsemble4D.h
oops::GETKFSolver::ObsEnsemble_
ObsEnsemble< OBS > ObsEnsemble_
Definition: GETKFSolver.h:53
oops::LETKFInflationParameters::rtpp
Parameter< double > rtpp
Definition: LETKFSolverParameters.h:31
oops::LETKFSolverParameters
LETKF parameters.
Definition: LETKFSolverParameters.h:50
ObsSpaces.h
oops::GETKFSolver::Geometry_
Geometry< MODEL > Geometry_
Definition: GETKFSolver.h:50
oops::GETKFSolver::geometry_
const Geometry_ & geometry_
Definition: GETKFSolver.h:88
oops::GETKFSolver::Wa_
Eigen::MatrixXd Wa_
Definition: GETKFSolver.h:95
LocalIncrement.h
oops::ObsErrors
\biref Container for ObsErrors for all observation types that are used in DA
Definition: ObsErrors.h:33
oops::VerticalLocEV
Definition: oops/generic/VerticalLocEV.h:59
VerticalLocEV.h
oops::GETKFSolver::IncrementEnsemble4D_
IncrementEnsemble4D< MODEL > IncrementEnsemble4D_
Definition: GETKFSolver.h:52
oops::GETKFSolver::ObsErrors_
ObsErrors< OBS > ObsErrors_
Definition: GETKFSolver.h:54
oops::GETKFSolver
An implementation of the GETKF from Lei 2018 JAMES.
Definition: GETKFSolver.h:47
oops::GETKFSolver::Departures_
Departures< OBS > Departures_
Definition: GETKFSolver.h:48
oops::LETKFSolverParameters::infl
Parameter< LETKFInflationParameters > infl
Definition: LETKFSolverParameters.h:53
Departures.h
ObsEnsemble.h
oops::GETKFSolver::GeometryIterator_
GeometryIterator< MODEL > GeometryIterator_
Definition: GETKFSolver.h:51
Observations.h
oops::GETKFSolver::ObsSpaces_
ObsSpaces< OBS > ObsSpaces_
Definition: GETKFSolver.h:56
oops::LocalEnsembleSolver
Base class for LETKF-type solvers.
Definition: LocalEnsembleSolver.h:34
ObsErrors.h
oops::LETKFInflationParameters
Parameters for LETKF inflation.
Definition: LETKFSolverParameters.h:17
LETKFSolverParameters.h
LocalEnsembleSolver.h
oops::Departures::packEigen
Eigen::VectorXd packEigen() const
Pack departures in an Eigen vector (excluding departures that are masked out)
Definition: oops/base/Departures.h:214
oops::Departures
Difference between two observation vectors.
Definition: oops/base/Departures.h:44
gletkfInterface.h
oops::LETKFInflationParameters::rtps
Parameter< double > rtps
Definition: LETKFSolverParameters.h:38
oops::GETKFSolver::neig_
size_t neig_
Definition: GETKFSolver.h:90
oops::LocalEnsembleSolver::computeHofX
virtual Observations_ computeHofX(const StateEnsemble4D_ &xx, size_t iteration, bool readFromDisk)
computes ensemble H(xx), returns mean H(xx), saves as hofx iteration
Definition: LocalEnsembleSolver.h:90
oops::GETKFSolver::State4D_
State4D< MODEL > State4D_
Definition: GETKFSolver.h:57
oops::IncrementEnsemble4D::size
unsigned int size() const
Accessors.
Definition: IncrementEnsemble4D.h:61
oops::IncrementEnsemble4D
Ensemble of 4D inrements.
Definition: IncrementEnsemble4D.h:37
oops::Observations::read
void read(const std::string &)
Definition: Observations.h:160
oops::State4D
Four dimensional state.
Definition: State4D.h:35
IncrementEnsemble4D.h
oops::StateEnsemble4D::mean
State4D_ mean() const
calculate ensemble mean
Definition: StateEnsemble4D.h:68
oops::LocalIncrement
Definition: LocalIncrement.h:19
oops::Geometry
Geometry class used in oops; subclass of interface class above.
Definition: oops/interface/Geometry.h:189
oops::ObsEnsemble
Ensemble of observations (can hold ensemble of H(x))
Definition: ObsEnsemble.h:21
oops::GETKFSolver::nens_
size_t nens_
Definition: GETKFSolver.h:87
oops::GETKFSolver::classname
static const std::string classname()
Definition: GETKFSolver.h:62
oops::Departures::nobs
size_t nobs() const
Return number of departures (excluding departures that are masked out)
Definition: oops/base/Departures.h:198
oops::GETKFSolver::wa_
Eigen::VectorXd wa_
Definition: GETKFSolver.h:96
oops::GETKFSolver::computeWeights
void computeWeights(const Departures_ &, const DeparturesEnsemble_ &, const DeparturesEnsemble_ &, const ObsErrors_ &)
Computes weights.
Definition: GETKFSolver.h:190
oops::GETKFSolver::computeHofX
Observations_ computeHofX(const StateEnsemble4D_ &, size_t, bool) override
computes ensemble H(xx), returns mean H(xx), saves as hofx iteration
Definition: GETKFSolver.h:139
oops::DeparturesEnsemble
Ensemble of Departures (can hold ensemble perturbations in the observation space)
Definition: oops/base/DeparturesEnsemble.h:23
State4D.h
oops::Observations
Observations Class.
Definition: oops/base/Departures.h:30
oops::GETKFSolver::applyWeights
void applyWeights(const IncrementEnsemble4D_ &, IncrementEnsemble4D_ &, const GeometryIterator_ &)
Applies weights and adds posterior inflation.
Definition: GETKFSolver.h:233
oops::GETKFSolver::VerticalLocEV_
VerticalLocEV< MODEL > VerticalLocEV_
Definition: GETKFSolver.h:59
oops::GETKFSolver::GETKFSolver
GETKFSolver(ObsSpaces_ &, const Geometry_ &, const eckit::Configuration &, size_t)
Definition: GETKFSolver.h:102
oops::StateEnsemble4D
Ensemble of 4D states.
Definition: StateEnsemble4D.h:26
GeometryIterator.h
oops::GETKFSolver::Observations_
Observations< OBS > Observations_
Definition: GETKFSolver.h:55
oops::ObsSpaces
Definition: ObsSpaces.h:41
oops::GETKFSolver::measurementUpdate
void measurementUpdate(const IncrementEnsemble4D_ &, const GeometryIterator_ &, IncrementEnsemble4D_ &) override
entire KF update (computeWeights+applyWeights) for a grid point GeometryIterator_
Definition: GETKFSolver.h:310
oops::GETKFSolver::nanal_
size_t nanal_
Definition: GETKFSolver.h:91
oops::GETKFSolver::DeparturesEnsemble_
DeparturesEnsemble< OBS > DeparturesEnsemble_
Definition: GETKFSolver.h:49
oops::letkf_core_f90
void letkf_core_f90(const int &, const float *, const float *, const float *, float *, float *, const float *, const int &, const int &, const int &, const int &, const int &)
oops::GETKFSolver::vertloc_
VerticalLocEV_ vertloc_
Definition: GETKFSolver.h:89
oops::LocalIncrement::getVals
const std::vector< double > & getVals() const
Definition: LocalIncrement.h:27
oops::ObsErrors::packInverseVarianceEigen
Eigen::VectorXd packInverseVarianceEigen() const
Definition: ObsErrors.h:108
oops::LETKFInflationParameters::rtpsInflMin
double rtpsInflMin() const
Definition: LETKFSolverParameters.h:42
oops::GETKFSolver::StateEnsemble4D_
StateEnsemble4D< MODEL > StateEnsemble4D_
Definition: GETKFSolver.h:58
Geometry.h
oops::GETKFSolver::HZb_
DeparturesEnsemble_ HZb_
Definition: GETKFSolver.h:93
oops::Observations::save
void save(const std::string &) const
Save/read observations values.
Definition: Observations.h:153