IODA Bundle
IodaDescription.cpp
Go to the documentation of this file.
1 /*
2  * (C) Copyright 2020 NOAA/NWS/NCEP/EMC
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 #include "IodaDescription.h"
9 
10 #include <boost/algorithm/string.hpp>
11 
12 #include "eckit/exception/Exceptions.h"
13 
14 namespace
15 {
16  namespace ConfKeys
17  {
18  const char* Backend = "backend";
19  const char* Filename = "obsdataout";
20  const char* Dimensions = "dimensions";
21  const char* Variables = "variables";
22  const char* Globals = "globals";
23 
24  namespace Dimension
25  {
26  const char* Name = "name";
27  const char* Size = "size";
28  } // Dimension
29 
30  namespace Variable
31  {
32  const char* Name = "name";
33  const char* Source = "source";
34  const char* Dimensions = "dimensions";
35  const char* LongName = "longName";
36  const char* Units = "units";
37  const char* Range = "range";
38  const char* Coords = "coordinates";
39  const char* Chunks = "chunks";
40  const char* CompressionLevel = "compressionLevel";
41  } // namespace Variable
42 
43  namespace Global
44  {
45  const char* Name = "name";
46  const char* Value = "value";
47  const char* Type = "type";
48  const char* StringType = "string";
49  const char* FloatType = "float";
50  const char* FloatVectorType = "floatVector";
51  const char* IntType = "int";
52  const char* IntVectorType = "intVector";
53  } // namespace Global
54  } // namespace ConfKeys
55 } // namespace
56 
57 namespace Ingester
58 {
59  IodaDescription::IodaDescription(const eckit::Configuration& conf)
60  {
61  if (conf.has(ConfKeys::Backend))
62  {
63  setBackend(conf.getString(ConfKeys::Backend));
64  }
65  else
66  {
67  throw eckit::BadParameter("Forgot to specify the ioda::backend.");
68  }
69 
70  if (conf.has(ConfKeys::Filename))
71  {
72  filepath_ = conf.getString(ConfKeys::Filename);
73  }
74  else
75  {
77  {
78  filepath_ = "";
79  }
80  else
81  {
82  throw eckit::BadParameter("Filename is required with the configured backend.");
83  }
84  }
85 
86  auto dimConfs = conf.getSubConfigurations(ConfKeys::Dimensions);
87  if (dimConfs.size() == 0)
88  {
89  std::stringstream errStr;
90  errStr << "ioda::dimensions must contain a list of dimensions!";
91  throw eckit::BadParameter(errStr.str());
92  }
93 
94  for (const auto& dimConf : dimConfs)
95  {
97  scale.name = dimConf.getString(ConfKeys::Dimension::Name);
98  scale.size = dimConf.getString(ConfKeys::Dimension::Size);
99 
100  addDimension(scale);
101  }
102 
103  auto varConfs = conf.getSubConfigurations(ConfKeys::Variables);
104  if (varConfs.size() == 0)
105  {
106  std::stringstream errStr;
107  errStr << "ioda::variables must contain a list of variables!";
108  throw eckit::BadParameter(errStr.str());
109  }
110 
111  for (const auto& varConf : varConfs)
112  {
114  variable.name = varConf.getString(ConfKeys::Variable::Name);
115  variable.source = varConf.getString(ConfKeys::Variable::Source);
116  variable.dimensions = varConf.getStringVector(ConfKeys::Variable::Dimensions);
117  variable.longName = varConf.getString(ConfKeys::Variable::LongName);
118  variable.units = varConf.getString(ConfKeys::Variable::Units);
119 
120  if (varConf.has(ConfKeys::Variable::Coords))
121  {
122  variable.coordinates =
123  std::make_shared<std::string> (varConf.getString(ConfKeys::Variable::Coords));
124  }
125  else
126  {
127  variable.coordinates = nullptr;
128  }
129 
130  variable.range = nullptr;
131  if (varConf.has(ConfKeys::Variable::Range))
132  {
133  auto range = std::make_shared<Range>();
134  range->start = std::stoi(varConf.getStringVector(ConfKeys::Variable::Range)[0]);
135  range->end = std::stoi(varConf.getStringVector(ConfKeys::Variable::Range)[1]);
136  variable.range = range;
137  }
138 
139  variable.chunks = {};
140  if (varConf.has(ConfKeys::Variable::Chunks))
141  {
142  auto chunks = std::vector<ioda::Dimensions_t>();
143 
144  for (const auto& chunkStr : varConf.getStringVector(ConfKeys::Variable::Chunks))
145  {
146  chunks.push_back(std::stoi(chunkStr));
147  }
148 
149  variable.chunks = chunks;
150  }
151 
152  variable.compressionLevel = 6;
153  if (varConf.has(ConfKeys::Variable::CompressionLevel))
154  {
155  int compressionLevel = varConf.getInt(ConfKeys::Variable::CompressionLevel);
156  if (compressionLevel < 0 || compressionLevel > 9)
157  {
158  throw eckit::BadParameter("GZip compression level must be a number 0-9");
159  }
160 
161  variable.compressionLevel = varConf.getInt(ConfKeys::Variable::CompressionLevel);
162  }
163 
165  }
166 
167  if (conf.has(ConfKeys::Globals))
168  {
169  auto globalConfs = conf.getSubConfigurations(ConfKeys::Globals);
170  for (const auto& globalConf : globalConfs)
171  {
172  if (globalConf.getString(ConfKeys::Global::Type) == \
174  {
175  auto global = std::make_shared<GlobalDescription<std::string>>();
176  global->name = globalConf.getString(ConfKeys::Global::Name);
177  global->value = globalConf.getString(ConfKeys::Global::Value);
178  addGlobal(global);
179  }
180  else if (globalConf.getString(ConfKeys::Global::Type) == \
182  {
183  auto global = std::make_shared<GlobalDescription<float>>();
184  global->name = globalConf.getString(ConfKeys::Global::Name);
185  global->name = globalConf.getFloat(ConfKeys::Global::Name);
186  addGlobal(global);
187  }
188  else if (globalConf.getString(ConfKeys::Global::Type) == \
190  {
191  auto global = std::make_shared<GlobalDescription<std::vector<float>>>();
192  global->name = globalConf.getString(ConfKeys::Global::Name);
193  global->value = globalConf.getFloatVector(ConfKeys::Global::Value);
194  addGlobal(global);
195  }
196  else if (globalConf.getString(ConfKeys::Global::Type) == \
198  {
199  auto global = std::make_shared<GlobalDescription<int>>();
200  global->name = globalConf.getString(ConfKeys::Global::Name);
201  global->value = globalConf.getInt(ConfKeys::Global::Value);
202  addGlobal(global);
203  }
204  else if (globalConf.getString(ConfKeys::Global::Type) == \
206  {
207  auto global = std::make_shared<GlobalDescription<std::vector<int>>>();
208  global->name = globalConf.getString(ConfKeys::Global::Name);
209  global->value = globalConf.getIntVector(ConfKeys::Global::Value);
210  addGlobal(global);
211  }
212  else
213  {
214  throw eckit::BadParameter("Unsupported global attribute type");
215  }
216  }
217  }
218  }
219 
221  {
222  dimensions_.push_back(scale);
223  }
224 
226  {
227  variables_.push_back(variable);
228  }
229 
230  void IodaDescription::addGlobal(const std::shared_ptr<GlobalDescriptionBase>& global)
231  {
232  globals_.push_back(global);
233  }
234 
235  void IodaDescription::setBackend(const std::string& backend)
236  {
237  auto backend_lowercase = boost::algorithm::to_lower_copy(backend);
238  if (backend_lowercase == "netcdf")
239  {
241  }
242  else if (backend_lowercase == "inmemory" || backend_lowercase == "in-memory")
243  {
245  }
246  else
247  {
248  throw eckit::BadParameter("Unknown ioda::backend specified.");
249  }
250  }
251 } // namespace Ingester
std::string filepath_
The relative path of the output file to create.
void addDimension(const DimensionDescription &scale)
Add Dimension defenition.
VariableDescriptions variables_
Collection of defined variables.
void addVariable(const VariableDescription &variable)
Add Variable defenition.
void setBackend(const ioda::Engines::BackendNames &backend)
void addGlobal(const std::shared_ptr< GlobalDescriptionBase > &global)
Add Globals defenition.
ioda::Engines::BackendNames backend_
The backend type to use.
GlobalDescriptions globals_
Collection of defined globals.
DimDescriptions dimensions_
Collection of defined dimensions.
@ Hdf5File
HDF5 file access.
@ ObsStore
ObsStore in-memory.
character(maxvarlen) function variable(this, jj)