PISM, A Parallel Ice Sheet Model  stable v2.1-1-g6902d5502 committed by Ed Bueler on 2023-12-20 08:38:27 -0800
ParallelIO.cc
Go to the documentation of this file.
1 /* Copyright (C) 2019, 2020, 2023 PISM Authors
2  *
3  * This file is part of PISM.
4  *
5  * PISM is free software; you can redistribute it and/or modify it under the
6  * terms of the GNU General Public License as published by the Free Software
7  * Foundation; either version 3 of the License, or (at your option) any later
8  * version.
9  *
10  * PISM is distributed in the hope that it will be useful, but WITHOUT ANY
11  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
13  * details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with PISM; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18  */
19 
20 #include <cassert>
21 #include <cstring> // memset
22 
23 // Why do I need this???
24 #define _NETCDF
25 #include <pio.h>
26 
27 #include "pism/util/io/ParallelIO.hh"
28 
29 #include "pism/util/error_handling.hh"
30 #include "pism/util/io/pism_type_conversion.hh"
31 #include "pism/util/Grid.hh"
32 #include "pism/util/io/File.hh"
33 
34 namespace pism {
35 namespace io {
36 
37 static void check(const ErrorLocation &where, int return_code) {
38  char message[PIO_MAX_NAME + 1];
39  if (return_code != PIO_NOERR) {
40  PIOc_strerror(return_code, message);
41  throw RuntimeError(where, message);
42  }
43 }
44 
46  if (PIOc_iotype_available(PIO_IOTYPE_PNETCDF) and netcdf3) {
47  return PISM_PIO_PNETCDF;
48  }
49  if (PIOc_iotype_available(PIO_IOTYPE_NETCDF4P)) {
50  return PISM_PIO_NETCDF4P;
51  }
52  if (PIOc_iotype_available(PIO_IOTYPE_NETCDF4C)) {
53  return PISM_PIO_NETCDF4C;
54  }
55  // always available and supports all file formats
56  return PISM_PIO_NETCDF;
57 }
58 
59 
60 ParallelIO::ParallelIO(MPI_Comm com, int iosysid, io::Backend iotype)
61  : NCFile(com), m_iosysid(iosysid) {
62  assert(iosysid != -1);
63 
64  switch (iotype) {
65  case PISM_PIO_PNETCDF:
66  m_iotype = PIO_IOTYPE_PNETCDF;
67  break;
68  case PISM_PIO_NETCDF4P:
69  m_iotype = PIO_IOTYPE_NETCDF4P;
70  break;
71  case PISM_PIO_NETCDF4C:
72  m_iotype = PIO_IOTYPE_NETCDF4C;
73  break;
74  case PISM_PIO_NETCDF:
75  m_iotype = PIO_IOTYPE_NETCDF;
76  break;
77  default:
78  throw RuntimeError::formatted(PISM_ERROR_LOCATION, "invalid iotype in ParallelIO");
79  }
80 }
81 
83  (void)level;
84  // FIXME: it may make sense to implement this for PIO IO types using HDF5.
85 }
86 
87 void ParallelIO::open_impl(const std::string &filename, io::Mode mode) {
88  int open_mode = mode == io::PISM_READONLY ? PIO_NOWRITE : PIO_WRITE;
89 
90  int stat = PIOc_open(m_iosysid, filename.c_str(), open_mode, &m_file_id);
92 }
93 
94 void ParallelIO::create_impl(const std::string &filename) {
95 
96  int mode = NC_CLOBBER;
97  if (m_iotype == PIO_IOTYPE_PNETCDF) {
98  mode |= NC_64BIT_DATA;
99  }
100 
101  int stat = PIOc_createfile(m_iosysid, &m_file_id, &m_iotype, filename.c_str(), mode);
102  check(PISM_ERROR_LOCATION, stat);
103 }
104 
105 void ParallelIO::sync_impl() const {
106 
107  int stat = PIOc_sync(m_file_id);
108  check(PISM_ERROR_LOCATION, stat);
109 }
110 
112  int stat = PIOc_closefile(m_file_id);
113  check(PISM_ERROR_LOCATION, stat);
114 }
115 
116 // redef/enddef
118  int stat = PIOc_enddef(m_file_id);
119  check(PISM_ERROR_LOCATION, stat);
120 }
121 
123  int stat = PIOc_redef(m_file_id);
124  check(PISM_ERROR_LOCATION, stat);
125 }
126 
127 // dim
128 void ParallelIO::def_dim_impl(const std::string &name, size_t length) const {
129  int dim_id = 0;
130  int stat = PIOc_def_dim(m_file_id, name.c_str(), length, &dim_id);
131  check(PISM_ERROR_LOCATION, stat);
132 }
133 
134 void ParallelIO::inq_dimid_impl(const std::string &dimension_name, bool &exists) const {
135  int tmp;
136 
137  int stat = PIOc_inq_dimid(m_file_id, dimension_name.c_str(), &tmp);
138 
139  if (stat == PIO_NOERR) {
140  exists = true;
141  } else {
142  exists = false;
143  }
144 }
145 
146 void ParallelIO::inq_dimlen_impl(const std::string &dimension_name, unsigned int &result) const {
147  int stat, dimid = -1;
148  PIO_Offset len;
149 
150  stat = PIOc_inq_dimid(m_file_id, dimension_name.c_str(), &dimid);
151  check(PISM_ERROR_LOCATION, stat);
152 
153  stat = PIOc_inq_dimlen(m_file_id, dimid, &len);
154  check(PISM_ERROR_LOCATION, stat);
155 
156  result = static_cast<unsigned int>(len);
157 }
158 
159 void ParallelIO::inq_unlimdim_impl(std::string &result) const {
160  int stat = PIO_NOERR, dimid = -1;
161  char dimname[PIO_MAX_NAME + 1];
162 
163  stat = PIOc_inq_unlimdim(m_file_id, &dimid);
164  check(PISM_ERROR_LOCATION, stat);
165 
166  if (dimid == -1) {
167  result.clear();
168  } else {
169  stat = PIOc_inq_dimname(m_file_id, dimid, dimname);
170  check(PISM_ERROR_LOCATION, stat);
171 
172  result = dimname;
173  }
174 }
175 
176 // var
177 void ParallelIO::def_var_impl(const std::string &name, io::Type nctype,
178  const std::vector<std::string> &dims) const {
179  std::vector<int> dimids;
180  int stat, varid;
181 
182  for (auto d : dims) {
183  int dimid = -1;
184  stat = PIOc_inq_dimid(m_file_id, d.c_str(), &dimid);
185  check(PISM_ERROR_LOCATION, stat);
186  dimids.push_back(dimid);
187  }
188 
189  stat = PIOc_def_var(m_file_id, name.c_str(), pism_type_to_nc_type(nctype),
190  static_cast<int>(dims.size()), dimids.data(), &varid);
191  check(PISM_ERROR_LOCATION, stat);
192 }
193 
194 void ParallelIO::def_var_chunking_impl(const std::string &name,
195  std::vector<size_t> &dimensions) const {
196  (void)name;
197  (void)dimensions;
198  // FIXME
199 }
200 
201 void ParallelIO::get_vara_double_impl(const std::string &variable_name,
202  const std::vector<unsigned int> &start,
203  const std::vector<unsigned int> &count, double *input) const {
204  int stat, varid, ndims = static_cast<int>(start.size());
205 
206  std::vector<PIO_Offset> nc_start(ndims), nc_count(ndims);
207 
208  stat = PIOc_inq_varid(m_file_id, variable_name.c_str(), &varid);
209  check(PISM_ERROR_LOCATION, stat);
210 
211  for (int j = 0; j < ndims; ++j) {
212  nc_start[j] = start[j];
213  nc_count[j] = count[j];
214  }
215 
216  stat = PIOc_get_vara_double(m_file_id, varid, nc_start.data(), nc_count.data(), input);
217  check(PISM_ERROR_LOCATION, stat);
218 }
219 
220 void ParallelIO::put_vara_double_impl(const std::string &variable_name,
221  const std::vector<unsigned int> &start,
222  const std::vector<unsigned int> &count,
223  const double *output) const {
224  int stat, varid, ndims = static_cast<int>(start.size());
225 
226  std::vector<PIO_Offset> nc_start(ndims), nc_count(ndims);
227 
228  stat = PIOc_inq_varid(m_file_id, variable_name.c_str(), &varid);
229  check(PISM_ERROR_LOCATION, stat);
230 
231  for (int j = 0; j < ndims; ++j) {
232  nc_start[j] = start[j];
233  nc_count[j] = count[j];
234  }
235 
236  stat = PIOc_put_vara_double(m_file_id, varid, nc_start.data(), nc_count.data(), output);
237  check(PISM_ERROR_LOCATION, stat);
238 }
239 
240 template <typename T>
241 std::vector<T> convert_data(const double *input, size_t length) {
242  std::vector<T> buffer(length);
243  for (size_t k = 0; k < length; ++k) {
244  buffer[k] = static_cast<T>(input[k]);
245  }
246  return buffer;
247 }
248 
249 void ParallelIO::write_darray_impl(const std::string &variable_name, const Grid &grid,
250  unsigned int z_count, bool time_dependent, unsigned int record,
251  const double *input) {
252  (void)time_dependent;
253 
254  int stat = 0, varid;
255 
256  stat = PIOc_inq_varid(m_file_id, variable_name.c_str(), &varid);
257  check(PISM_ERROR_LOCATION, stat);
258 
259  int type = 0;
260  stat = PIOc_inq_vartype(m_file_id, varid, &type);
261 
262  int decompid = grid.pio_io_decomposition(z_count, type);
263 
264  size_t length = grid.xm() * grid.ym() * z_count;
265 
266  switch (type) {
267  case PIO_DOUBLE:
268  // no conversion necessary
269  stat = PIOc_put_vard_double(m_file_id, varid, decompid, (PIO_Offset)record, input);
270  check(PISM_ERROR_LOCATION, stat);
271  break;
272  case PIO_FLOAT: {
273  auto buffer = convert_data<float>(input, length);
274  stat = PIOc_put_vard_float(m_file_id, varid, decompid, (PIO_Offset)record, buffer.data());
275  check(PISM_ERROR_LOCATION, stat);
276  break;
277  }
278  case PIO_INT: {
279  auto buffer = convert_data<int>(input, length);
280  stat = PIOc_put_vard_int(m_file_id, varid, decompid, (PIO_Offset)record, buffer.data());
281  check(PISM_ERROR_LOCATION, stat);
282  break;
283  }
284  default:
286  "ParallelIO: type conversion is not supported");
287  }
288 }
289 
290 
291 void ParallelIO::get_varm_double_impl(const std::string &variable_name,
292  const std::vector<unsigned int> &start,
293  const std::vector<unsigned int> &count,
294  const std::vector<unsigned int> &imap, double *input) const {
295  (void)variable_name;
296  (void)start;
297  (void)count;
298  (void)imap;
299  (void)input;
301  "ParallelIO does not support transposed access");
302 }
303 
304 void ParallelIO::inq_nvars_impl(int &result) const {
305  int stat = PIOc_inq_nvars(m_file_id, &result);
306  check(PISM_ERROR_LOCATION, stat);
307 }
308 
309 void ParallelIO::inq_vardimid_impl(const std::string &variable_name,
310  std::vector<std::string> &result) const {
311  int stat, ndims, varid = -1;
312  std::vector<int> dimids;
313 
314  stat = PIOc_inq_varid(m_file_id, variable_name.c_str(), &varid);
315  check(PISM_ERROR_LOCATION, stat);
316 
317  stat = PIOc_inq_varndims(m_file_id, varid, &ndims);
318  check(PISM_ERROR_LOCATION, stat);
319 
320  if (ndims == 0) {
321  result.clear();
322  return;
323  }
324 
325  result.resize(ndims);
326  dimids.resize(ndims);
327 
328  stat = PIOc_inq_vardimid(m_file_id, varid, dimids.data());
329  check(PISM_ERROR_LOCATION, stat);
330 
331  for (int k = 0; k < ndims; ++k) {
332  char name[PIO_MAX_NAME];
333  memset(name, 0, PIO_MAX_NAME);
334 
335  stat = PIOc_inq_dimname(m_file_id, dimids[k], name);
336  check(PISM_ERROR_LOCATION, stat);
337 
338  result[k] = name;
339  }
340 }
341 
342 void ParallelIO::inq_varnatts_impl(const std::string &variable_name, int &result) const {
343  int varid = get_varid(variable_name);
344 
345  int stat = PIO_NOERR;
346  if (varid == PIO_GLOBAL) {
347  stat = PIOc_inq_natts(m_file_id, &result);
348  } else {
349  stat = PIOc_inq_varnatts(m_file_id, varid, &result);
350  }
351  check(PISM_ERROR_LOCATION, stat);
352 }
353 
354 void ParallelIO::inq_varid_impl(const std::string &variable_name, bool &exists) const {
355  int stat, flag = -1;
356 
357  stat = PIOc_inq_varid(m_file_id, variable_name.c_str(), &flag);
358 
359  exists = (stat == PIO_NOERR);
360 }
361 
362 void ParallelIO::inq_varname_impl(unsigned int j, std::string &result) const {
363  int stat;
364  char varname[PIO_MAX_NAME];
365  memset(varname, 0, PIO_MAX_NAME);
366 
367  stat = PIOc_inq_varname(m_file_id, j, varname);
368  check(PISM_ERROR_LOCATION, stat);
369 
370  result = varname;
371 }
372 
373 // att
374 void ParallelIO::get_att_double_impl(const std::string &variable_name, const std::string &att_name,
375  std::vector<double> &result) const {
376  // Read the attribute length:
377  int varid = get_varid(variable_name);
378 
379  PIO_Offset attlen = 0;
380  int stat = PIOc_inq_attlen(m_file_id, varid, att_name.c_str(), &attlen);
381 
382  int len = 0;
383  if (stat == NC_NOERR) {
384  len = static_cast<int>(attlen);
385  } else if (stat == NC_ENOTATT) {
386  len = 0;
387  } else {
388  check(PISM_ERROR_LOCATION, stat);
389  }
390 
391  if (len == 0) {
392  result.clear();
393  return;
394  }
395 
396  result.resize(len);
397 
398  stat = PIOc_get_att_double(m_file_id, varid, att_name.c_str(), result.data());
399  check(PISM_ERROR_LOCATION, stat);
400 }
401 
402 void ParallelIO::get_att_text_impl(const std::string &variable_name, const std::string &att_name,
403  std::string &result) const {
404  PIO_Offset attlen;
405 
406  int varid = get_varid(variable_name);
407 
408  int stat = PIOc_inq_attlen(m_file_id, varid, att_name.c_str(), &attlen);
409 
410  int len = 0;
411  if (stat == NC_NOERR) {
412  len = static_cast<int>(attlen);
413  } else {
414  len = 0;
415  }
416 
417  // Allocate some memory or clear result.
418  if (len == 0) {
419  result.clear();
420  return;
421  }
422 
423  // Now read the string and see if we succeeded:
424  std::vector<char> str(len + 1, 0);
425  stat = PIOc_get_att_text(m_file_id, varid, att_name.c_str(), str.data());
426  check(PISM_ERROR_LOCATION, stat);
427 
428  result = str.data();
429 }
430 
431 void ParallelIO::put_att_double_impl(const std::string &variable_name, const std::string &att_name,
432  io::Type xtype, const std::vector<double> &data) const {
433  int stat = PIOc_put_att_double(m_file_id, get_varid(variable_name), att_name.c_str(),
434  pism_type_to_nc_type(xtype), data.size(), data.data());
435  check(PISM_ERROR_LOCATION, stat);
436 }
437 
438 void ParallelIO::put_att_text_impl(const std::string &variable_name, const std::string &att_name,
439  const std::string &value) const {
440  int stat = PIOc_put_att_text(m_file_id, get_varid(variable_name), att_name.c_str(), value.size(),
441  value.c_str());
442  check(PISM_ERROR_LOCATION, stat);
443 }
444 
445 void ParallelIO::inq_attname_impl(const std::string &variable_name, unsigned int n,
446  std::string &result) const {
447  std::vector<char> name(PIO_MAX_NAME + 1, 0);
448 
449  int stat = PIOc_inq_attname(m_file_id, get_varid(variable_name), n, name.data());
450  check(PISM_ERROR_LOCATION, stat);
451 
452  result = name.data();
453 }
454 
455 void ParallelIO::inq_atttype_impl(const std::string &variable_name, const std::string &att_name,
456  io::Type &result) const {
457  nc_type tmp;
458  int stat = PIOc_inq_atttype(m_file_id, get_varid(variable_name), att_name.c_str(), &tmp);
459  if (stat == PIO_ENOTATT) {
460  tmp = NC_NAT;
461  } else {
462  check(PISM_ERROR_LOCATION, stat);
463  }
464 
465  result = nc_type_to_pism_type(tmp);
466 }
467 
468 // misc
469 void ParallelIO::set_fill_impl(int fillmode, int &old_modep) const {
470 
471  int stat = PIOc_set_fill(m_file_id, fillmode, &old_modep);
472  check(PISM_ERROR_LOCATION, stat);
473 }
474 
475 void ParallelIO::del_att_impl(const std::string &variable_name, const std::string &att_name) const {
476  int stat = PIOc_del_att(m_file_id, get_varid(variable_name), att_name.c_str());
477  check(PISM_ERROR_LOCATION, stat);
478 }
479 
480 int ParallelIO::get_varid(const std::string &variable_name) const {
481  if (variable_name == "PISM_GLOBAL") {
482  return NC_GLOBAL;
483  }
484 
485  int varid = -2;
486  int stat = PIOc_inq_varid(m_file_id, variable_name.c_str(), &varid);
487  check(PISM_ERROR_LOCATION, stat);
488  return varid;
489 }
490 
491 } // end of namespace io
492 } // end of namespace pism
int pio_io_decomposition(int dof, int output_datatype) const
Definition: Grid.cc:1361
int xm() const
Width of this processor's sub-domain.
Definition: Grid.cc:741
int ym() const
Width of this processor's sub-domain.
Definition: Grid.cc:746
Describes the PISM grid and the distribution of data across processors.
Definition: Grid.hh:282
static RuntimeError formatted(const ErrorLocation &location, const char format[],...) __attribute__((format(printf
build a RuntimeError with a formatted message
std::string filename() const
Definition: NCFile.cc:39
The PISM wrapper for a subset of the NetCDF C API.
Definition: NCFile.hh:59
void inq_varid_impl(const std::string &variable_name, bool &exists) const
Definition: ParallelIO.cc:354
ParallelIO(MPI_Comm com, int iosysid, io::Backend iotype)
Definition: ParallelIO.cc:60
void inq_dimlen_impl(const std::string &dimension_name, unsigned int &result) const
Definition: ParallelIO.cc:146
void get_vara_double_impl(const std::string &variable_name, const std::vector< unsigned int > &start, const std::vector< unsigned int > &count, double *ip) const
Definition: ParallelIO.cc:201
void del_att_impl(const std::string &variable_name, const std::string &att_name) const
Definition: ParallelIO.cc:475
void inq_varnatts_impl(const std::string &variable_name, int &result) const
Definition: ParallelIO.cc:342
void set_fill_impl(int fillmode, int &old_modep) const
Definition: ParallelIO.cc:469
void enddef_impl() const
Definition: ParallelIO.cc:117
void sync_impl() const
Definition: ParallelIO.cc:105
void put_att_text_impl(const std::string &variable_name, const std::string &att_name, const std::string &value) const
Definition: ParallelIO.cc:438
void redef_impl() const
Definition: ParallelIO.cc:122
void def_var_impl(const std::string &name, io::Type nctype, const std::vector< std::string > &dims) const
Definition: ParallelIO.cc:177
void inq_nvars_impl(int &result) const
Definition: ParallelIO.cc:304
void open_impl(const std::string &filename, io::Mode mode)
Definition: ParallelIO.cc:87
static io::Backend best_iotype(bool netcdf3)
Definition: ParallelIO.cc:45
void put_att_double_impl(const std::string &variable_name, const std::string &att_name, io::Type xtype, const std::vector< double > &data) const
Definition: ParallelIO.cc:431
void create_impl(const std::string &filename)
Definition: ParallelIO.cc:94
void get_att_double_impl(const std::string &variable_name, const std::string &att_name, std::vector< double > &result) const
Definition: ParallelIO.cc:374
void inq_dimid_impl(const std::string &dimension_name, bool &exists) const
Definition: ParallelIO.cc:134
void set_compression_level_impl(int level) const
Definition: ParallelIO.cc:82
void def_dim_impl(const std::string &name, size_t length) const
Definition: ParallelIO.cc:128
void get_att_text_impl(const std::string &variable_name, const std::string &att_name, std::string &result) const
Definition: ParallelIO.cc:402
int get_varid(const std::string &variable_name) const
Definition: ParallelIO.cc:480
void def_var_chunking_impl(const std::string &name, std::vector< size_t > &dimensions) const
Definition: ParallelIO.cc:194
void write_darray_impl(const std::string &variable_name, const Grid &grid, unsigned int z_count, bool time_dependent, unsigned int record, const double *input)
Definition: ParallelIO.cc:249
void put_vara_double_impl(const std::string &variable_name, const std::vector< unsigned int > &start, const std::vector< unsigned int > &count, const double *op) const
Definition: ParallelIO.cc:220
void inq_attname_impl(const std::string &variable_name, unsigned int n, std::string &result) const
Definition: ParallelIO.cc:445
void get_varm_double_impl(const std::string &variable_name, const std::vector< unsigned int > &start, const std::vector< unsigned int > &count, const std::vector< unsigned int > &imap, double *ip) const
Definition: ParallelIO.cc:291
void inq_atttype_impl(const std::string &variable_name, const std::string &att_name, io::Type &result) const
Definition: ParallelIO.cc:455
void inq_vardimid_impl(const std::string &variable_name, std::vector< std::string > &result) const
Definition: ParallelIO.cc:309
void inq_varname_impl(unsigned int j, std::string &result) const
Definition: ParallelIO.cc:362
void inq_unlimdim_impl(std::string &result) const
Definition: ParallelIO.cc:159
#define PISM_ERROR_LOCATION
#define n
Definition: exactTestM.c:37
@ PISM_PIO_NETCDF
Definition: IO_Flags.hh:62
@ PISM_PIO_NETCDF4P
Definition: IO_Flags.hh:64
@ PISM_PIO_PNETCDF
Definition: IO_Flags.hh:61
@ PISM_PIO_NETCDF4C
Definition: IO_Flags.hh:63
@ PISM_READONLY
open an existing file for reading only
Definition: IO_Flags.hh:72
static void check(const ErrorLocation &where, int return_code)
Prints an error message; for debugging.
Definition: NC4_Par.cc:36
std::vector< T > convert_data(const double *input, size_t length)
Definition: ParallelIO.cc:241
static const double k
Definition: exactTestP.cc:42
static pism::io::Type nc_type_to_pism_type(int input)
static nc_type pism_type_to_nc_type(pism::io::Type input)
int count
Definition: test_cube.c:16