IODA Bundle
test/interface/Increment.h
Go to the documentation of this file.
1 /*
2  * (C) Copyright 2009-2016 ECMWF.
3  * (C) Copyright 2020 UCAR.
4  *
5  * This software is licensed under the terms of the Apache Licence Version 2.0
6  * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
7  * In applying this licence, ECMWF does not waive the privileges and immunities
8  * granted to it by virtue of its status as an intergovernmental organisation nor
9  * does it submit to any jurisdiction.
10  */
11 
12 #ifndef TEST_INTERFACE_INCREMENT_H_
13 #define TEST_INTERFACE_INCREMENT_H_
14 
15 #include <cmath>
16 #include <iostream>
17 #include <memory>
18 #include <string>
19 #include <vector>
20 
21 #define ECKIT_TESTING_SELF_REGISTER_CASES 0
22 
23 #include <boost/noncopyable.hpp>
24 
25 #include "eckit/config/LocalConfiguration.h"
26 #include "eckit/testing/Test.h"
27 #include "oops/base/Geometry.h"
28 #include "oops/base/Increment.h"
29 #include "oops/base/State.h"
30 #include "oops/base/Variables.h"
31 #include "oops/mpi/mpi.h"
32 #include "oops/runs/Test.h"
33 #include "oops/util/DateTime.h"
34 #include "oops/util/dot_product.h"
35 #include "oops/util/Logger.h"
36 #include "test/TestEnvironment.h"
37 
38 
39 namespace test {
40 
41 // =============================================================================
42 
43 template <typename MODEL> class IncrementFixture : private boost::noncopyable {
45 
46  public:
47  static const Geometry_ & resol() {return *getInstance().resol_;}
48  static const oops::Variables & ctlvars() {return *getInstance().ctlvars_;}
49  static const util::DateTime & time() {return *getInstance().time_;}
50  static const double & tolerance() {return getInstance().tolerance_;}
51  static const eckit::Configuration & test() {return *getInstance().test_;}
52  static void reset() {
53  getInstance().time_.reset();
54  getInstance().ctlvars_.reset();
55  getInstance().test_.reset();
56  getInstance().resol_.reset();
57  }
58 
59  private:
61  static IncrementFixture<MODEL> theIncrementFixture;
62  return theIncrementFixture;
63  }
64 
66 // Setup a geometry
67  const eckit::LocalConfiguration resolConfig(TestEnvironment::config(), "geometry");
68  resol_.reset(new Geometry_(resolConfig, oops::mpi::world()));
69 
70  ctlvars_.reset(new oops::Variables(TestEnvironment::config(), "inc variables"));
71 
72  const double tol_default = 1e-8;
73  test_.reset(new eckit::LocalConfiguration(TestEnvironment::config(), "increment test"));
74  time_.reset(new util::DateTime(test_->getString("date")));
75  tolerance_ = test_->getDouble("tolerance", tol_default);
76  if (tolerance_ > tol_default) {
77  oops::Log::warning() <<
78  "Warning: Increment norm tolerance greater than 1e-8 "
79  "may not be suitable for certain solvers." <<
80  std::endl; }
81  }
82 
83  ~IncrementFixture<MODEL>() {}
84 
85  std::unique_ptr<Geometry_> resol_;
86  std::unique_ptr<oops::Variables> ctlvars_;
87  std::unique_ptr<const eckit::LocalConfiguration> test_;
88  double tolerance_;
89  std::unique_ptr<util::DateTime> time_;
90  std::unique_ptr<bool> skipAccumTest_;
91  std::unique_ptr<bool> skipDiffTest_;
92 };
93 
94 // =============================================================================
95 /// \brief tests Increment constructor and print method
96 template <typename MODEL> void testIncrementConstructor() {
97  typedef IncrementFixture<MODEL> Test_;
98  typedef oops::Increment<MODEL> Increment_;
99 
100  Increment_ dx(Test_::resol(), Test_::ctlvars(), Test_::time());
101  oops::Log::test() << "Printing zero increment: " << dx << std::endl;
102 
103  EXPECT(dx.norm() == 0.0);
104 }
105 
106 // -----------------------------------------------------------------------------
107 
108 template <typename MODEL> void testIncrementCopyConstructor() {
109  typedef IncrementFixture<MODEL> Test_;
110  typedef oops::Increment<MODEL> Increment_;
111 
112  Increment_ dx1(Test_::resol(), Test_::ctlvars(), Test_::time());
113  dx1.random();
114  oops::Log::test() << "Printing random increment: " << dx1 << std::endl;
115 
116  EXPECT(dx1.norm() > 0.0);
117 
118  Increment_ dx2(dx1);
119  EXPECT(dx2.norm() > 0.0);
120 
121 // Check that the copy is equal to the original
122  dx2 -= dx1;
123  EXPECT(dx2.norm() == 0.0);
124 }
125 
126 // -----------------------------------------------------------------------------
127 
128 template <typename MODEL> void testIncrementCopyBoolConstructor() {
129  typedef IncrementFixture<MODEL> Test_;
130  typedef oops::Increment<MODEL> Increment_;
131 
132  Increment_ dx1(Test_::resol(), Test_::ctlvars(), Test_::time());
133  dx1.random();
134  EXPECT(dx1.norm() > 0.0);
135 
136  // Test with copy set to true
137  Increment_ dx2(dx1, true);
138  EXPECT(dx2.norm() == dx1.norm());
139 
140  // Test with copy set to false
141  Increment_ dx3(dx1, false);
142  EXPECT(dx3.norm() == 0.0);
143 }
144 
145 // -----------------------------------------------------------------------------
146 
147 template <typename MODEL> void testIncrementChangeResConstructor() {
148  typedef IncrementFixture<MODEL> Test_;
149  typedef oops::Increment<MODEL> Increment_;
150 
151  // For now this is just a copy and not a change res, would require config changes
152  Increment_ dx1(Test_::resol(), Test_::ctlvars(), Test_::time());
153  Increment_ dx2(Test_::resol(), dx1);
154 
155  // Check they are same. Should be replaced with change res and check they are different
156  EXPECT(dx2.norm() == dx1.norm());
157 }
158 
159 // -----------------------------------------------------------------------------
160 
161 template <typename MODEL> void testIncrementTriangle() {
162  typedef IncrementFixture<MODEL> Test_;
163  typedef oops::Increment<MODEL> Increment_;
164 
165  Increment_ dx1(Test_::resol(), Test_::ctlvars(), Test_::time());
166  dx1.random();
167  Increment_ dx2(Test_::resol(), Test_::ctlvars(), Test_::time());
168  dx2.random();
169 
170 // test triangle inequality
171  double dot1 = dx1.norm();
172  EXPECT(dot1 > 0.0);
173 
174  double dot2 = dx2.norm();
175  EXPECT(dot2 > 0.0);
176 
177  dx2 += dx1;
178  double dot3 = dx2.norm();
179  EXPECT(dot3 > 0.0);
180 
181  EXPECT(dot3 <= dot1 + dot2);
182 }
183 
184 // -----------------------------------------------------------------------------
185 
186 template <typename MODEL> void testIncrementOpPlusEq() {
187  typedef IncrementFixture<MODEL> Test_;
188  typedef oops::Increment<MODEL> Increment_;
189 
190  Increment_ dx1(Test_::resol(), Test_::ctlvars(), Test_::time());
191  dx1.random();
192  Increment_ dx2(dx1);
193 
194 // test *= and +=
195  dx2 += dx1;
196  dx1 *= 2.0;
197 
198  dx2 -= dx1;
199  EXPECT(dx2.norm() < Test_::tolerance());
200 }
201 
202 // -----------------------------------------------------------------------------
203 
204 template <typename MODEL> void testIncrementDotProduct() {
205  typedef IncrementFixture<MODEL> Test_;
206  typedef oops::Increment<MODEL> Increment_;
207 
208  Increment_ dx1(Test_::resol(), Test_::ctlvars(), Test_::time());
209  dx1.random();
210  Increment_ dx2(Test_::resol(), Test_::ctlvars(), Test_::time());
211  dx2.random();
212 
213 // test symmetry of dot product
214  double zz1 = dot_product(dx1, dx2);
215  double zz2 = dot_product(dx2, dx1);
216 
217  EXPECT(zz1 == zz2);
218 }
219 
220 // -----------------------------------------------------------------------------
221 
222 template <typename MODEL> void testIncrementZero() {
223  typedef IncrementFixture<MODEL> Test_;
224  typedef oops::Increment<MODEL> Increment_;
225 
226  Increment_ dx(Test_::resol(), Test_::ctlvars(), Test_::time());
227  dx.random();
228  EXPECT(dx.norm() > 0.0);
229 
230 // test zero
231  dx.zero();
232  EXPECT(dx.norm() == 0.0);
233  EXPECT(dx.validTime() == Test_::time());
234 
235 // Create a new time one hour in the future
236  util::Duration onehour(3600);
237  util::DateTime newTime(Test_::time().toString());
238  newTime+=onehour;
239 
240 // Confirm zero with setting new datetime works
241  dx.random();
242  dx.zero(newTime);
243  EXPECT(dx.norm() == 0.0);
244  EXPECT(dx.validTime() == newTime);
245 }
246 
247 // -----------------------------------------------------------------------------
248 
249 template <typename MODEL> void testIncrementAxpy() {
250  typedef IncrementFixture<MODEL> Test_;
251  typedef oops::Increment<MODEL> Increment_;
252 
253  Increment_ dx1(Test_::resol(), Test_::ctlvars(), Test_::time());
254  dx1.random();
255 
256 // test axpy
257  Increment_ dx2(dx1);
258  dx2.axpy(2.0, dx1);
259 
260  dx2 -= dx1;
261  dx2 -= dx1;
262  dx2 -= dx1;
263 
264  EXPECT(dx2.norm() < Test_::tolerance());
265 }
266 
267 // -----------------------------------------------------------------------------
268 
269 template <typename MODEL> void testIncrementAccum() {
270  typedef IncrementFixture<MODEL> Test_;
271  typedef oops::Increment<MODEL> Increment_;
272  typedef oops::State<MODEL> State_;
273 
274  // Option to skip test
275  bool skipTest = Test_::test().getBool("skip accum test", false);
276  if (skipTest) {
277  oops::Log::warning() << "Skipping Increment.accum test";
278  return;
279  }
280 
281  // Create two different random increments
282  Increment_ dx1(Test_::resol(), Test_::ctlvars(), Test_::time());
283  dx1.random();
284 
285  Increment_ dx2(Test_::resol(), Test_::ctlvars(), Test_::time());
286  dx2.random();
287 
288  Increment_ diff(dx1);
289  diff -= dx2;
290  EXPECT(diff.norm() != 0.0);
291 
292  // Create a state that is equal to dx2
293  State_ x(Test_::resol(), Test_::ctlvars(), Test_::time());
294  x.zero();
295  x += dx2;
296 
297  // Create copy of dx1 to test against axpy
298  Increment_ dx3(dx1);
299 
300  // Test accum
301  dx1.accumul(2.0, x);
302 
303  // Use axpy for reference
304  dx3.axpy(2.0, dx2);
305 
306  // Check axpy did something
307  diff = dx3;
308  diff -= dx2;
309  EXPECT(diff.norm() != 0.0);
310 
311  // Check accumul matches axpy
312  diff = dx1;
313  diff -= dx3;
314  EXPECT(diff.norm() == 0.0);
315 }
316 
317 // -----------------------------------------------------------------------------
318 
319 template <typename MODEL> void testIncrementSerialize() {
320  typedef IncrementFixture<MODEL> Test_;
321  typedef oops::Increment<MODEL> Increment_;
322 
323 // Create two random increments
324  Increment_ dx1(Test_::resol(), Test_::ctlvars(), Test_::time());
325  dx1.random();
326 
327  util::DateTime tt(Test_::time() + util::Duration("PT15H"));
328  Increment_ dx2(Test_::resol(), Test_::ctlvars(), tt);
329 
330 // Test serialize-deserialize
331  std::vector<double> vect;
332  dx1.serialize(vect);
333  EXPECT(vect.size() == dx1.serialSize());
334 
335  size_t index = 0;
336  dx2.deserialize(vect, index);
337  EXPECT(index == dx1.serialSize());
338  EXPECT(index == dx2.serialSize());
339 
340  dx1.serialize(vect);
341  EXPECT(vect.size() == dx1.serialSize() * 2);
342 
343  if (dx1.serialSize() > 0) { // until all models have implemented serialize
344  EXPECT(dx1.norm() > 0.0);
345  EXPECT(dx2.norm() > 0.0);
346  EXPECT(dx2.validTime() == Test_::time());
347 
348  dx2 -= dx1;
349  EXPECT(dx2.norm() == 0.0);
350  }
351 }
352 
353 // -----------------------------------------------------------------------------
354 
355 template <typename MODEL> void testIncrementDiff() {
356  typedef IncrementFixture<MODEL> Test_;
357  typedef oops::Increment<MODEL> Increment_;
358  typedef oops::State<MODEL> State_;
359 
360  // Option to skip test
361  bool skipTest = Test_::test().getBool("skip diff test", false);
362  if (skipTest) {
363  oops::Log::warning() << "Skipping Increment.diff test";
364  return;
365  }
366 
367  // Create two states to diff
368  State_ x1(Test_::resol(), Test_::ctlvars(), Test_::time());
369  State_ x2(Test_::resol(), Test_::ctlvars(), Test_::time());
370 
371  // Create two different random increments
372  Increment_ dx1(Test_::resol(), Test_::ctlvars(), Test_::time());
373  dx1.random();
374 
375  Increment_ dx2(Test_::resol(), Test_::ctlvars(), Test_::time());
376  dx2.random();
377 
378  // Create some different increments
379  Increment_ diff1(dx1);
380  Increment_ diff2(Test_::resol(), Test_::ctlvars(), Test_::time());
381  diff1 -= dx2;
382  EXPECT(diff1.norm() != 0.0);
383 
384  // Fill states with some different random values
385  x1.zero();
386  x1+=dx1;
387  x2.zero();
388  x2+=dx2;
389 
390  // Use difference of states to compute difference
391  diff2.diff(x1, x2);
392 
393  // Compare difference with -= operator
394  EXPECT(diff1.norm() == diff2.norm());
395 }
396 
397 // -----------------------------------------------------------------------------
398 
399 template <typename MODEL> void testIncrementTime() {
400  typedef IncrementFixture<MODEL> Test_;
401  typedef oops::Increment<MODEL> Increment_;
402 
403  // Create an increment
404  Increment_ dx(Test_::resol(), Test_::ctlvars(), Test_::time());
405 
406  // Move increment time forward by one hour
407  util::Duration onehour(3600);
408  dx.updateTime(onehour);
409 
410  // Confirm new valid time and that validTime function works
411  util::DateTime newTime(Test_::time().toString());
412  newTime+=onehour;
413  EXPECT(dx.validTime() == newTime);
414 }
415 
416 // -----------------------------------------------------------------------------
417 
418 template <typename MODEL> void testIncrementSchur() {
419  typedef IncrementFixture<MODEL> Test_;
420  typedef oops::Increment<MODEL> Increment_;
421 
422  // Create two identical random increments
423  Increment_ dx1(Test_::resol(), Test_::ctlvars(), Test_::time());
424  dx1.random();
425  Increment_ dx2(dx1);
426  EXPECT(dx1.norm() == dx2.norm());
427 
428  // Compute schur product
429  dx1.schur_product_with(dx2);
430 
431  // Checks
432  EXPECT(dx1.norm() != dx2.norm());
433 
434  // Pass through zeros
435  dx1.random();
436  dx2.zero();
437  dx1.schur_product_with(dx2);
438  EXPECT(dx1.norm() == 0.0);
439 
440  // Pass through ones
441  dx1.random();
442  Increment_ dx1in(dx1);
443  dx2.ones();
444  dx1.schur_product_with(dx2);
445  EXPECT(dx1.norm() == dx1in.norm());
446 }
447 
448 // =============================================================================
449 
450 template <typename MODEL>
451 class Increment : public oops::Test {
452  public:
455 
456  private:
457  std::string testid() const override {return "test::Increment<" + MODEL::name() + ">";}
458 
459  void register_tests() const override {
460  std::vector<eckit::testing::Test>& ts = eckit::testing::specification();
461 
462  ts.emplace_back(CASE("interface/Increment/testIncrementConstructor")
463  { testIncrementConstructor<MODEL>(); });
464  ts.emplace_back(CASE("interface/Increment/testIncrementCopyConstructor")
465  { testIncrementCopyConstructor<MODEL>(); });
466  ts.emplace_back(CASE("interface/Increment/testIncrementCopyBoolConstructor")
467  { testIncrementCopyBoolConstructor<MODEL>(); });
468  ts.emplace_back(CASE("interface/Increment/testIncrementChangeResConstructor")
469  { testIncrementChangeResConstructor<MODEL>(); });
470  ts.emplace_back(CASE("interface/Increment/testIncrementTriangle")
471  { testIncrementTriangle<MODEL>(); });
472  ts.emplace_back(CASE("interface/Increment/testIncrementOpPlusEq")
473  { testIncrementOpPlusEq<MODEL>(); });
474  ts.emplace_back(CASE("interface/Increment/testIncrementDotProduct")
475  { testIncrementDotProduct<MODEL>(); });
476  ts.emplace_back(CASE("interface/Increment/testIncrementAxpy")
477  { testIncrementAxpy<MODEL>(); });
478  ts.emplace_back(CASE("interface/Increment/testIncrementAccum")
479  { testIncrementAccum<MODEL>(); });
480  ts.emplace_back(CASE("interface/Increment/testIncrementDiff")
481  { testIncrementDiff<MODEL>(); });
482  ts.emplace_back(CASE("interface/Increment/testIncrementZero")
483  { testIncrementZero<MODEL>(); });
484  ts.emplace_back(CASE("interface/Increment/testIncrementTime")
485  { testIncrementTime<MODEL>(); });
486  ts.emplace_back(CASE("interface/Increment/testIncrementSchur")
487  { testIncrementSchur<MODEL>(); });
488  ts.emplace_back(CASE("interface/Increment/testIncrementSerialize")
489  { testIncrementSerialize<MODEL>(); });
490  }
491 
492  void clear() const override {}
493 };
494 
495 // =============================================================================
496 
497 } // namespace test
498 
499 #endif // TEST_INTERFACE_INCREMENT_H_
program test
Geometry class used in oops; subclass of interface class interface::Geometry.
Increment class used in oops.
State class used in oops; subclass of interface class interface::State.
std::unique_ptr< bool > skipAccumTest_
static const eckit::Configuration & test()
static const double & tolerance()
static IncrementFixture< MODEL > & getInstance()
std::unique_ptr< const eckit::LocalConfiguration > test_
static const Geometry_ & resol()
std::unique_ptr< Geometry_ > resol_
std::unique_ptr< bool > skipDiffTest_
static const oops::Variables & ctlvars()
static const util::DateTime & time()
std::unique_ptr< oops::Variables > ctlvars_
std::unique_ptr< util::DateTime > time_
oops::Geometry< MODEL > Geometry_
std::string testid() const override
void clear() const override
void register_tests() 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
void testIncrementCopyBoolConstructor()
void testIncrementChangeResConstructor()
void testIncrementSchur()
void testIncrementSerialize()
void testIncrementDiff()
void testIncrementCopyConstructor()
void testIncrementZero()
void testIncrementAxpy()
void testIncrementTriangle()
void testIncrementAccum()
void testIncrementOpPlusEq()
void testIncrementDotProduct()
void testIncrementConstructor()
tests Increment constructor and print method
CASE("assimilation/FullGMRES/FullGMRES")
void testIncrementTime()