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