PISM, A Parallel Ice Sheet Model  stable v2.1-1-g6902d5502 committed by Ed Bueler on 2023-12-20 08:38:27 -0800
PNCFile.cc
Go to the documentation of this file.
1 // Copyright (C) 2012, 2013, 2014, 2015, 2016, 2017, 2019, 2020, 2021, 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 #include <pnetcdf.h>
20 #include <sstream>
21 #include <cstring> // memset
22 
23 #include "pism/util/io/PNCFile.hh"
24 
25 #include "pism/util/io/pism_type_conversion.hh" // has to go after pnetcdf.h
26 
27 #include "pism/util/error_handling.hh"
28 #include "pism/util/pism_utilities.hh"
29 
30 namespace pism {
31 namespace io {
32 
33 PNCFile::PNCFile(MPI_Comm c)
34  : NCFile(c) {
35  MPI_Info_create(&m_mpi_info);
36 }
37 
38 
40  MPI_Info_free(&m_mpi_info);
41 }
42 
43 static void check(const ErrorLocation &where, int return_code) {
44  if (return_code != NC_NOERR) {
45  throw RuntimeError(where, ncmpi_strerror(return_code));
46  }
47 }
48 
49 void PNCFile::open_impl(const std::string &fname, io::Mode mode) {
50  int stat;
51 
52  init_hints();
53 
54  int open_mode = mode == io::PISM_READONLY ? NC_NOWRITE : NC_WRITE;
55 
56  stat = ncmpi_open(m_com, fname.c_str(), open_mode, m_mpi_info, &m_file_id);
58 }
59 
60 
61 void PNCFile::create_impl(const std::string &fname) {
62  int stat;
63 
64  init_hints();
65 
66  stat = ncmpi_create(m_com, fname.c_str(), NC_CLOBBER | NC_64BIT_DATA, m_mpi_info, &m_file_id);
68 }
69 
70 void PNCFile::sync_impl() const {
71 
72  int stat = ncmpi_sync(m_file_id);
74 }
75 
76 
78  int stat = ncmpi_close(m_file_id);
80 
81  m_file_id = -1;
82 }
83 
84 
85 void PNCFile::enddef_impl() const {
86 
87  int stat = ncmpi_enddef(m_file_id);
89 }
90 
91 
92 void PNCFile::redef_impl() const {
93 
94  int stat = ncmpi_redef(m_file_id);
96 }
97 
98 
99 void PNCFile::def_dim_impl(const std::string &name, size_t length) const {
100  int dimid = 0, stat;
101 
102  stat = ncmpi_def_dim(m_file_id, name.c_str(), length, &dimid);
103  check(PISM_ERROR_LOCATION, stat);
104 }
105 
106 
107 void PNCFile::inq_dimid_impl(const std::string &dimension_name, bool &exists) const {
108  int tmp, stat;
109 
110  stat = ncmpi_inq_dimid(m_file_id, dimension_name.c_str(), &tmp);
111 
112  if (stat == NC_NOERR) {
113  exists = true;
114  } else {
115  exists = false;
116  }
117 }
118 
119 
120 void PNCFile::inq_dimlen_impl(const std::string &dimension_name, unsigned int &result) const {
121  int stat, dimid = -1;
122  MPI_Offset len;
123 
124  stat = ncmpi_inq_dimid(m_file_id, dimension_name.c_str(), &dimid);
125  check(PISM_ERROR_LOCATION, stat);
126 
127  stat = ncmpi_inq_dimlen(m_file_id, dimid, &len);
128  check(PISM_ERROR_LOCATION, stat);
129 
130  result = static_cast<unsigned int>(len);
131 }
132 
133 
134 void PNCFile::inq_unlimdim_impl(std::string &result) const {
135  int stat = NC_NOERR, dimid = -1;
136  char dimname[NC_MAX_NAME];
137 
138  stat = ncmpi_inq_unlimdim(m_file_id, &dimid);
139  check(PISM_ERROR_LOCATION, stat);
140 
141  if (dimid == -1) {
142  result.clear();
143  } else {
144  stat = ncmpi_inq_dimname(m_file_id, dimid, dimname);
145  check(PISM_ERROR_LOCATION, stat);
146 
147  result = dimname;
148  }
149 }
150 
151 void PNCFile::def_var_impl(const std::string &name, io::Type nctype,
152  const std::vector<std::string> &dims) const {
153  std::vector<int> dimids;
154  int stat, varid;
155 
156  for (auto d : dims) {
157  int dimid = -1;
158  stat = ncmpi_inq_dimid(m_file_id, d.c_str(), &dimid);
159  check(PISM_ERROR_LOCATION, stat);
160  dimids.push_back(dimid);
161  }
162 
163  stat = ncmpi_def_var(m_file_id, name.c_str(), pism_type_to_nc_type(nctype),
164  static_cast<int>(dims.size()), dimids.data(), &varid);
165  check(PISM_ERROR_LOCATION, stat);
166 }
167 
168 
169 void PNCFile::get_vara_double_impl(const std::string &variable_name,
170  const std::vector<unsigned int> &start,
171  const std::vector<unsigned int> &count, double *ip) const {
172  std::vector<unsigned int> dummy;
173  return this->get_var_double(variable_name, start, count, dummy, ip, false);
174 }
175 
176 
177 void PNCFile::put_vara_double_impl(const std::string &variable_name,
178  const std::vector<unsigned int> &start,
179  const std::vector<unsigned int> &count, const double *op) const {
180  int stat, varid, ndims = static_cast<int>(start.size());
181 
182  std::vector<MPI_Offset> nc_start(ndims), nc_count(ndims), nc_stride(ndims);
183 
184  stat = ncmpi_inq_varid(m_file_id, variable_name.c_str(), &varid);
185  check(PISM_ERROR_LOCATION, stat);
186 
187  for (int j = 0; j < ndims; ++j) {
188  nc_start[j] = start[j];
189  nc_count[j] = count[j];
190  nc_stride[j] = 1;
191  }
192 
193  stat = ncmpi_put_vara_double_all(m_file_id, varid, nc_start.data(), nc_count.data(), op);
194  check(PISM_ERROR_LOCATION, stat);
195 }
196 
197 
198 void PNCFile::get_varm_double_impl(const std::string &variable_name,
199  const std::vector<unsigned int> &start,
200  const std::vector<unsigned int> &count,
201  const std::vector<unsigned int> &imap, double *ip) const {
202  return this->get_var_double(variable_name, start, count, imap, ip, true);
203 }
204 
205 
206 void PNCFile::inq_nvars_impl(int &result) const {
207  int stat;
208 
209  stat = ncmpi_inq_nvars(m_file_id, &result);
210  check(PISM_ERROR_LOCATION, stat);
211 }
212 
213 
214 void PNCFile::inq_vardimid_impl(const std::string &variable_name,
215  std::vector<std::string> &result) const {
216  int stat, ndims, varid = -1;
217  std::vector<int> dimids;
218 
219  stat = ncmpi_inq_varid(m_file_id, variable_name.c_str(), &varid);
220  check(PISM_ERROR_LOCATION, stat);
221 
222  stat = ncmpi_inq_varndims(m_file_id, varid, &ndims);
223  check(PISM_ERROR_LOCATION, stat);
224 
225  if (ndims == 0) {
226  result.clear();
227  return;
228  }
229 
230  result.resize(ndims);
231  dimids.resize(ndims);
232 
233  stat = ncmpi_inq_vardimid(m_file_id, varid, dimids.data());
234  check(PISM_ERROR_LOCATION, stat);
235 
236  for (int k = 0; k < ndims; ++k) {
237  char name[NC_MAX_NAME];
238  memset(name, 0, NC_MAX_NAME);
239 
240  stat = ncmpi_inq_dimname(m_file_id, dimids[k], name);
241  check(PISM_ERROR_LOCATION, stat);
242 
243  result[k] = name;
244  }
245 }
246 
247 int PNCFile::get_varid(const std::string &variable_name) const {
248  if (variable_name == "PISM_GLOBAL") {
249  return NC_GLOBAL;
250  }
251 
252  int varid = -1;
253  int stat = ncmpi_inq_varid(m_file_id, variable_name.c_str(), &varid);
254  check(PISM_ERROR_LOCATION, stat);
255 
256  return varid;
257 }
258 
259 void PNCFile::inq_varnatts_impl(const std::string &variable_name, int &result) const {
260 
261  int stat = ncmpi_inq_varnatts(m_file_id, get_varid(variable_name), &result);
262  check(PISM_ERROR_LOCATION, stat);
263 }
264 
265 
266 void PNCFile::inq_varid_impl(const std::string &variable_name, bool &exists) const {
267  int stat, flag = -1;
268 
269  stat = ncmpi_inq_varid(m_file_id, variable_name.c_str(), &flag);
270 
271  exists = (stat == NC_NOERR);
272 }
273 
274 
275 void PNCFile::inq_varname_impl(unsigned int j, std::string &result) const {
276  int stat;
277  char varname[NC_MAX_NAME];
278  memset(varname, 0, NC_MAX_NAME);
279 
280  stat = ncmpi_inq_varname(m_file_id, j, varname);
281  check(PISM_ERROR_LOCATION, stat);
282 
283  result = varname;
284 }
285 
286 void PNCFile::get_att_double_impl(const std::string &variable_name, const std::string &att_name,
287  std::vector<double> &result) const {
288  int len, varid = get_varid(variable_name);
289  MPI_Offset attlen = 0;
290 
291  // Read the attribute length:
292  int stat = ncmpi_inq_attlen(m_file_id, varid, att_name.c_str(), &attlen);
293 
294  if (stat == NC_NOERR) {
295  len = static_cast<int>(attlen);
296  } else if (stat == NC_ENOTATT) {
297  len = 0;
298  } else {
299  check(PISM_ERROR_LOCATION, stat);
300  len = 0;
301  }
302 
303  if (len == 0) {
304  result.clear();
305  return;
306  }
307 
308  result.resize(len);
309 
310  // Now read data and broadcast stat to see if we succeeded:
311  stat = ncmpi_get_att_double(m_file_id, varid, att_name.c_str(), result.data());
312  check(PISM_ERROR_LOCATION, stat);
313 
314  // On error, print a message and stop.
315  if (stat != NC_NOERR) {
316  fprintf(stderr, "Error reading the %s attribute; (varid %d, NetCDF error %s)", att_name.c_str(),
317  varid, ncmpi_strerror(stat));
318  }
319 }
320 
321 
322 void PNCFile::get_att_text_impl(const std::string &variable_name, const std::string &att_name,
323  std::string &result) const {
324  int varid = get_varid(variable_name);
325 
326  // Read the attribute length:
327  MPI_Offset attlen = 0;
328 
329  int stat = ncmpi_inq_attlen(m_file_id, varid, att_name.c_str(), &attlen);
330  int len = (stat == NC_NOERR) ? static_cast<int>(attlen) : 0;
331 
332  // Allocate some memory or set result to NULL and return:
333  if (len == 0) {
334  result.clear();
335  return;
336  }
337 
338  std::vector<char> str(len + 1, 0);
339 
340  // Now read the string and see if we succeeded:
341  stat = ncmpi_get_att_text(m_file_id, varid, att_name.c_str(), str.data());
342  check(PISM_ERROR_LOCATION, stat);
343 
344  result = str.data();
345 }
346 
347 void PNCFile::del_att_impl(const std::string &variable_name, const std::string &att_name) const {
348  int stat = ncmpi_del_att(m_file_id, get_varid(variable_name), att_name.c_str());
349  check(PISM_ERROR_LOCATION, stat);
350 }
351 
352 void PNCFile::put_att_double_impl(const std::string &variable_name, const std::string &att_name,
353  io::Type nctype, const std::vector<double> &data) const {
354 
355  int stat = ncmpi_put_att_double(m_file_id, get_varid(variable_name), att_name.c_str(),
356  pism_type_to_nc_type(nctype), data.size(), data.data());
357  check(PISM_ERROR_LOCATION, stat);
358 }
359 
360 
361 void PNCFile::put_att_text_impl(const std::string &variable_name, const std::string &att_name,
362  const std::string &value) const {
363  int stat = ncmpi_put_att_text(m_file_id, get_varid(variable_name), att_name.c_str(), value.size(),
364  value.c_str());
365  check(PISM_ERROR_LOCATION, stat);
366 }
367 
368 
369 void PNCFile::inq_attname_impl(const std::string &variable_name, unsigned int n,
370  std::string &result) const {
371  int stat;
372  char name[NC_MAX_NAME];
373  memset(name, 0, NC_MAX_NAME);
374 
375  int varid = get_varid(variable_name);
376 
377  stat = ncmpi_inq_attname(m_file_id, varid, n, name);
378  check(PISM_ERROR_LOCATION, stat);
379 
380  result = name;
381 }
382 
383 
384 void PNCFile::inq_atttype_impl(const std::string &variable_name, const std::string &att_name,
385  io::Type &result) const {
386  int varid = get_varid(variable_name);
387 
388  nc_type tmp = NC_NAT;
389  int stat = ncmpi_inq_atttype(m_file_id, varid, att_name.c_str(), &tmp);
390  if (stat == NC_ENOTATT) {
391  tmp = NC_NAT;
392  } else {
393  check(PISM_ERROR_LOCATION, stat);
394  }
395 
396  result = nc_type_to_pism_type(tmp);
397 }
398 
399 
400 void PNCFile::set_fill_impl(int fillmode, int &old_modep) const {
401  int stat = ncmpi_set_fill(m_file_id, fillmode, &old_modep);
402  check(PISM_ERROR_LOCATION, stat);
403 }
404 
405 
406 void PNCFile::get_var_double(const std::string &variable_name,
407  const std::vector<unsigned int> &start,
408  const std::vector<unsigned int> &count,
409  const std::vector<unsigned int> &imap_input, double *ip,
410  bool transposed) const {
411  std::vector<unsigned int> imap = imap_input;
412  int stat, varid, ndims = static_cast<int>(start.size());
413 
414  if (not transposed) {
415  imap.resize(ndims);
416  }
417 
418  std::vector<MPI_Offset> nc_start(ndims), nc_count(ndims), nc_imap(ndims), nc_stride(ndims);
419 
420  stat = ncmpi_inq_varid(m_file_id, variable_name.c_str(), &varid);
421  check(PISM_ERROR_LOCATION, stat);
422 
423  for (int j = 0; j < ndims; ++j) {
424  nc_start[j] = start[j];
425  nc_count[j] = count[j];
426  nc_imap[j] = imap[j];
427  nc_stride[j] = 1;
428  }
429 
430  if (transposed) {
431  stat = ncmpi_get_varm_double_all(m_file_id, varid, nc_start.data(), nc_count.data(),
432  nc_stride.data(), nc_imap.data(), ip);
433  check(PISM_ERROR_LOCATION, stat);
434  } else {
435  stat = ncmpi_get_vara_double_all(m_file_id, varid,
436  nc_start.data(), nc_count.data(),
437  ip);
438  check(PISM_ERROR_LOCATION, stat);
439  }
440 }
441 
443 
444  for (auto hint : m_mpi_io_hints) {
445  auto words = split(hint, ':');
446 
447  if (words.size() == 2) {
448  // printf("Setting MPI I/O hint \"%s\" to \"%s\"...\n",
449  // words[0].c_str(), words[1].c_str());
450 
451  MPI_Info_set(m_mpi_info,
452  const_cast<char*>(words[0].c_str()),
453  const_cast<char*>(words[1].c_str()));
454  } else {
455  int rank = 0;
456  MPI_Comm_rank(m_com, &rank);
457  if (rank == 0) {
458  printf("PISM WARNING: invalid MPI I/O hint: %s. Ignoring it...\n",
459  hint.c_str());
460  }
461  }
462  }
463 }
464 
465 void PNCFile::set_compression_level_impl(int level) const {
466  (void) level;
467  // NetCDF-3 does not support compression.
468 }
469 
470 } // end of namespace io
471 } // end of namespace pism
MPI_Comm m_com
Definition: NCFile.hh:232
The PISM wrapper for a subset of the NetCDF C API.
Definition: NCFile.hh:59
void def_dim_impl(const std::string &name, size_t length) const
Definition: PNCFile.cc:99
PNCFile(MPI_Comm com)
Definition: PNCFile.cc:33
void inq_unlimdim_impl(std::string &result) const
Definition: PNCFile.cc:134
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: PNCFile.cc:169
void inq_varid_impl(const std::string &variable_name, bool &exists) const
Definition: PNCFile.cc:266
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: PNCFile.cc:198
void close_impl()
Definition: PNCFile.cc:77
void inq_varname_impl(unsigned int j, std::string &result) const
Definition: PNCFile.cc:275
void inq_dimid_impl(const std::string &dimension_name, bool &exists) const
Definition: PNCFile.cc:107
void inq_nvars_impl(int &result) const
Definition: PNCFile.cc:206
void del_att_impl(const std::string &variable_name, const std::string &att_name) const
Definition: PNCFile.cc:347
void enddef_impl() const
Definition: PNCFile.cc:85
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: PNCFile.cc:177
void inq_varnatts_impl(const std::string &variable_name, int &result) const
Definition: PNCFile.cc:259
void inq_dimlen_impl(const std::string &dimension_name, unsigned int &result) const
Definition: PNCFile.cc:120
std::vector< std::string > m_mpi_io_hints
Definition: PNCFile.hh:118
void sync_impl() const
Definition: PNCFile.cc:70
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: PNCFile.cc:352
void inq_attname_impl(const std::string &variable_name, unsigned int n, std::string &result) const
Definition: PNCFile.cc:369
void inq_atttype_impl(const std::string &variable_name, const std::string &att_name, io::Type &result) const
Definition: PNCFile.cc:384
virtual ~PNCFile()
Definition: PNCFile.cc:39
void inq_vardimid_impl(const std::string &variable_name, std::vector< std::string > &result) const
Definition: PNCFile.cc:214
void get_att_text_impl(const std::string &variable_name, const std::string &att_name, std::string &result) const
Definition: PNCFile.cc:322
void init_hints()
Definition: PNCFile.cc:442
MPI_Info m_mpi_info
Definition: PNCFile.hh:130
void redef_impl() const
Definition: PNCFile.cc:92
void get_var_double(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, bool transposed) const
Definition: PNCFile.cc:406
void set_fill_impl(int fillmode, int &old_modep) const
Definition: PNCFile.cc:400
void set_compression_level_impl(int level) const
Definition: PNCFile.cc:465
void create_impl(const std::string &filename)
Definition: PNCFile.cc:61
void get_att_double_impl(const std::string &variable_name, const std::string &att_name, std::vector< double > &result) const
Definition: PNCFile.cc:286
int get_varid(const std::string &variable_name) const
Definition: PNCFile.cc:247
void open_impl(const std::string &filename, io::Mode mode)
Definition: PNCFile.cc:49
void put_att_text_impl(const std::string &variable_name, const std::string &att_name, const std::string &value) const
Definition: PNCFile.cc:361
void def_var_impl(const std::string &name, io::Type nctype, const std::vector< std::string > &dims) const
Definition: PNCFile.cc:151
#define PISM_ERROR_LOCATION
#define n
Definition: exactTestM.c:37
@ 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::string printf(const char *format,...)
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)
std::vector< std::string > split(const std::string &input, char separator)
Transform a separator-separated list (a string) into a vector of strings.
int count
Definition: test_cube.c:16