UFO
test/ufo/PrimitiveVariables.h
Go to the documentation of this file.
1 /*
2  * (C) Copyright 2021 Met Office UK
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_PRIMITIVEVARIABLES_H_
9 #define TEST_UFO_PRIMITIVEVARIABLES_H_
10 
11 #include <iomanip>
12 #include <memory>
13 #include <string>
14 #include <vector>
15 
16 #define ECKIT_TESTING_SELF_REGISTER_CASES 0
17 
18 #include "eckit/config/LocalConfiguration.h"
19 #include "eckit/testing/Test.h"
20 #include "ioda/ObsSpace.h"
21 #include "oops/mpi/mpi.h"
22 #include "oops/runs/Test.h"
23 #include "oops/util/Expect.h"
24 #include "oops/util/FloatCompare.h"
25 #include "oops/util/parameters/Parameters.h"
26 #include "oops/util/parameters/RequiredParameter.h"
27 #include "test/TestEnvironment.h"
29 #include "ufo/filters/Variables.h"
32 
33 namespace eckit
34 {
35  // Don't use the contracted output for floats: the current implementation works only
36  // with integer types.
37  template <> struct VectorPrintSelector<float> { typedef VectorPrintSimple selector; };
38 } // namespace eckit
39 
40 namespace ufo {
41 namespace test {
42 
43 class TestCaseParameters : public oops::Parameters {
45  public:
46  oops::RequiredParameter<std::vector<Variable>> variables{"variables", this};
47  oops::RequiredParameter<std::vector<std::string>> expectedNames{"expected names", this};
48  oops::RequiredParameter<std::vector<std::string>> expectedGroups{"expected groups", this};
49  oops::RequiredParameter<std::vector<std::vector<float>>> expectedValues{"expected values", this};
50 };
51 
52 /// Code shared by all tests
53 class TestFixture : private boost::noncopyable {
54  public:
55  static const ioda::ObsSpace & obsspace() { return *getInstance().obsspace_; }
56  static const ObsFilterData & data() { return *getInstance().data_; }
57 
58  private:
59  static TestFixture & getInstance() {
60  static TestFixture theTestFixture;
61  return theTestFixture;
62  }
63 
65  const eckit::Configuration & conf = ::test::TestEnvironment::config();
66  util::DateTime bgn(conf.getString("window begin"));
67  util::DateTime end(conf.getString("window end"));
68  const eckit::LocalConfiguration obsconf(conf, "obs space");
69  ioda::ObsTopLevelParameters obsparams;
70  obsparams.validateAndDeserialize(obsconf);
71  obsspace_.reset(new ioda::ObsSpace(obsparams, oops::mpi::world(), bgn, end,
72  oops::mpi::myself()));
73  data_.reset(new ObsFilterData(*obsspace_));
74  }
75 
76  std::shared_ptr<ioda::ObsSpace> obsspace_;
77  std::unique_ptr<ObsFilterData> data_;
78 };
79 
80 /// Test a range-based for loop over the PrimitiveVariables range.
81 void testPrimitiveVariables(const eckit::LocalConfiguration &conf) {
82  TestCaseParameters parameters;
83  parameters.validateAndDeserialize(conf);
84 
85  Variables variables;
86  for (const Variable &variable : parameters.variables.value())
87  variables += variable;
88 
89  std::vector<std::string> names;
90  std::vector<std::string> groups;
91  std::vector<Variable> primitiveVariables;
92  std::vector<std::vector<float>> values;
93 
94  for (PrimitiveVariable pv : PrimitiveVariables(variables, TestFixture::data())) {
95  names.push_back(pv.name());
96  groups.push_back(pv.group());
97  primitiveVariables.push_back(pv.variable());
98  values.push_back(pv.values());
99  }
100 
101  EXPECT_EQUAL(names, parameters.expectedNames.value());
102  EXPECT_EQUAL(groups, parameters.expectedGroups.value());
103  EXPECT_EQUAL(values.size(), parameters.expectedValues.value().size());
104  for (size_t i = 0; i < values.size(); ++i)
105  EXPECT(oops::are_all_close_relative(values[i], parameters.expectedValues.value()[i], 1e-6f));
106 
107  EXPECT(std::equal(primitiveVariables.begin(), primitiveVariables.end(), names.begin(),
108  [](const Variable &primitiveVariable, const std::string &name)
109  {return primitiveVariable.variable() == name; }));
110  EXPECT(std::equal(primitiveVariables.begin(), primitiveVariables.end(), groups.begin(),
111  [](const Variable &primitiveVariable, const std::string &group)
112  {return primitiveVariable.group() == group; }));
113 }
114 
115 /// Test explicitly that the * and -> operator overloads in PrimitiveVariablesIterator work
116 /// correctly.
117 void testPrimitiveVariablesIterator(const eckit::LocalConfiguration &conf) {
118  TestCaseParameters parameters;
119  parameters.validateAndDeserialize(conf);
120 
121  Variables variables;
122  for (const Variable &variable : parameters.variables.value())
123  variables += variable;
124 
125  std::vector<std::string> names;
126  std::vector<std::vector<float>> values;
127 
128  {
129  PrimitiveVariables pvars(variables, TestFixture::data());
130  for (PrimitiveVariablesIterator it = pvars.begin(); it != pvars.end(); ++it) {
131  // Use the * operator to get access to the variable name and the -> operator to get access
132  // to its values.
133  names.push_back((*it).name());
134  values.push_back(it->values());
135  }
136  }
137 
138  EXPECT_EQUAL(names, parameters.expectedNames.value());
139  EXPECT_EQUAL(values.size(), parameters.expectedValues.value().size());
140  for (size_t i = 0; i < values.size(); ++i)
141  EXPECT(oops::are_all_close_relative(values[i], parameters.expectedValues.value()[i], 1e-6f));
142 }
143 
144 class PrimitiveVariables : public oops::Test {
145  private:
146  std::string testid() const override {return "ufo::test::PrimitiveVariables";}
147 
148  void register_tests() const override {
149  std::vector<eckit::testing::Test>& ts = eckit::testing::specification();
150 
151  const eckit::LocalConfiguration conf(::test::TestEnvironment::config(), "cases");
152  for (const std::string & testCaseName : conf.keys())
153  {
154  const eckit::LocalConfiguration testCaseConf(conf, testCaseName);
155  ts.emplace_back(CASE("ufo/PrimitiveVariables/range/" + testCaseName, testCaseConf)
156  {
157  testPrimitiveVariables(testCaseConf);
158  });
159  ts.emplace_back(CASE("ufo/PrimitiveVariables/iterator/" + testCaseName, testCaseConf)
160  {
161  testPrimitiveVariablesIterator(testCaseConf);
162  });
163  }
164  }
165 
166  void clear() const override {}
167 };
168 
169 } // namespace test
170 } // namespace ufo
171 
172 #endif // TEST_UFO_PRIMITIVEVARIABLES_H_
Iterator over the names and values of primitive variables held in a Variables object.
std::string testid() const override
OOPS_CONCRETE_PARAMETERS(TestCaseParameters, Parameters)
oops::RequiredParameter< std::vector< std::string > > expectedNames
oops::RequiredParameter< std::vector< Variable > > variables
oops::RequiredParameter< std::vector< std::vector< float > > > expectedValues
oops::RequiredParameter< std::vector< std::string > > expectedGroups
Code shared by all tests.
static TestFixture & getInstance()
static const ObsFilterData & data()
std::unique_ptr< ObsFilterData > data_
static const ioda::ObsSpace & obsspace()
std::shared_ptr< ioda::ObsSpace > obsspace_
Forward declarations.
Definition: ObsAodExt.h:21
void testPrimitiveVariablesIterator(const eckit::LocalConfiguration &conf)
void testPrimitiveVariables(const eckit::LocalConfiguration &conf)
Test a range-based for loop over the PrimitiveVariables range.
CASE("ufo/DataExtractor/bilinearinterp/float_linear")
Definition: RunCRTM.h:27