IODA Bundle
test/interface/ObsOperator.h
Go to the documentation of this file.
1 /*
2  * (C) Copyright 2017-2018 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 TEST_INTERFACE_OBSOPERATOR_H_
9 #define TEST_INTERFACE_OBSOPERATOR_H_
10 
11 #include <memory>
12 #include <string>
13 #include <vector>
14 
15 #define ECKIT_TESTING_SELF_REGISTER_CASES 0
16 
17 #include <boost/noncopyable.hpp>
18 
19 #include "eckit/config/LocalConfiguration.h"
20 #include "eckit/testing/Test.h"
21 #include "oops/base/Variables.h"
22 #include "oops/interface/GeoVaLs.h"
28 #include "oops/runs/Test.h"
29 #include "oops/util/Expect.h"
31 #include "test/TestEnvironment.h"
32 
33 namespace test {
34 
35 const char *expectConstructorToThrow = "expect constructor to throw exception with message";
36 const char *expectSimulateObsToThrow = "expect simulateObs to throw exception with message";
37 
38 // -----------------------------------------------------------------------------
39 /// \brief tests constructor and print method
40 template <typename OBS> void testConstructor() {
41  typedef ObsTestsFixture<OBS> Test_;
42  typedef oops::ObsOperator<OBS> ObsOperator_;
43 
44  for (std::size_t jj = 0; jj < Test_::obspace().size(); ++jj) {
45  eckit::LocalConfiguration obsopconf(Test_::config(jj), "obs operator");
46 
47  if (!Test_::config(jj).has(expectConstructorToThrow)) {
48  std::unique_ptr<ObsOperator_> hop(new ObsOperator_(Test_::obspace()[jj], obsopconf));
49  EXPECT(hop.get());
50  oops::Log::test() << "Testing ObsOperator: " << *hop << std::endl;
51  hop.reset();
52  EXPECT(!hop.get());
53  } else {
54  // The constructor is expected to throw an exception containing the specified string.
55  const std::string expectedMessage = Test_::config(jj).getString(expectConstructorToThrow);
56  EXPECT_THROWS_MSG(ObsOperator_(Test_::obspace()[jj], obsopconf),
57  expectedMessage.c_str());
58  }
59  }
60 }
61 
62 // -----------------------------------------------------------------------------
63 
64 template <typename OBS> void testSimulateObs() {
65  typedef ObsTestsFixture<OBS> Test_;
66  typedef oops::GeoVaLs<OBS> GeoVaLs_;
67  typedef oops::ObsDiagnostics<OBS> ObsDiags_;
68  typedef oops::ObsAuxControl<OBS> ObsAuxCtrl_;
69  typedef oops::ObsOperator<OBS> ObsOperator_;
70  typedef oops::ObsVector<OBS> ObsVector_;
71 
72  for (std::size_t jj = 0; jj < Test_::obspace().size(); ++jj) {
73  const eckit::LocalConfiguration & conf = Test_::config(jj);
74  if (conf.has(expectConstructorToThrow))
75  continue;
76 
77  // initialize observation operator (set variables requested from the model,
78  // variables simulated by the observation operator, other init)
79  eckit::LocalConfiguration obsopconf(conf, "obs operator");
80  ObsOperator_ hop(Test_::obspace()[jj], obsopconf);
81 
82  // initialize bias correction
83  eckit::LocalConfiguration biasconf = conf.getSubConfiguration("obs bias");
84  typename ObsAuxCtrl_::Parameters_ biasparams;
85  biasparams.validateAndDeserialize(biasconf);
86  const ObsAuxCtrl_ ybias(Test_::obspace()[jj], biasparams);
87 
88  // read geovals from the file
89  eckit::LocalConfiguration gconf(conf, "geovals");
90  oops::Variables hopvars = hop.requiredVars();
91  hopvars += ybias.requiredVars();
92  const GeoVaLs_ gval(gconf, Test_::obspace()[jj], hopvars);
93 
94  // create obsvector to hold H(x)
95  ObsVector_ hofx(Test_::obspace()[jj]);
96 
97  // create diagnostics to hold HofX diags
98  oops::Variables diagvars;
99  diagvars += ybias.requiredHdiagnostics();
100  ObsDiags_ diags(Test_::obspace()[jj], hop.locations(), diagvars);
101 
102  // call H(x), save result in the output file as @hofx
103  if (Test_::config(jj).has(expectSimulateObsToThrow)) {
104  // The simulateObs method is expected to throw an exception
105  // containing the specified string.
106  const std::string expectedMessage =
107  Test_::config(jj).getString(expectSimulateObsToThrow);
108  EXPECT_THROWS_MSG(hop.simulateObs(gval, hofx, ybias, diags),
109  expectedMessage.c_str());
110  continue;
111  } else {
112  hop.simulateObs(gval, hofx, ybias, diags);
113  }
114  hofx.save("hofx");
115 
116  const double tol = conf.getDouble("tolerance");
117  if (conf.has("vector ref")) {
118  // if reference h(x) is saved in file as a vector, read from file
119  // and compare the norm of difference to zero
120  ObsVector_ obsref(Test_::obspace()[jj], conf.getString("vector ref"));
121  obsref -= hofx;
122  const double zz = obsref.rms();
123  oops::Log::test() << "Vector difference between reference and computed: " << obsref;
124  EXPECT(zz < 100*tol); // change tol from percent to actual value.
125  // tol used in is_close is relative
126  } else if (conf.has("norm ref")) {
127  // if reference h(x) is saved in file as a vector, read from file
128  // and compare the difference, normalised by the reference values to zero
129  ObsVector_ obsref(Test_::obspace()[jj], conf.getString("norm ref"));
130  obsref -= hofx;
131  obsref /= hofx;
132  const double zz = obsref.rms();
133  oops::Log::test() << "Normalised vector difference between reference and computed: "
134  << obsref;
135  EXPECT(zz < 100*tol); // change tol from percent to actual value.
136  // tol used in is_close is relative
137  } else {
138  // else compare h(x) norm to the norm from the config
139  const double zz = hofx.rms();
140  const double xx = conf.getDouble("rms ref");
141 
142  oops::Log::debug() << "zz: " << std::fixed << std::setprecision(8) << zz << std::endl;
143  oops::Log::debug() << "xx: " << std::fixed << std::setprecision(8) << xx << std::endl;
144 
145  EXPECT(oops::is_close(xx, zz, tol));
146  }
147  }
148 }
149 
150 // -----------------------------------------------------------------------------
151 
152 template <typename OBS>
153 class ObsOperator : public oops::Test {
155  public:
157  virtual ~ObsOperator() {}
158  private:
159  std::string testid() const override {return "test::ObsOperator<" + OBS::name() + ">";}
160 
161  void register_tests() const override {
162  std::vector<eckit::testing::Test>& ts = eckit::testing::specification();
163 
164  ts.emplace_back(CASE("interface/ObsOperator/testConstructor")
165  { testConstructor<OBS>(); });
166  ts.emplace_back(CASE("interface/ObsOperator/testSimulateObs")
167  { testSimulateObs<OBS>(); });
168  }
169 
170  void clear() const override {
171  Test_::reset();
172  }
173 };
174 
175 // =============================================================================
176 
177 } // namespace test
178 
179 #endif // TEST_INTERFACE_OBSOPERATOR_H_
program test
void clear() const override
void register_tests() const override
ObsTestsFixture< OBS > Test_
std::string testid() const override
logical function has(this, var)
const char * expectConstructorToThrow
const char * expectSimulateObsToThrow
CASE("assimilation/FullGMRES/FullGMRES")
void testConstructor()
Tests creation and destruction of ObsErrorCovariances.