FV3 Bundle
OdbApiIO.cc
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 #include <iostream>
9 #include <string>
10 
11 #include "oops/util/abor1_cpp.h"
12 #include "oops/util/datetime_f.h"
13 #include "oops/util/Duration.h"
14 #include "oops/util/Logger.h"
15 
16 #include "fileio/OdbApiIO.h"
17 
18 ////////////////////////////////////////////////////////////////////////
19 // Implementation of IodaIO for ODB API.
20 ////////////////////////////////////////////////////////////////////////
21 
22 // This way of logging errors is taken from the ODB API C Example code. May
23 // want to modify.
24 #define checkRC(return_code, message, db_) { \
25  if (return_code != ODBQL_OK) { \
26  oops::Log::error() << __func__ << ": ODB ERROR: " << message << std::endl; \
27  odbql_close(db_); \
28  } \
29 }
30 
31 namespace ioda {
32 // -----------------------------------------------------------------------------
33 /*!
34  * \details This constructor will open the ODB API file. If opening in read
35  * mode, the parameters nlocs, nobs, nrecs and nvars will be set
36  * by querying the size of dimensions of the same names in the input
37  * file. If opening in write mode, the parameters will be set from the
38  * same named arguements to this constructor.
39  *
40  * \param[in] FileName Path to the ODB API file
41  * \param[in] FileMode "r" for read, "w" for overwrite to an existing file
42  * and "W" for create and write to a new file.
43  * \param[in] Nlocs Number of unique locations in the obs data.
44  * \param[in] Nobs Number of unique observations in the obs data.
45  * \param[in] Nrecs Number of unique records in the obs data. Records are
46  * atomic units that will remain intact when obs are
47  * distributed across muliple process elements. A single
48  * radiosonde sounding would be an example.
49  * \param[in] Nvars Number of unique varibles in the obs data.
50  */
51 OdbApiIO::OdbApiIO(const std::string & FileName, const std::string & FileMode,
52  const std::size_t & Nlocs, const std::size_t & Nobs,
53  const std::size_t & Nrecs, const std::size_t & Nvars) {
54  // Set the data members to the file name, file mode and provide a trace message.
55  fname_ = FileName;
56  fmode_ = FileMode;
57  nlocs_ = Nlocs;
58  nobs_ = Nobs;
59  nrecs_ = Nrecs;
60  nvars_ = Nvars;
61  oops::Log::trace() << __func__ << " fname_: " << fname_ << " fmode_: " << fmode_ << std::endl;
62 
63  // Open the file. The fmode_ values that are recognized are:
64  // "r" - read
65  // "w" - write, disallow overriting an existing file
66  // "W" - write, allow overwriting an existing file
67  odbql_stmt *res = nullptr;
68  int rc = ODBQL_OK;
69  int i, column, number_of_columns;
70 
71  if (fmode_ == "r") {
72  // int i, column, number_of_columns;
73  // long long number_of_rows = 0, number_of_rows_in_current_dataset = 0;
74  odbql_stmt *res;
75 
76  // Uncomment code below if _db is later changed to unique_ptr
77  // odbql *temp_db;
78  // rc = odbql_open(fname_.c_str(), &temp_db);
79  rc = odbql_open(fname_.c_str(), &db_);
80  if (rc != ODBQL_OK) {
81  std::string errmsg = "OdbApi constructor cannot open file: " + fname_
82  + ". Return code: " + std::to_string(rc);
83  oops::Log::error() << __func__ << ": " << errmsg << std::endl; \
84  ABORT(errmsg);
85  }
86  // db_.reset(temp_db);
87  } else if (fmode_ == "w") {
88  oops::Log::error() << __func__ << ": Unimplemented FileMode: " << fmode_ << std::endl;
89  ABORT("Unimplemented file mode 'w' for OdbApiIO constructor");
90  } else if (fmode_ == "W") {
91  oops::Log::error() << __func__ << ": Unimplemented FileMode: " << fmode_ << std::endl;
92  ABORT("Unimplemented file mode 'W' for OdbApiIO constructor");
93  } else {
94  oops::Log::error() << __func__ << ": Unrecognized FileMode: " << fmode_ << std::endl;
95  oops::Log::error() << __func__ << ": Must use one of: 'r', 'w', 'W'" << std::endl;
96  ABORT("Unrecognized file mode for OdbApiIO constructor");
97  }
98 
99  // When in read mode, the constructor is responsible for setting
100  // the data members nlocs_, nobs_, nrecs_ and nvars_.
101  //
102 
103  if (fmode_ == "r") {
104  /* Current code is for radiosonde data only and makes the following big
105  assumptions about the format of the ODB API database file:
106  * one location per row
107  * one variable per location
108  * nrecs_ == nlocs_
109  Keeping all these assumptions is probably untenable for the long-term. */
110 
111  nlocs_ = 0;
112 
113  std::string sqlstmt = "SELECT count(*) FROM '" + fname_ + "';";
114  oops::Log::trace() << __func__ << " sql statement: " << sqlstmt << std::endl;
115 
116  rc = odbql_prepare_v2(db_, sqlstmt.c_str(), -1, &res, 0);
117  checkRC(rc, "Failed to prepare statement to count records.", db_);
118  // number_of_columns = odbql_column_count(res);
119  // int columnType = odbql_column_type(res, 0);
120  // oops::Log::trace() << __func__ << " number of columns: "
121  // << std::to_string(number_of_columns) << std::endl;
122  // oops::Log::trace() << __func__ << " column type: " << std::to_string(columnType)
123  // << std::endl;
124 
125  rc = odbql_step(res);
126  // oops::Log::trace() << __func__ << " return code from odbql_step: "
127  // << std::to_string(rc) << std::endl;
128  if (rc == ODBQL_ROW) {
129  odbql_value* pv = odbql_column_value(res, 0);
130  if (pv != 0) {
131  nlocs_ = odbql_value_double(pv);
132  oops::Log::trace() << __func__ << " nlocs_ set to: "
133  << std::to_string(nlocs_) << std::endl;
134  } else {
135  oops::Log::error() << __func__ << " unexpected pv null. " << std::endl;
136  }
137  }
138 
139  rc = odbql_finalize(res);
140  checkRC(rc, "odbql_finalize failed.", db_);
141  res = nullptr;
142 
143  nrecs_ = nlocs_; // Assumption for now
144  nvars_ = 1; // Hardcoded for now
145  nobs_ = nlocs_ * nvars_;
146  }
147 /*
148  // When in write mode, create dimensions in the output file based on
149  // nlocs_, nobs_, nrecs_, nvars_.
150  if ((fmode_ == "W") || (fmode_ == "w")) {
151  ncfile_->addDim("nlocs", nlocs_);
152  ncfile_->addDim("nobs", nobs_);
153  ncfile_->addDim("nrecs", nrecs_);
154  ncfile_->addDim("nvars", nvars_);
155  }
156 */
157  }
158 
159 // -----------------------------------------------------------------------------
160 
162  oops::Log::trace() << __func__ << " fname_: " << fname_ << std::endl;
163  if (db_ != nullptr) {
164  // Uncomment code below if we can later make db_ a unique_ptr
165  // int rc = odbql_close(db_.get());
166  // checkRC(rc, "odbql_close failed", db_.get());
167  // db_.reset();
168  int rc = odbql_close(db_);
169  checkRC(rc, "odbql_close failed", db_);
170  db_ = nullptr;
171  }
172 }
173 
174 
175 // -----------------------------------------------------------------------------
176 
177 /*!
178  * \details This method selects the data in the column called VarName and loads
179  * it into the returned array VarData. The caller is responsible for
180  * allocating the memory for the VarData array.
181  *
182  * \param[in] VarName Name of variable to select
183  * \param[in/out] VarData Pointer to the array where the data is loaded.
184  */
185 template <class T>
186 void OdbApiIO::ReadVarTemplate(const std::string & VarName, T* VarData) {
187  oops::Log::trace() << __func__ << " VarName: " << VarName << std::endl;
188  int rc;
189  odbql_stmt *res = nullptr;
190 
191  std::string sql = "SELECT " + VarName + " FROM '" + fname_ + "';";
192  oops::Log::trace() << __func__ << " sql: " << sql << std::endl;
193 
194  rc = odbql_prepare_v2(db_, sql.c_str(), -1, &res, 0);
195  if (rc != ODBQL_OK) {
196  std::string errorString = "ODB ERROR: error when preparing SQL statemtnt: " +
197  sql;
198  oops::Log::error() << __func__ << ": " << errorString << std::endl;
199  res = nullptr;
200  ABORT(errorString); // No way to return errors to ReadVar caller,
201  // so we have to just abort.
202  }
203 
204  int columnType = odbql_column_type(res, 0);
205  /* if (columnType != expectedType) {
206  // TODO(sv): review if this is the desired behavior for this situation.
207  std::string errorString = "WARNING: Data type for '" + VarName + "' in file " +
208  fname_ + " does not match the type of the allocated memory. Requires cast.";
209  oops::Log::error() << __func__ << ": " << errorString << std::endl;
210  }*/
211  int index = 0;
212  odbql_value* pv = nullptr;
213 
214  while (((rc = odbql_step(res)) != ODBQL_DONE) && (index < nlocs_)) {
215  if (rc == ODBQL_ROW) {
216  pv = odbql_column_value(res, 0);
217  if (pv == nullptr) {
218  std::string errorString = "ODB ERROR: unexpected NULL in a column of file: " + fname_;
219  oops::Log::error() << __func__ << errorString << std::endl;
220  odbql_finalize(res);
221  ABORT(errorString); // No way to return errors to ReadVar caller,
222  // so we have to just abort.
223  }
224  /* if (RetrieveColValPtr(res, 0, &pv)) {
225  VarData[index] = odbql_value_int(pv);
226  index++;
227  }*/
228  } else {
229  oops::Log::error() << __func__ << ": odbql_step returned unimplemented code: " <<
230  std::to_string(rc) << " in file " << fname_ << std::endl;
231  odbql_finalize(res);
232  ABORT("Encountered unimplemented odbql_step return code.");
233  }
234 
235  // put the value in the return array
236  // We cast it to the right type for the array and hope the caller
237  // got the type right.
238  switch (columnType) {
239  case ODBQL_INTEGER:
240  VarData[index] = (T)odbql_value_int(pv);
241  break;
242  case ODBQL_FLOAT:
243  // TODO(sv): Possible overflow or truncation happening here if T is int or float
244  VarData[index] = (T)odbql_value_double(pv);
245  break;
246  default:
247  std::string errorString = "Unimplemented data type for '" +
248  VarName + "' in file " + fname_;
249  oops::Log::error() << __func__ << ": " << errorString << std::endl;
250  odbql_finalize(res);
251  res = nullptr;
252  ABORT(errorString);
253  break;
254  }
255  index++;
256  }
257  oops::Log::trace() << __func__ << "finished sql: " << sql << std::endl;
258  // TODO(sv): Validate that we came to the end of the table data and filled up the array
259  rc = odbql_finalize(res);
260  checkRC(rc, "odbql_finalize failed.", db_);
261  res = nullptr;
262 }
263 
264 // -----------------------------------------------------------------------------
265 
266 void OdbApiIO::ReadVar(const std::string & VarName, int* VarData) {
267  oops::Log::trace() << __func__ << " VarName: " << VarName << std::endl;
268  ReadVarTemplate<int>(VarName, VarData);
269 }
270 
271 // -----------------------------------------------------------------------------
272 
273 void OdbApiIO::ReadVar(const std::string & VarName, float* VarData) {
274  oops::Log::trace() << __func__ << " VarName: " << VarName << std::endl;
275  ReadVarTemplate<float>(VarName, VarData);
276 }
277 
278 // -----------------------------------------------------------------------------
279 
280 void OdbApiIO::ReadVar(const std::string & VarName, double* VarData) {
281  oops::Log::trace() << __func__ << " VarName: " << VarName << std::endl;
282  ReadVarTemplate<double>(VarName, VarData);
283 }
284 
285 // -----------------------------------------------------------------------------
286 
287 void OdbApiIO::WriteVar(const std::string & VarName, int* VarData) {
288  oops::Log::trace() << __func__ << " VarName: " << VarName << std::endl;
289 /*
290  netCDF::NcVar ncvar_ = ncfile_->getVar(VarName);
291  if (ncvar_.isNull()) {
292  // Var does not exist, so create it
293  ncvar_ = ncfile_->addVar(VarName, netCDF::ncInt, ncfile_->getDim("nlocs"));
294  }
295  ncvar_.putVar(VarData);*/
296 }
297 
298 // -----------------------------------------------------------------------------
299 
300 void OdbApiIO::WriteVar(const std::string & VarName, float* VarData) {
301  oops::Log::trace() << __func__ << " VarName: " << VarName << std::endl;
302 
303 /* netCDF::NcVar ncvar_ = ncfile_->getVar(VarName);
304  if (ncvar_.isNull()) {
305  // Var does not exist, so create it
306  ncvar_ = ncfile_->addVar(VarName, netCDF::ncFloat, ncfile_->getDim("nlocs"));
307  }
308  ncvar_.putVar(VarData);*/
309 }
310 
311 // -----------------------------------------------------------------------------
312 
313 void OdbApiIO::WriteVar(const std::string & VarName, double* VarData) {
314  oops::Log::trace() << __func__ << " VarName: " << VarName << std::endl;
315 
316  /*netCDF::NcVar ncvar_ = ncfile_->getVar(VarName);
317  if (ncvar_.isNull()) {
318  // Var does not exist, so create it
319  ncvar_ = ncfile_->addVar(VarName, netCDF::ncDouble, ncfile_->getDim("nlocs"));
320  }
321  ncvar_.putVar(VarData);*/
322 }
323 
324 // -----------------------------------------------------------------------------
325 
326 void OdbApiIO::ReadDateTime(int* VarDate, int* VarTime) {
327  oops::Log::trace() << __func__ << std::endl;
328 
329  // Right now we have to hard-code the names of the date/time columns.
330  const std::string dateColumnName = "date@odb";
331  const std::string timeColumnName = "time@odb";
332 
333  ReadVarTemplate<int>(dateColumnName, VarDate);
334  ReadVarTemplate<int>(timeColumnName, VarTime);
335 }
336 
337 // -----------------------------------------------------------------------------
338 
339 void OdbApiIO::print(std::ostream & os) const {
340  os << "OdbApi: In " << __FILE__ << " @ " << __LINE__ << std::endl;
341 }
342 
343 
344 } // namespace ioda
std::size_t nobs_
number of unique observations
l_size ! loop over number of fields ke do je do i
std::size_t nlocs_
number of unique locations
odbql * db_
pointer to odbql object
Definition: OdbApiIO.h:83
OdbApiIO(const std::string &FileName, const std::string &FileMode, const std::size_t &Nlocs, const std::size_t &Nobs, const std::size_t &Nrecs, const std::size_t &Nvars)
Definition: OdbApiIO.cc:51
std::string fname_
file name
#define checkRC(return_code, message, db_)
Definition: OdbApiIO.cc:24
void WriteVar(const std::string &VarName, int *VarData)
Definition: OdbApiIO.cc:287
************************************************************************GNU Lesser General Public License **This file is part of the GFDL Flexible Modeling System(FMS). ! *! *FMS is free software without even the implied warranty of MERCHANTABILITY or *FITNESS FOR A PARTICULAR PURPOSE See the GNU General Public License *for more details **You should have received a copy of the GNU Lesser General Public *License along with FMS If see< http:! ***********************************************************************!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !! MPP_TRANSMIT !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! subroutine MPP_TRANSMIT_(put_data, put_len, to_pe, get_data, get_len, from_pe, block, tag, recv_request, send_request)!a message-passing routine intended to be reminiscent equally of both MPI and SHMEM!put_data and get_data are contiguous MPP_TYPE_ arrays!at each call, your put_data array is put to to_pe 's get_data! your get_data array is got from from_pe 's put_data!i.e we assume that typically(e.g updating halo regions) each PE performs a put _and_ a get!special PE designations:! NULL_PE:to disable a put or a get(e.g at boundaries)! ANY_PE:if remote PE for the put or get is to be unspecific! ALL_PES:broadcast and collect operations(collect not yet implemented)!ideally we would not pass length, but this f77-style call performs better(arrays passed by address, not descriptor)!further, this permits< length > contiguous words from an array of any rank to be passed(avoiding f90 rank conformance check)!caller is responsible for completion checks(mpp_sync_self) before and after integer, intent(in) ::put_len, to_pe, get_len, from_pe MPP_TYPE_, intent(in) ::put_data(*) MPP_TYPE_, intent(out) ::get_data(*) logical, intent(in), optional ::block integer, intent(in), optional ::tag integer, intent(out), optional ::recv_request, send_request logical ::block_comm integer ::i MPP_TYPE_, allocatable, save ::local_data(:) !local copy used by non-parallel code(no SHMEM or MPI) integer ::comm_tag integer ::rsize if(.NOT.module_is_initialized) call mpp_error(FATAL, 'MPP_TRANSMIT:You must first call mpp_init.') if(to_pe.EQ.NULL_PE .AND. from_pe.EQ.NULL_PE) return block_comm=.true. if(PRESENT(block)) block_comm=block if(debug) then call SYSTEM_CLOCK(tick) write(stdout_unit,'(a, i18, a, i6, a, 2i6, 2i8)')&'T=', tick, ' PE=', pe, ' MPP_TRANSMIT begin:to_pe, from_pe, put_len, get_len=', to_pe, from_pe, put_len, get_len end if comm_tag=DEFAULT_TAG if(present(tag)) comm_tag=tag!do put first and then get if(to_pe.GE.0 .AND. to_pe.LT.npes) then!use non-blocking sends if(debug .and.(current_clock.NE.0)) call SYSTEM_CLOCK(start_tick)!z1l:truly non-blocking send.! if(request(to_pe).NE.MPI_REQUEST_NULL) then !only one message from pe-> to_pe in queue *PE waiting for to_pe ! call error else get_len so only do gets but you cannot have a pure get with MPI call a get means do a wait to ensure put on remote PE is complete error call increase mpp_nml request_multiply call MPP_TRANSMIT get_len end if return end subroutine MPP_TRANSMIT_ ! MPP_BROADCAST ! subroutine but that doesn t allow !broadcast to a subset of PEs This version and mpp_transmit will remain !backward compatible intent(inout) & T
integer error
Definition: mpp.F90:1310
void ReadVar(const std::string &VarName, int *VarData)
Definition: OdbApiIO.cc:266
std::string fmode_
file mode ("r" -> read, "w" -> overwrite, "W" -> create and write)
void ReadDateTime(int *VarDate, int *VarTime)
Definition: OdbApiIO.cc:326
void ReadVarTemplate(const std::string &VarName, T *VarData)
Definition: OdbApiIO.cc:186
std::size_t nrecs_
number of unique records
void print(std::ostream &os) const
Definition: OdbApiIO.cc:339
std::size_t nvars_
number of unique variables