IODA
test/io/ObsFrameWrite.h
Go to the documentation of this file.
1 /*
2  * (C) Copyright 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_IO_OBSFRAMEWRITE_H_
9 #define TEST_IO_OBSFRAMEWRITE_H_
10 
11 #include <algorithm>
12 #include <functional>
13 #include <map>
14 #include <memory>
15 #include <numeric>
16 #include <string>
17 #include <utility>
18 #include <vector>
19 
20 #define ECKIT_TESTING_SELF_REGISTER_CASES 0
21 
22 #include "eckit/config/LocalConfiguration.h"
23 #include "eckit/testing/Test.h"
24 
25 #include "oops/mpi/mpi.h"
26 #include "oops/runs/Test.h"
27 #include "oops/test/TestEnvironment.h"
28 #include "oops/util/DateTime.h"
29 #include "oops/util/FloatCompare.h"
30 #include "oops/util/Logger.h"
31 
32 #include "ioda/core/IodaUtils.h"
33 #include "ioda/Engines/HH.h"
34 #include "ioda/io/ObsFrameWrite.h"
35 #include "ioda/Layout.h"
36 #include "ioda/ObsGroup.h"
37 #include "ioda/ObsSpaceParameters.h"
39 
40 namespace ioda {
41 namespace test {
42 
43 // -----------------------------------------------------------------------------
44 // Helper Functions
45 // -----------------------------------------------------------------------------
46 
47 // -----------------------------------------------------------------------------
48 ObsGroup buildTestObsGroup(const std::vector<eckit::LocalConfiguration> & dimConfigs,
49  const std::vector<eckit::LocalConfiguration> & varConfigs) {
50  // create an ObsGroup with an in-memory backend
51  Engines::BackendNames backendName;
56  backendParams.allocBytes = 1024*1024*50;
57  backendParams.flush = false;
58 
59  backendName = Engines::BackendNames::ObsStore; // Hdf5Mem; ObsStore;
60  Group backend = constructBackend(backendName, backendParams);
61 
62  // Add the dimension scales
64  for (std::size_t i = 0; i < dimConfigs.size(); ++i) {
65  std::string dimName = dimConfigs[i].getString("name");
66  Dimensions_t dimSize = dimConfigs[i].getInt("size");
67  bool isUnlimited = dimConfigs[i].getBool("unlimited", false);
68 
69  if (isUnlimited) {
70  newDims.push_back(
71  ioda::NewDimensionScale<int>(
72  dimName, dimSize, ioda::Unlimited, dimSize));
73  } else {
74  newDims.push_back(
75  ioda::NewDimensionScale<int>(
76  dimName, dimSize, dimSize, dimSize));
77  }
78  }
79  ObsGroup obsGroup = ObsGroup::generate(backend, newDims);
80 
81  // Create variables
82  for (std::size_t j = 0; j < varConfigs.size(); ++j) {
83  std::string varName = varConfigs[j].getString("name");
84  std::string varType = varConfigs[j].getString("type");
85  std::vector<std::string> varDimNames =
86  varConfigs[j].getStringVector("dims");
87 
88  std::vector<ioda::Variable> varDims;
89  for (std::size_t idim = 0; idim < varDimNames.size(); ++idim) {
90  varDims.push_back(obsGroup.vars.open(varDimNames[idim]));
91  }
92 
94  params.chunk = true;
95  params.compressWithGZIP();
96  if (varType == "int") {
97  params.setFillValue<int>(-999);
98  obsGroup.vars.createWithScales<int>(varName, varDims, params)
99  .write<int>(varConfigs[j].getIntVector("values"));
100  } else if (varType == "float") {
101  params.setFillValue<float>(-999);
102  obsGroup.vars.createWithScales<float>(varName, varDims, params)
103  .write<float>(varConfigs[j].getFloatVector("values"));
104  } else if (varType == "string") {
105  params.setFillValue<std::string>("fill");
106  obsGroup.vars.createWithScales<std::string>(varName, varDims, params)
107  .write<std::string>(varConfigs[j].getStringVector("values"));
108  }
109  }
110  return obsGroup;
111 }
112 
113 // -----------------------------------------------------------------------------
114 void frameWrite(ObsFrameWrite & obsFrame, eckit::LocalConfiguration & testConfig,
115  const Has_Variables & sourceVars, const VarNameObjectList & varList,
116  const VarNameObjectList & dimVarList,
117  const VarDimMap & varDimMap, const Dimensions_t maxVarSize) {
118  std::vector<eckit::LocalConfiguration> writeVarConfigs =
119  testConfig.getSubConfigurations("write variables");
120 
121  int iframe = 0;
122  for (obsFrame.frameInit(varList, dimVarList, varDimMap, maxVarSize);
123  obsFrame.frameAvailable(); obsFrame.frameNext(varList)) {
124  Dimensions_t frameStart = obsFrame.frameStart();
125  oops::Log::debug() << "testWrite: Frame number: " << iframe << std::endl
126  << " frameStart: " << frameStart << std::endl;
127 
128  // Write the test variables
129  for (std::size_t j = 0; j < writeVarConfigs.size(); ++j) {
130  std::string varName = writeVarConfigs[j].getString("name");
131  std::string varType = writeVarConfigs[j].getString("type");
132  std::vector<std::string> varDimNames =
133  writeVarConfigs[j].getStringVector("dims");
134 
135  ioda::Variable var = obsFrame.vars().open(varName);
136 
137  Dimensions_t frameCount = obsFrame.frameCount(varName);
138  if (frameCount > 0) {
139  oops::Log::debug() << " Variable: " << varName
140  << ", frameCount: " << frameCount << std::endl;
141 
142  if (varType == "int") {
143  std::vector<int> values =
144  writeVarConfigs[j].getIntVector("values");
145  std::vector<int> varValues(values.begin() + frameStart,
146  values.begin() + frameStart + frameCount);
147  obsFrame.writeFrameVar(varName, varValues);
148  } else if (varType == "float") {
149  std::vector<float> values =
150  writeVarConfigs[j].getFloatVector("values");
151  std::vector<float> varValues(values.begin() + frameStart,
152  values.begin() + frameStart + frameCount);
153  obsFrame.writeFrameVar(varName, varValues);
154  } else if (varType == "string") {
155  std::vector<std::string> values =
156  writeVarConfigs[j].getStringVector("values");
157  std::vector<std::string> varValues(values.begin() + frameStart,
158  values.begin() + frameStart + frameCount);
159  obsFrame.writeFrameVar(varName, varValues);
160  }
161  }
162  }
163  iframe++;
164  }
165 }
166 
167 // -----------------------------------------------------------------------------
168 // Test Functions
169 // -----------------------------------------------------------------------------
170 
171 // -----------------------------------------------------------------------------
172 void testWrite() {
173  const eckit::LocalConfiguration conf(::test::TestEnvironment::config());
174  std::vector<eckit::LocalConfiguration> confOspaces = conf.getSubConfigurations("observations");
175  util::DateTime bgn(::test::TestEnvironment::config().getString("window begin"));
176  util::DateTime end(::test::TestEnvironment::config().getString("window end"));
177 
178  for (std::size_t i = 0; i < confOspaces.size(); ++i) {
179  eckit::LocalConfiguration obsConfig;
180  eckit::LocalConfiguration testConfig;
181  confOspaces[i].get("obs space", obsConfig);
182  confOspaces[i].get("test data", testConfig);
183  oops::Log::trace() << "ObsFrame testWrite obs space config: " << i << ": "
184  << obsConfig << std::endl;
185  oops::Log::trace() << "ObsFrame testWrite test data config: " << i << ": "
186  << testConfig << std::endl;
187 
188  ioda::ObsTopLevelParameters topParams;
189  topParams.validateAndDeserialize(obsConfig);
190  ioda::ObsSpaceParameters obsParams(topParams, bgn, end,
191  oops::mpi::world(), oops::mpi::myself());
192 
193  if (obsParams.top_level_.obsOutFile.value() != boost::none) {
194  // Get dimensions and variables sub configurations
195  std::vector<eckit::LocalConfiguration> writeDimConfigs =
196  testConfig.getSubConfigurations("write dimensions");
197  std::vector<eckit::LocalConfiguration> writeVarConfigs =
198  testConfig.getSubConfigurations("write variables");
199 
200  // Create an in-memory ObsGroup containing the test dimensions and variables
201  ObsGroup testObsGroup = buildTestObsGroup(writeDimConfigs, writeVarConfigs);
202 
203  // Form lists of regular and dimension scale variables
204  VarNameObjectList varList;
205  VarNameObjectList dimVarList;
206  VarDimMap dimsAttachedToVars;
207  Dimensions_t maxVarSize;
208  collectVarDimInfo(testObsGroup, varList, dimVarList,
209  dimsAttachedToVars, maxVarSize);
210 
211  // Record dimension scale variables for the output file creation.
212  for (auto & dimNameObject : dimVarList) {
213  std::string dimName = dimNameObject.first;
214  Dimensions_t dimSize = dimNameObject.second.getDimensions().dimsCur[0];
215  Dimensions_t dimMaxSize = dimSize;
216  if (dimName == "nlocs") {
217  dimMaxSize = Unlimited;
218  }
219  obsParams.setDimScale(dimName, dimSize, dimMaxSize, dimSize);
220  }
221 
222  // Record the maximum variable size
223  obsParams.setMaxVarSize(maxVarSize);
224 
225  // Output constructor
226  ObsFrameWrite obsFrame(obsParams);
227 
228  // Write contents of file
229  frameWrite(obsFrame, testConfig, testObsGroup.vars, varList, dimVarList,
230  dimsAttachedToVars, maxVarSize);
231  obsFrame.ioUpdateVarDimInfo();
232 
233  // Check if all the variables got written into the file
234  // Dimension scale variables
235  std::vector<std::string> expectedDimList;
236  for (size_t i = 0; i < writeDimConfigs.size(); ++i) {
237  expectedDimList.push_back(writeDimConfigs[i].getString("name"));
238  }
239  std::sort(expectedDimList.begin(), expectedDimList.end());
240  std::vector<std::string> dimList;
241  for (auto & dimVarNameObject : obsFrame.ioDimVarList()) {
242  dimList.push_back(dimVarNameObject.first);
243  }
244  std::sort(dimList.begin(), dimList.end());
245  for (size_t i = 0; i < expectedDimList.size(); ++i) {
246  EXPECT_EQUAL(dimList[i], expectedDimList[i]);
247  }
248 
249  // Regular variables
250  std::vector<std::string> expectedVariableList;
251  for (size_t i = 0; i < writeVarConfigs.size(); ++i) {
252  expectedVariableList.push_back(writeVarConfigs[i].getString("name"));
253  }
254  std::sort(expectedVariableList.begin(), expectedVariableList.end());
255  std::vector<std::string> variableList;
256  for (auto & varNameObject : obsFrame.ioVarList()) {
257  variableList.push_back(varNameObject.first);
258  }
259  std::sort(variableList.begin(), variableList.end());
260  EXPECT_EQUAL(variableList, expectedVariableList);
261  }
262  }
263 }
264 
265 // -----------------------------------------------------------------------------
266 
267 class ObsFrameWrite : public oops::Test {
268  public:
270  virtual ~ObsFrameWrite() {}
271  private:
272  std::string testid() const override {return "test::ObsFrameWrite";}
273 
274  void register_tests() const override {
275  std::vector<eckit::testing::Test>& ts = eckit::testing::specification();
276 
277  ts.emplace_back(CASE("ioda/ObsFrameWrite/testWrite")
278  { testWrite(); });
279  }
280 
281  void clear() const override {}
282 };
283 
284 // -----------------------------------------------------------------------------
285 
286 } // namespace test
287 } // namespace ioda
288 
289 #endif // TEST_IO_OBSFRAMEWRITE_H_
HDF5 engine.
Contains definitions for how data are arranged in ioda internally.
Interfaces for ioda::ObsGroup and related classes.
Interfaces for ioda::Variable and related classes.
Groups are a new implementation of ObsSpaces.
Definition: Group.h:159
This class exists inside of ioda::Group and provides the interface to manipulating Variables.
An ObsGroup is a specialization of a ioda::Group. It provides convenience functions and guarantees th...
Definition: ObsGroup.h:32
static ObsGroup generate(Group &emptyGroup, const NewDimensionScales_t &fundamentalDims, std::shared_ptr< const detail::DataLayoutPolicy > layout=nullptr)
Create an empty ObsGroup and populate it with the fundamental dimensions.
Definition: ObsGroup.cpp:72
void setMaxVarSize(const Dimensions_t maxVarSize)
set the maximum variable size
ObsTopLevelParameters top_level_
sub groups of parameters
void setDimScale(const std::string &dimName, const Dimensions_t curSize, const Dimensions_t maxSize, const Dimensions_t chunkSize)
set a new dimension scale
oops::OptionalParameter< ObsFileOutParameters > obsOutFile
output specification by writing to a file
Variables store data!
Definition: Variable.h:680
Has_Variables vars
Use this to access variables.
Definition: Group.h:123
Variable createWithScales(const std::string &name, const std::vector< Variable > &dimension_scales, const VariableCreationParameters &params=VariableCreationParameters::defaulted< DataType >())
Convenience function to create a Variable from certain dimension scales.
virtual Variable open(const std::string &name) const
Open a Variable by name.
void register_tests() const override
std::string testid() const override
IODA_DL std::string genUniqueName()
Convenience function to generate a random file name.
Definition: HH.cpp:50
BackendNames
Backend names.
Definition: Factory.h:28
IODA_DL Group constructBackend(BackendNames name, BackendCreationParameters &params)
This is a simple factory style function that will instantiate a different backend based on a given na...
Definition: Factory.cpp:124
@ ObsStore
ObsStore in-memory.
@ Truncate_If_Exists
If the file already exists, overwrite it.
list newDims
Definition: 05-ObsGroup.py:95
CASE("Derived variable, unit conversion, and exception checking methods")
void frameWrite(ObsFrameWrite &obsFrame, eckit::LocalConfiguration &testConfig, const Has_Variables &sourceVars, const VarNameObjectList &varList, const VarNameObjectList &dimVarList, const VarDimMap &varDimMap, const Dimensions_t maxVarSize)
ObsGroup buildTestObsGroup(const std::vector< eckit::LocalConfiguration > &dimConfigs, const std::vector< eckit::LocalConfiguration > &varConfigs)
constexpr int Unlimited
Specifies that a dimension is resizable to infinity.
std::vector< std::pair< std::string, Variable > > VarNameObjectList
typedef for holding list of variable names with associated variable object
Definition: IodaUtils.h:30
std::vector< std::shared_ptr< NewDimensionScale_Base > > NewDimensionScales_t
std::map< std::string, std::vector< std::string > > VarDimMap
typedef for holding dim names attached to variables
Definition: IodaUtils.h:36
void collectVarDimInfo(const ObsGroup &obsGroup, VarNameObjectList &varObjectList, VarNameObjectList &dimVarObjectList, VarDimMap &dimsAttachedToVars, Dimensions_t &maxVarSize0)
collect variable and dimension information from a ioda ObsGroup
Definition: IodaUtils.cc:125
Used to specify backend creation-time properties.
Definition: Factory.h:59
Used to specify Variable creation-time properties.
Definition: Has_Variables.h:57