PISM, A Parallel Ice Sheet Model  stable v2.1-1-g6902d5502 committed by Ed Bueler on 2023-12-20 08:38:27 -0800
NC4File.cc
Go to the documentation of this file.
1 // Copyright (C) 2012, 2013, 2014, 2015, 2016, 2017, 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 #include "pism/util/io/NC4File.hh"
20 
21 // The following is a stupid kludge necessary to make NetCDF 4.x work in
22 // serial mode in an MPI program:
23 #ifndef MPI_INCLUDED
24 #define MPI_INCLUDED 1
25 #endif
26 #include <netcdf.h>
27 
28 #include "pism/util/io/pism_type_conversion.hh"
29 #include "pism/util/pism_utilities.hh"
30 #include "pism/util/error_handling.hh"
31 
32 namespace pism {
33 namespace io {
34 
35 //! \brief Prints an error message; for debugging.
36 static void check(const ErrorLocation &where, int return_code) {
37  if (return_code != NC_NOERR) {
38  throw RuntimeError(where, nc_strerror(return_code));
39  }
40 }
41 
42 NC4File::NC4File(MPI_Comm c, unsigned int compression_level)
43  : NCFile(c), m_compression_level(compression_level) {
44  // empty
45 }
46 
47 // open/create/close
48 
49 void NC4File::sync_impl() const {
50 
51  int stat = nc_sync(m_file_id); check(PISM_ERROR_LOCATION, stat);
52 }
53 
55  int stat = nc_close(m_file_id); check(PISM_ERROR_LOCATION, stat);
56 
57  m_file_id = -1;
58 }
59 
60 // redef/enddef
61 void NC4File::enddef_impl() const {
62  int stat = nc_enddef(m_file_id); check(PISM_ERROR_LOCATION, stat);
63 }
64 
65 void NC4File::redef_impl() const {
66  int stat = nc_redef(m_file_id); check(PISM_ERROR_LOCATION, stat);
67 }
68 
69 // dim
70 void NC4File::def_dim_impl(const std::string &name, size_t length) const {
71  int dimid = 0;
72 
73  int stat = nc_def_dim(m_file_id, name.c_str(), length, &dimid);
75 }
76 
77 void NC4File::inq_dimid_impl(const std::string &dimension_name, bool &exists) const {
78  int tmp = 0;
79 
80  int stat = nc_inq_dimid(m_file_id, dimension_name.c_str(), &tmp);
81 
82  if (stat == NC_NOERR) {
83  exists = true;
84  } else {
85  exists = false;
86  }
87 }
88 
89 void NC4File::inq_dimlen_impl(const std::string &dimension_name, unsigned int &result) const {
90  int dimid = -1;
91  size_t len;
92 
93  int stat = nc_inq_dimid(m_file_id, dimension_name.c_str(), &dimid);
95 
96  stat = nc_inq_dimlen(m_file_id, dimid, &len); check(PISM_ERROR_LOCATION, stat);
97 
98  result = static_cast<unsigned int>(len);
99 }
100 
101 void NC4File::inq_unlimdim_impl(std::string &result) const {
102  int dimid = -1;
103  std::vector<char> dimname(NC_MAX_NAME + 1, 0);
104 
105  int stat = nc_inq_unlimdim(m_file_id, &dimid); check(PISM_ERROR_LOCATION, stat);
106 
107  if (dimid == -1) {
108  result.clear();
109  } else {
110  stat = nc_inq_dimname(m_file_id, dimid, dimname.data()); check(PISM_ERROR_LOCATION, stat);
111 
112  result = dimname.data();
113  }
114 }
115 
116 // var
117 void NC4File::def_var_impl(const std::string &name,
118  io::Type nctype,
119  const std::vector<std::string> &dims) const {
120  std::vector<int> dimids;
121  int stat = 0, varid = -1;
122 
123  for (auto d : dims) {
124  int dimid = -1;
125  stat = nc_inq_dimid(m_file_id, d.c_str(), &dimid); check(PISM_ERROR_LOCATION, stat);
126  dimids.push_back(dimid);
127  }
128 
129  stat = nc_def_var(m_file_id, name.c_str(), pism_type_to_nc_type(nctype),
130  static_cast<int>(dims.size()), dimids.data(), &varid);
131  check(PISM_ERROR_LOCATION, stat);
132 
133  // Compress 2D and 3D variables
134  if (m_compression_level > 0 and dims.size() > 1) {
135  stat = nc_inq_varid(m_file_id, name.c_str(), &varid); check(PISM_ERROR_LOCATION, stat);
136  stat = nc_def_var_deflate(m_file_id, varid, 0, 1, m_compression_level);
137 
138  // The NetCDF version used by PISM may not support compression.
139  if (stat == NC_EINVAL) {
140  stat = NC_NOERR;
141  }
142 
143  check(PISM_ERROR_LOCATION, stat);
144  }
145 }
146 
147 void NC4File::def_var_chunking_impl(const std::string &name,
148  std::vector<size_t> &dimensions) const {
149  int stat = 0, varid = 0;
150 
151  stat = nc_inq_varid(m_file_id, name.c_str(), &varid);
152  check(PISM_ERROR_LOCATION, stat);
153 
154  stat = nc_def_var_chunking(m_file_id, varid, NC_CHUNKED, dimensions.data());
155  check(PISM_ERROR_LOCATION, stat);
156 }
157 
158 void NC4File::get_varm_double_impl(const std::string &variable_name,
159  const std::vector<unsigned int> &start,
160  const std::vector<unsigned int> &count,
161  const std::vector<unsigned int> &imap, double *op) const {
162  return this->get_put_var_double(variable_name,
163  start, count, imap, op,
164  true /*get*/,
165  true /*transposed*/);
166 }
167 
168 void NC4File::get_vara_double_impl(const std::string &variable_name,
169  const std::vector<unsigned int> &start,
170  const std::vector<unsigned int> &count,
171  double *op) const {
172  std::vector<unsigned int> dummy;
173  return this->get_put_var_double(variable_name,
174  start, count, dummy, op,
175  true /*get*/,
176  false /*not transposed*/);
177 }
178 
179 void NC4File::put_vara_double_impl(const std::string &variable_name,
180  const std::vector<unsigned int> &start,
181  const std::vector<unsigned int> &count,
182  const double *op) const {
183  std::vector<unsigned int> dummy;
184  return this->get_put_var_double(variable_name,
185  start, count, dummy, const_cast<double*>(op),
186  false /*put*/,
187  false /*not transposed*/);
188 }
189 
190 
191 void NC4File::inq_nvars_impl(int &result) const {
192  int stat = nc_inq_nvars(m_file_id, &result); check(PISM_ERROR_LOCATION, stat);
193 }
194 
195 void NC4File::inq_vardimid_impl(const std::string &variable_name, std::vector<std::string> &result) const {
196  int ndims = 0, varid = -1;
197  std::vector<int> dimids;
198 
199  int stat = nc_inq_varid(m_file_id, variable_name.c_str(), &varid); check(PISM_ERROR_LOCATION, stat);
200 
201  stat = nc_inq_varndims(m_file_id, varid, &ndims); check(PISM_ERROR_LOCATION, stat);
202 
203  if (ndims == 0) {
204  result.clear();
205  return;
206  }
207 
208  result.resize(ndims);
209  dimids.resize(ndims);
210 
211  stat = nc_inq_vardimid(m_file_id, varid, dimids.data()); check(PISM_ERROR_LOCATION, stat);
212 
213  for (int k = 0; k < ndims; ++k) {
214  std::vector<char> name(NC_MAX_NAME + 1, 0);
215 
216  stat = nc_inq_dimname(m_file_id, dimids[k], name.data()); check(PISM_ERROR_LOCATION, stat);
217 
218  result[k] = name.data();
219  }
220 }
221 
222 int NC4File::get_varid(const std::string &variable_name) const {
223  if (variable_name == "PISM_GLOBAL") {
224  return NC_GLOBAL;
225  }
226 
227  int result = 0;
228  int stat = nc_inq_varid(m_file_id, variable_name.c_str(), &result);
229  check(PISM_ERROR_LOCATION, stat);
230 
231  return result;
232 }
233 
234 void NC4File::inq_varnatts_impl(const std::string &variable_name, int &result) const {
235  int stat = nc_inq_varnatts(m_file_id, get_varid(variable_name), &result);
236  check(PISM_ERROR_LOCATION, stat);
237 }
238 
239 void NC4File::inq_varid_impl(const std::string &variable_name, bool &exists) const {
240  int varid = -1;
241 
242  int stat = nc_inq_varid(m_file_id, variable_name.c_str(), &varid);
243 
244  exists = (stat == NC_NOERR);
245 }
246 
247 void NC4File::inq_varname_impl(unsigned int j, std::string &result) const {
248  std::vector<char> varname(NC_MAX_NAME + 1, 0);
249 
250  int stat = nc_inq_varname(m_file_id, j, varname.data());
251  check(PISM_ERROR_LOCATION, stat);
252 
253  result = varname.data();
254 }
255 
256 // att
257 
258 void NC4File::get_att_double_impl(const std::string &variable_name,
259  const std::string &att_name,
260  std::vector<double> &result) const {
261  int varid = get_varid(variable_name);
262 
263  // Read the attribute length:
264  size_t attlen = 0;
265  int stat = nc_inq_attlen(m_file_id, varid, att_name.c_str(), &attlen);
266 
267  int len = 0;
268  if (stat == NC_NOERR) {
269  len = static_cast<int>(attlen);
270  } else if (stat == NC_ENOTATT) {
271  len = 0;
272  } else {
273  check(PISM_ERROR_LOCATION, stat);
274  len = 0;
275  }
276 
277  if (len == 0) {
278  result.clear();
279  return;
280  }
281 
282  result.resize(len);
283 
284  // Now read data and see if we succeeded:
285  stat = nc_get_att_double(m_file_id, varid, att_name.c_str(), result.data());
286  check(PISM_ERROR_LOCATION, stat);
287 }
288 
289 // Get a text (character array) attribute on rank 0.
290 static void get_att_text(int ncid, int varid, const std::string &att_name,
291  std::string &result) {
292  size_t attlen = 0;
293  int stat = nc_inq_attlen(ncid, varid, att_name.c_str(), &attlen);
294  if (stat != NC_NOERR) {
295  result = "";
296  return;
297  }
298 
299  std::vector<char> buffer(attlen + 1, 0);
300  stat = nc_get_att_text(ncid, varid, att_name.c_str(), buffer.data());
301 
302  result = (stat == NC_NOERR) ? buffer.data() : "";
303 }
304 
305 // Get a string attribute on rank 0. In "string array" attributes array elements are
306 // concatenated using "," as the separator.
307 static void get_att_string(int ncid, int varid, const std::string &att_name,
308  std::string &result) {
309  size_t attlen = 0;
310  int stat = nc_inq_attlen(ncid, varid, att_name.c_str(), &attlen);
311  if (stat != NC_NOERR) {
312  result = "";
313  return;
314  }
315 
316  std::vector<char*> buffer(attlen + 1, 0);
317  stat = nc_get_att_string(ncid, varid, att_name.c_str(), buffer.data());
318  if (stat == NC_NOERR) {
319  std::vector<std::string> strings(attlen);
320  for (size_t k = 0; k < attlen; ++k) {
321  strings[k] = buffer[k];
322  }
323  result = join(strings, ",");
324  } else {
325  result = "";
326  }
327  stat = nc_free_string(attlen, buffer.data());
328  check(PISM_ERROR_LOCATION, stat);
329 }
330 
331 void NC4File::get_att_text_impl(const std::string &variable_name,
332  const std::string &att_name, std::string &result) const {
333  int varid = get_varid(variable_name);
334 
335  nc_type nctype;
336  int stat = nc_inq_atttype(m_file_id, varid, att_name.c_str(), &nctype);
337 
338  if (stat == NC_NOERR) {
339  if (nctype == NC_CHAR) {
340  pism::io::get_att_text(m_file_id, varid, att_name, result);
341  } else if (nctype == NC_STRING) {
342  pism::io::get_att_string(m_file_id, varid, att_name, result);
343  } else {
344  result = "";
345  }
346  } else if (stat == NC_ENOTATT) {
347  result = "";
348  } else {
349  check(PISM_ERROR_LOCATION, stat);
350  }
351 }
352 
353 void NC4File::del_att_impl(const std::string &variable_name, const std::string &att_name) const {
354  int stat = nc_del_att(m_file_id, get_varid(variable_name), att_name.c_str());
355  check(PISM_ERROR_LOCATION, stat);
356 }
357 
358 void NC4File::put_att_double_impl(const std::string &variable_name,
359  const std::string &att_name,
360  io::Type xtype,
361  const std::vector<double> &data) const {
362  int stat = nc_put_att_double(m_file_id, get_varid(variable_name), att_name.c_str(),
363  xtype, data.size(), data.data());
364  check(PISM_ERROR_LOCATION, stat);
365 }
366 
367 void NC4File::put_att_text_impl(const std::string &variable_name,
368  const std::string &att_name,
369  const std::string &value) const {
370  int stat = nc_put_att_text(m_file_id, get_varid(variable_name), att_name.c_str(),
371  value.size(), value.c_str());
372  check(PISM_ERROR_LOCATION, stat);
373 }
374 
375 void NC4File::inq_attname_impl(const std::string &variable_name,
376  unsigned int n,
377  std::string &result) const {
378  std::vector<char> name(NC_MAX_NAME + 1, 0);
379  int stat = nc_inq_attname(m_file_id, get_varid(variable_name), n, name.data());
380  check(PISM_ERROR_LOCATION, stat);
381 
382  result = name.data();
383 }
384 
385 void NC4File::inq_atttype_impl(const std::string &variable_name,
386  const std::string &att_name,
387  io::Type &result) const {
388  nc_type tmp = NC_NAT;
389  int stat = nc_inq_atttype(m_file_id, get_varid(variable_name), att_name.c_str(), &tmp);
390 
391  if (stat == NC_ENOTATT) {
392  tmp = NC_NAT;
393  } else {
394  check(PISM_ERROR_LOCATION, stat);
395  }
396 
397  result = nc_type_to_pism_type(tmp);
398 }
399 
400 // misc
401 
402 void NC4File::set_fill_impl(int fillmode, int &old_modep) const {
403  int stat = nc_set_fill(m_file_id, fillmode, &old_modep); check(PISM_ERROR_LOCATION, stat);
404 }
405 
406 void NC4File::set_access_mode(int /*unused*/, bool /*unused*/) const {
407  // empty
408 }
409 
410 void NC4File::get_put_var_double(const std::string &variable_name,
411  const std::vector<unsigned int> &start,
412  const std::vector<unsigned int> &count,
413  const std::vector<unsigned int> &imap_input,
414  double *op,
415  bool get,
416  bool transposed) const {
417  int stat, varid, ndims = static_cast<int>(start.size());
418  std::vector<unsigned int> imap = imap_input;
419 
420  if (not transposed) {
421  imap.resize(ndims);
422  }
423 
424  std::vector<size_t> nc_start(ndims), nc_count(ndims);
425  std::vector<ptrdiff_t> nc_imap(ndims), nc_stride(ndims);
426 
427  stat = nc_inq_varid(m_file_id, variable_name.c_str(), &varid); check(PISM_ERROR_LOCATION, stat);
428 
429  for (int j = 0; j < ndims; ++j) {
430  nc_start[j] = start[j];
431  nc_count[j] = count[j];
432  nc_imap[j] = imap[j];
433  nc_stride[j] = 1;
434  }
435 
436  if (transposed) {
437 
438  set_access_mode(varid, transposed);
439 
440  if (get) {
441  stat = nc_get_varm_double(m_file_id, varid,
442  nc_start.data(), nc_count.data(), nc_stride.data(), nc_imap.data(),
443  op); check(PISM_ERROR_LOCATION, stat);
444  } else {
445  stat = nc_put_varm_double(m_file_id, varid,
446  nc_start.data(), nc_count.data(), nc_stride.data(), nc_imap.data(),
447  op); check(PISM_ERROR_LOCATION, stat);
448  }
449  } else {
450 
451  set_access_mode(varid, transposed);
452 
453  if (get) {
454  stat = nc_get_vara_double(m_file_id, varid,
455  nc_start.data(), nc_count.data(),
456  op); check(PISM_ERROR_LOCATION, stat);
457  } else {
458  stat = nc_put_vara_double(m_file_id, varid,
459  nc_start.data(), nc_count.data(),
460  op); check(PISM_ERROR_LOCATION, stat);
461  }
462  }
463 }
464 
465 } // end of namespace io
466 } // end of namespace pism
virtual void get_att_double_impl(const std::string &variable_name, const std::string &att_name, std::vector< double > &result) const
Definition: NC4File.cc:258
virtual void inq_attname_impl(const std::string &variable_name, unsigned int n, std::string &result) const
Definition: NC4File.cc:375
virtual void inq_nvars_impl(int &result) const
Definition: NC4File.cc:191
virtual 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: NC4File.cc:358
virtual void get_att_text_impl(const std::string &variable_name, const std::string &att_name, std::string &result) const
Definition: NC4File.cc:331
virtual void close_impl()
Definition: NC4File.cc:54
virtual void inq_varid_impl(const std::string &variable_name, bool &exists) const
Definition: NC4File.cc:239
virtual void sync_impl() const
Definition: NC4File.cc:49
virtual void inq_dimid_impl(const std::string &dimension_name, bool &exists) const
Definition: NC4File.cc:77
virtual void enddef_impl() const
Definition: NC4File.cc:61
virtual void inq_unlimdim_impl(std::string &result) const
Definition: NC4File.cc:101
unsigned int m_compression_level
Definition: NC4File.hh:113
virtual void def_var_chunking_impl(const std::string &name, std::vector< size_t > &dimensions) const
Definition: NC4File.cc:147
virtual void set_access_mode(int varid, bool mapped) const
Definition: NC4File.cc:406
virtual void redef_impl() const
Definition: NC4File.cc:65
virtual void inq_dimlen_impl(const std::string &dimension_name, unsigned int &result) const
Definition: NC4File.cc:89
virtual void del_att_impl(const std::string &variable_name, const std::string &att_name) const
Definition: NC4File.cc:353
virtual void get_put_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 get, bool mapped) const
Definition: NC4File.cc:410
virtual void put_att_text_impl(const std::string &variable_name, const std::string &att_name, const std::string &value) const
Definition: NC4File.cc:367
NC4File(MPI_Comm com, unsigned int compression_level)
Definition: NC4File.cc:42
virtual void def_dim_impl(const std::string &name, size_t length) const
Definition: NC4File.cc:70
virtual void inq_vardimid_impl(const std::string &variable_name, std::vector< std::string > &result) const
Definition: NC4File.cc:195
virtual void set_fill_impl(int fillmode, int &old_modep) const
Definition: NC4File.cc:402
virtual void def_var_impl(const std::string &name, io::Type nctype, const std::vector< std::string > &dims) const
Definition: NC4File.cc:117
virtual 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: NC4File.cc:158
virtual 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: NC4File.cc:179
int get_varid(const std::string &variable_name) const
Definition: NC4File.cc:222
virtual void inq_varname_impl(unsigned int j, std::string &result) const
Definition: NC4File.cc:247
virtual 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: NC4File.cc:168
virtual void inq_atttype_impl(const std::string &variable_name, const std::string &att_name, io::Type &result) const
Definition: NC4File.cc:385
virtual void inq_varnatts_impl(const std::string &variable_name, int &result) const
Definition: NC4File.cc:234
The PISM wrapper for a subset of the NetCDF C API.
Definition: NCFile.hh:59
#define PISM_ERROR_LOCATION
#define n
Definition: exactTestM.c:37
static void check(const ErrorLocation &where, int return_code)
Prints an error message; for debugging.
Definition: NC4_Par.cc:36
static void get_att_string(int ncid, int varid, const std::string &att_name, std::string &result)
Definition: NC4File.cc:307
static void get_att_text(int ncid, int varid, const std::string &att_name, std::string &result)
Definition: NC4File.cc:290
static const double k
Definition: exactTestP.cc:42
std::string join(const std::vector< std::string > &strings, const std::string &separator)
Concatenate strings, inserting separator between elements.
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