IODA Bundle
test/interface/LinearModel.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 TEST_INTERFACE_LINEARMODEL_H_
12 #define TEST_INTERFACE_LINEARMODEL_H_
13 
14 #include <algorithm>
15 #include <cmath>
16 #include <iomanip>
17 #include <iostream>
18 #include <limits>
19 #include <memory>
20 #include <string>
21 #include <vector>
22 
23 #define ECKIT_TESTING_SELF_REGISTER_CASES 0
24 
25 #include <boost/noncopyable.hpp>
26 
27 #include "eckit/config/LocalConfiguration.h"
28 #include "eckit/testing/Test.h"
29 #include "oops/base/Geometry.h"
30 #include "oops/base/Increment.h"
32 #include "oops/base/Model.h"
36 #include "oops/base/State.h"
38 #include "oops/base/Variables.h"
43 #include "oops/mpi/mpi.h"
44 #include "oops/runs/Test.h"
45 #include "oops/util/DateTime.h"
46 #include "oops/util/dot_product.h"
47 #include "oops/util/Duration.h"
48 #include "test/TestEnvironment.h"
49 
50 namespace test {
51 
52 // =============================================================================
53 
54 template <typename MODEL> class LinearModelFixture : private boost::noncopyable {
63 
64  public:
65  static const eckit::Configuration & test() {return *getInstance().test_;}
66  static const Geometry_ & resol() {return *getInstance().resol_;}
67  static const oops::Variables & ctlvars() {return *getInstance().ctlvars_;}
68  static const util::DateTime & time() {return *getInstance().time_;}
69  static const Covariance_ & covariance() {return *getInstance().B_;}
70  static const Model_ & model() {return *getInstance().model_;}
71  static const State_ & xref() {return *getInstance().xref_;}
72  static const ModelAux_ & bias() {return *getInstance().bias_;}
73  static const ModelAuxIncr_ & dbias() {return *getInstance().dbias_;}
74  static const LinearModel_ & tlm() {return *getInstance().tlm_;}
75  static void reset() {
76  getInstance().tlm_.reset();
77  getInstance().B_.reset();
78  getInstance().time_.reset();
79  getInstance().xref_.reset();
80  getInstance().model_.reset();
81  getInstance().dbias_.reset();
82  getInstance().bias_.reset();
83  getInstance().ctlvars_.reset();
84  getInstance().resol_.reset();
85  getInstance().test_.reset();
86  }
87 
88  private:
90  static LinearModelFixture<MODEL> theLinearModelFixture;
91  return theLinearModelFixture;
92  }
93 
95  test_.reset(new eckit::LocalConfiguration(TestEnvironment::config(), "linear model test"));
96  const util::Duration len(test_->getString("forecast length"));
97 
98  const eckit::LocalConfiguration resolConfig(TestEnvironment::config(), "geometry");
99  resol_.reset(new Geometry_(resolConfig, oops::mpi::world()));
100 
101  ctlvars_.reset(new oops::Variables(TestEnvironment::config(), "analysis variables"));
102 
103  const eckit::LocalConfiguration biasConf(TestEnvironment::config(), "model aux control");
104  bias_.reset(new ModelAux_(*resol_, biasConf));
105 
106  const eckit::LocalConfiguration dbiasConf =
107  TestEnvironment::config().getSubConfiguration("model aux error");
108  dbias_.reset(new ModelAuxIncr_(*resol_, dbiasConf));
109 
110  const eckit::LocalConfiguration nlConf(TestEnvironment::config(), "model");
111  model_.reset(new Model_(*resol_, nlConf));
112 
113  const eckit::LocalConfiguration iniConf(TestEnvironment::config(), "initial condition");
114  xref_.reset(new State_(*resol_, iniConf));
115  time_.reset(new util::DateTime(xref_->validTime()));
116 
117 // Create a covariance matrix
118  oops::instantiateCovarFactory<MODEL>();
119  const eckit::LocalConfiguration covar(TestEnvironment::config(), "background error");
121 
122 // Linear model configuration
123  tlConf_.reset(new eckit::LocalConfiguration(TestEnvironment::config(), "linear model"));
124 
125 // Setup trajectory for TL and AD
126  oops::instantiateTlmFactory<MODEL>();
129  tlm_.reset(new LinearModel_(*resol_, *tlConf_));
131  State_ xx(*xref_);
132  model_->forecast(xx, *bias_, len, post);
133  }
134 
135  ~LinearModelFixture<MODEL>() {}
136 
137  std::unique_ptr<const eckit::LocalConfiguration> test_;
138  std::unique_ptr<const eckit::LocalConfiguration> tlConf_;
139  std::unique_ptr<const Geometry_> resol_;
140  std::unique_ptr<const util::DateTime> time_;
141  std::unique_ptr<const oops::Variables> ctlvars_;
142  std::unique_ptr<const State_> xref_;
143  std::unique_ptr<const Model_> model_;
144  std::unique_ptr<const ModelAux_> bias_;
145  std::unique_ptr<const ModelAuxIncr_> dbias_;
146  std::unique_ptr<const Covariance_> B_;
147  std::shared_ptr<LinearModel_> tlm_;
148 };
149 
150 // =============================================================================
151 
152 /// \brief tests constructor, timeResolution() method and print method
153 template <typename MODEL> void testLinearModelConstructor() {
154  typedef LinearModelFixture<MODEL> Test_;
155 
156  const util::Duration zero(0);
157  EXPECT(Test_::tlm().timeResolution() > zero);
158  oops::Log::test() << "Testing LinearModel: " << Test_::tlm() << std::endl;
159 }
160 
161 // -----------------------------------------------------------------------------
162 
163 template <typename MODEL> void testLinearModelZeroLength() {
164  typedef LinearModelFixture<MODEL> Test_;
165  typedef oops::Increment<MODEL> Increment_;
166  typedef oops::ModelAuxIncrement<MODEL> ModelAuxIncr_;
167 
168  const util::DateTime vt(Test_::time());
169  const util::Duration zero(0);
170 
171  Increment_ dxref(Test_::resol(), Test_::ctlvars(), vt);
172  Test_::covariance().randomize(dxref);
173  ModelAuxIncr_ daux(Test_::dbias());
174  const double ininorm = dxref.norm();
175  EXPECT(ininorm > 0.0);
176 
177  Increment_ dx(Test_::resol(), Test_::ctlvars(), vt);
178  Increment_ dxm(Test_::resol(), Test_::tlm().variables(), vt);
179  dxm = dxref;
180  Test_::tlm().forecastTL(dxm, daux, zero);
181  dx = dxm;
182  EXPECT(dx.validTime() == vt);
183  EXPECT(dx.norm() == ininorm);
184 
185  dxm.zero();
186  dxm = dxref;
187  Test_::tlm().forecastAD(dxm, daux, zero);
188  dx = dxm;
189  EXPECT(dx.validTime() == vt);
190  EXPECT(dx.norm() == ininorm);
191 }
192 
193 // -----------------------------------------------------------------------------
194 
195 template <typename MODEL> void testLinearModelZeroPert() {
196  typedef LinearModelFixture<MODEL> Test_;
197  typedef oops::Increment<MODEL> Increment_;
198  typedef oops::ModelAuxIncrement<MODEL> ModelAuxIncr_;
199 
200  const util::Duration len(Test_::test().getString("forecast length"));
201  const util::DateTime t1(Test_::time());
202  const util::DateTime t2(t1 + len);
203  EXPECT(t2 > t1);
204 
205  Increment_ dx(Test_::resol(), Test_::tlm().variables(), t1);
206  ModelAuxIncr_ daux(Test_::dbias());
207 
208  dx.zero();
209  daux.zero();
210  EXPECT(dx.norm() == 0.0);
211  Test_::tlm().forecastTL(dx, daux, len);
212  EXPECT(dx.validTime() == t2);
213  EXPECT(dx.norm() == 0.0);
214 
215  dx.zero();
216  daux.zero();
217  EXPECT(dx.norm() == 0.0);
218  Test_::tlm().forecastAD(dx, daux, len);
219  EXPECT(dx.validTime() == t1);
220  EXPECT(dx.norm() == 0.0);
221 }
222 
223 // -----------------------------------------------------------------------------
224 
225 template <typename MODEL> void testLinearModelLinearity() {
226  typedef LinearModelFixture<MODEL> Test_;
227  typedef oops::Increment<MODEL> Increment_;
228  typedef oops::ModelAuxIncrement<MODEL> ModelAuxIncr_;
229 
230  const util::Duration len(Test_::test().getString("forecast length"));
231  const util::DateTime t1(Test_::time());
232  const util::DateTime t2(t1 + len);
233  EXPECT(t2 > t1);
234  const double zz = 3.1415;
235 
236  Increment_ dx1(Test_::resol(), Test_::tlm().variables(), t1);
237  Test_::covariance().randomize(dx1);
238  ModelAuxIncr_ daux1(Test_::dbias());
239  EXPECT(dx1.norm() > 0.0);
240 
241  Increment_ dx2(dx1);
242  ModelAuxIncr_ daux2(daux1);
243 
244  Test_::tlm().forecastTL(dx1, daux1, len);
245  EXPECT(dx1.validTime() == t2);
246  dx1 *= zz;
247  daux1 *= zz;
248 
249  dx2 *= zz;
250  daux2 *= zz;
251  Test_::tlm().forecastTL(dx2, daux2, len);
252  EXPECT(dx2.validTime() == t2);
253 
254  const double tol = Test_::test().getDouble("tolerance AD");
255  EXPECT(oops::is_close(dx1.norm(), dx2.norm(), tol));
256 }
257 
258 // -----------------------------------------------------------------------------
259 
260 template <typename MODEL> void testLinearApproximation() {
261  typedef LinearModelFixture<MODEL> Test_;
262  typedef oops::Increment<MODEL> Increment_;
263  typedef oops::State<MODEL> State_;
264 
265  const util::Duration len(Test_::test().getString("forecast length"));
266  const util::DateTime t1(Test_::time());
267  const util::DateTime t2(t1 + len);
268  EXPECT(t2 > t1);
269 
270  Increment_ dx0(Test_::resol(), Test_::tlm().variables(), t1);
271  Test_::covariance().randomize(dx0);
272  EXPECT(dx0.norm() > 0.0);
273 
274  Increment_ dx(dx0);
275  Test_::tlm().forecastTL(dx, Test_::dbias(), len);
276  const double dxnorm = dx.norm();
277 
279  State_ xx0(Test_::xref());
280  Test_::model().forecast(xx0, Test_::bias(), len, post);
281 
282  const unsigned int ntest = Test_::test().getInt("iterations TL");
283  double zz = 1.0;
284  if (Test_::test().has("first multiplier TL")) {
285  zz = Test_::test().getDouble("first multiplier TL");
286  }
287 
288  std::vector<double> errors;
289  for (unsigned int jtest = 0; jtest < ntest; ++jtest) {
290  State_ xx(Test_::xref());
291  Increment_ pert(dx0);
292  pert *= zz;
293  xx += pert;
294  Test_::model().forecast(xx, Test_::bias(), len, post);
295 
296  Increment_ diff(Test_::resol(), Test_::tlm().variables(), t2);
297  diff.diff(xx, xx0);
298  const double difnorm = diff.norm();
299  const double err = zz * dxnorm / difnorm;
300  Increment_ derr(dx);
301  derr *= zz;
302  derr -= diff;
303  const double errnorm = derr.norm();
304  errors.push_back(errnorm / difnorm);
305  oops::Log::test() << "TL error = " << std::setprecision(16) << err
306  << ", relative error = " << errnorm / difnorm << std::endl;
307  zz /= 10.0;
308  }
309 
310 // Analyze results
311  const double approx = *std::min_element(errors.begin(), errors.end());
312  oops::Log::test() << "Test TL min error = " << approx << std::endl;
313  const double tol = Test_::test().getDouble("tolerance TL");
314  EXPECT(approx < tol);
315 }
316 
317 // -----------------------------------------------------------------------------
318 
319 template <typename MODEL> void testLinearModelAdjoint() {
320  typedef LinearModelFixture<MODEL> Test_;
321  typedef oops::Increment<MODEL> Increment_;
322  typedef oops::ModelAuxIncrement<MODEL> ModelAuxIncr_;
323 
324  const util::Duration len(Test_::test().getString("forecast length"));
325  const util::DateTime t1(Test_::time());
326  const util::DateTime t2(t1 + len);
327  EXPECT(t2 > t1);
328 
329  Increment_ dx11(Test_::resol(), Test_::tlm().variables(), t1);
330  Test_::covariance().randomize(dx11);
331  ModelAuxIncr_ daux1(Test_::dbias());
332  EXPECT(dx11.norm() > 0.0);
333  Increment_ dx12(dx11);
334  Test_::tlm().forecastTL(dx12, daux1, len);
335  EXPECT(dx12.norm() > 0.0);
336  Increment_ dx22(Test_::resol(), Test_::tlm().variables(), t2);
337  Test_::covariance().randomize(dx22);
338  ModelAuxIncr_ daux2(Test_::dbias());
339  EXPECT(dx22.norm() > 0.0);
340  Increment_ dx21(dx22);
341  Test_::tlm().forecastAD(dx21, daux2, len);
342  EXPECT(dx21.norm() > 0.0);
343 
344  EXPECT(dx11.norm() != dx22.norm());
345  EXPECT(dx11.validTime() == t1);
346  EXPECT(dx21.validTime() == t1);
347  EXPECT(dx12.validTime() == t2);
348  EXPECT(dx22.validTime() == t2);
349 
350  const double dot1 = dot_product(dx11, dx21);
351  const double dot2 = dot_product(dx12, dx22);
352  const double tol = Test_::test().getDouble("tolerance AD");
353  EXPECT(oops::is_close(dot1, dot2, tol));
354 }
355 
356 // =============================================================================
357 
358 template <typename MODEL>
359 class LinearModel : public oops::Test {
360  public:
363 
364  private:
365  std::string testid() const override {return "test::LinearModel<" + MODEL::name() + ">";}
366 
367  void register_tests() const override {
368  std::vector<eckit::testing::Test>& ts = eckit::testing::specification();
369 
370  ts.emplace_back(CASE("interface/LinearModel/testLinearModelConstructor")
371  { testLinearModelConstructor<MODEL>(); });
372  ts.emplace_back(CASE("interface/LinearModel/testLinearModelZeroLength")
373  { testLinearModelZeroLength<MODEL>(); });
374  ts.emplace_back(CASE("interface/LinearModel/testLinearModelZeroPert")
375  { testLinearModelZeroPert<MODEL>(); });
376  ts.emplace_back(CASE("interface/LinearModel/testLinearModelLinearity")
377  { testLinearModelLinearity<MODEL>(); });
378  ts.emplace_back(CASE("interface/LinearModel/testLinearApproximation")
379  { testLinearApproximation<MODEL>(); });
380  ts.emplace_back(CASE("interface/LinearModel/testLinearModelAdjoint")
381  { testLinearModelAdjoint<MODEL>(); });
382  }
383 
384  void clear() const override {}
385 };
386 
387 // =============================================================================
388 
389 } // namespace test
390 
391 #endif // TEST_INTERFACE_LINEARMODEL_H_
program test
Geometry class used in oops; subclass of interface class interface::Geometry.
Increment class used in oops.
Encapsulates the linear forecast model.
Abstract nonlinear forecast model used by high level algorithms and applications.
Control model post processing.
Definition: PostProcessor.h:30
void enrollProcessor(PostBase_ *pp)
Definition: PostProcessor.h:38
Control model post processing.
State class used in oops; subclass of interface class interface::State.
Save trajectory during forecast run.
static const eckit::Configuration & test()
std::unique_ptr< const ModelAuxIncr_ > dbias_
std::unique_ptr< const ModelAux_ > bias_
oops::ModelSpaceCovarianceBase< MODEL > Covariance_
static const Covariance_ & covariance()
oops::ModelAuxControl< MODEL > ModelAux_
std::unique_ptr< const util::DateTime > time_
oops::Geometry< MODEL > Geometry_
static const util::DateTime & time()
std::unique_ptr< const Covariance_ > B_
std::shared_ptr< LinearModel_ > tlm_
static LinearModelFixture< MODEL > & getInstance()
static const oops::Variables & ctlvars()
std::unique_ptr< const eckit::LocalConfiguration > test_
static const Geometry_ & resol()
static const ModelAuxIncr_ & dbias()
std::unique_ptr< const State_ > xref_
std::unique_ptr< const eckit::LocalConfiguration > tlConf_
static const LinearModel_ & tlm()
std::unique_ptr< const oops::Variables > ctlvars_
oops::LinearModel< MODEL > LinearModel_
oops::ModelAuxIncrement< MODEL > ModelAuxIncr_
std::unique_ptr< const Model_ > model_
std::unique_ptr< const Geometry_ > resol_
static const ModelAux_ & bias()
oops::Increment< MODEL > Increment_
void clear() const override
void register_tests() const override
std::string testid() const override
static const eckit::Configuration & config()
const eckit::mpi::Comm & world()
Default communicator with all MPI tasks (ie MPI_COMM_WORLD)
Definition: oops/mpi/mpi.cc:84
logical function has(this, var)
void testLinearModelConstructor()
tests constructor, timeResolution() method and print method
void testLinearModelAdjoint()
void testLinearModelZeroLength()
void testLinearModelZeroPert()
void testLinearApproximation()
CASE("assimilation/FullGMRES/FullGMRES")
void testLinearModelLinearity()