UFO
Predictor.h
Go to the documentation of this file.
1 /*
2  * (C) Crown copyright 2021, Met Office
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_UFO_PREDICTOR_H_
9 #define TEST_UFO_PREDICTOR_H_
10 
11 #include <memory>
12 #include <set>
13 #include <string>
14 #include <vector>
15 
16 
17 #define ECKIT_TESTING_SELF_REGISTER_CASES 0
18 
19 
20 #include "eckit/config/LocalConfiguration.h"
21 #include "eckit/mpi/Comm.h"
22 #include "eckit/testing/Test.h"
23 #include "ioda/ObsSpace.h"
24 #include "ioda/ObsVector.h"
25 #include "oops/interface/ObsDataVector.h"
26 #include "oops/interface/ObsOperator.h"
27 #include "oops/mpi/mpi.h"
28 #include "oops/runs/Test.h"
29 #include "oops/util/Expect.h"
30 #include "oops/util/IntSetParser.h"
31 #include "test/interface/ObsTestsFixture.h"
32 #include "ufo/ObsTraits.h"
33 
34 
35 namespace ufo {
36 namespace test {
37 
38 // -----------------------------------------------------------------------------
39 void testPredictor() {
40  typedef ::test::ObsTestsFixture<ObsTraits> Test_;
41  typedef oops::ObsDiagnostics<ufo::ObsTraits> ObsDiags_;
42 
43  std::vector<eckit::LocalConfiguration> typeconfs;
44  ::test::TestEnvironment::config().get("observations", typeconfs);
45 
46  for (std::size_t jj = 0; jj < Test_::obspace().size(); ++jj) {
47  ioda::ObsSpace &ospace = Test_::obspace()[jj].obsspace();
48  const eckit::Configuration &conf = typeconfs[jj];
49 
50  // initialize bias correction
51  eckit::LocalConfiguration bconf(conf, "obs bias");
52  ObsBiasParameters bparams;
53  bparams.validateAndDeserialize(bconf);
54  const ObsBias ybias(ospace, bparams);
55  // get predictor names
56  std::vector<std::string> predictor_names = ybias.requiredPredictors();
57 
58  // Initialize GeoVaLs
59  oops::Variables gvars;
60  gvars += ybias.requiredVars();
61  std::unique_ptr<const GeoVaLs> gval;
62  if (gvars.size() > 0) {
63  // Read GeoVaLs from a file
64  eckit::LocalConfiguration gconf(conf, "geovals");
65  gval.reset(new GeoVaLs(gconf, ospace, gvars));
66  } else {
67  // Create an empty GeoVaLs object
68  gval.reset(new GeoVaLs(ospace.distribution(), gvars));
69  }
70 
71  // initialize Obs diagnostics
72  oops::Variables diagvars;
73  diagvars += ybias.requiredHdiagnostics();
74  std::vector<float> lons(ospace.nlocs());
75  std::vector<float> lats(ospace.nlocs());
76  std::vector<util::DateTime> times(ospace.nlocs());
77  ospace.get_db("MetaData", "latitude", lats);
78  ospace.get_db("MetaData", "longitude", lons);
79  ospace.get_db("MetaData", "datetime", times);
80  Locations locs(lons, lats, times, ospace.distribution());
81  ObsDiagnostics ydiags(ospace, locs, diagvars);
82 
83  bool expect_error_message = false;
84 
85  // Calculate predictor values
86  const std::size_t npreds = predictor_names.size();
87  EXPECT(npreds > 0);
88  std::vector<ioda::ObsVector> predData(npreds, ioda::ObsVector(ospace));
89 
90  const Predictors & predictors = ybias.predictors();
91  for (std::size_t p = 0; p < npreds; ++p) {
92  if (conf.has("expectExceptionWithMessage")) {
93  const std::string msg = conf.getString("expectExceptionWithMessage");
94  EXPECT_THROWS_MSG(predictors[p]->compute(ospace, *gval, ydiags, predData[p]), msg.c_str());
95  expect_error_message = true;
96  break;
97  }
98  predictors[p]->compute(ospace, *gval, ydiags, predData[p]);
99  predData[p].save(predictors[p]->name() + "Predictor");
100  }
101 
102  if (expect_error_message) {
103  continue;
104  }
105 
106  // Read in tolerance from yaml
107  const double tol = conf.getDouble("tolerance");
108 
109  // Get output variable names and read test data
110  // Note prepend predictor_ to predictor names to distingush
111  // predictor reference from obsbias reference
112  std::vector<std::string> vars;
113  for (std::size_t jp = 0; jp < npreds; ++jp) {
114  vars.push_back("predictor_" + predictor_names[jp]);
115  }
116  const oops::Variables testvars = ospace.obsvariables();
117  const std::size_t nvars = testvars.size();
118  if (!testvars.channels().empty()) {
119  // At present we can label predictors with either the channel number or the variable
120  // name, but not both. So make sure there's only one multi-channel variable.
121  ASSERT(nvars == testvars.channels().size());
122  }
123 
124  /// For each predictor for each variable compare computed predictor values to reference
125  std::vector<double> testData(ospace.nlocs());
126  for (std::size_t jp = 0; jp < npreds; ++jp) {
127  const std::size_t nlocs = predData[jp].nlocs();
128  for (std::size_t jv = 0; jv < nvars; ++jv) {
129  std::string refVarName = "predictor_" + predictor_names[jp] + '_';
130  if (testvars.channels().empty())
131  refVarName += testvars[jv];
132  else
133  refVarName += std::to_string(testvars.channels()[jv]);
134 
135  ospace.get_db("TestReference", refVarName, testData);
136 
137  // compare test and reference vectors
138  double rms = 0.0;
139  for (size_t jl = 0; jl < nlocs; jl++) {
140  testData[jl] -= predData[jp][jl*nvars + jv];
141  rms += testData[jl] * testData[jl];
142  }
143  rms = std::sqrt(rms / nlocs);
144 
145  oops::Log::debug() << "Vector difference between reference and computed for "
146  << refVarName << ": " << testData << std::endl;
147  EXPECT(rms <= tol);
148  }
149  }
150  }
151 }
152 
153 class Predictor : public oops::Test {
154  public:
156  virtual ~Predictor() {}
157  private:
158  std::string testid() const override {return "ufo::test::Predictor";}
159 
160  void register_tests() const override {
161  std::vector<eckit::testing::Test>& ts = eckit::testing::specification();
162 
163  ts.emplace_back(CASE("ufo/Predictor/testPredictor")
164  { testPredictor(); });
165  }
166 
167  void clear() const override {}
168 };
169 
170 // -----------------------------------------------------------------------------
171 
172 } // namespace test
173 } // namespace ufo
174 
175 #endif // TEST_UFO_PREDICTOR_H_
Parameters influencing the bias correction process.
void register_tests() const override
Definition: Predictor.h:160
void clear() const override
Definition: Predictor.h:167
virtual ~Predictor()
Definition: Predictor.h:156
std::string testid() const override
Definition: Predictor.h:158
void testPredictor()
Definition: Predictor.h:39
CASE("ufo/DataExtractor/bilinearinterp/float_linear")
integer function nlocs(this)
Return the number of observational locations in this Locations object.
Definition: RunCRTM.h:27
std::vector< std::shared_ptr< PredictorBase > > Predictors
Definition: PredictorBase.h:92