PISM, A Parallel Ice Sheet Model  stable v2.0.5 committed by Constantine Khrulev on 2022-10-14 09:56:26 -0800
File.cc
Go to the documentation of this file.
1 // Copyright (C) 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021 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 <cassert>
20 #include <cstdio>
21 #include <memory>
22 using std::shared_ptr;
23 
24 #include <petscvec.h>
25 
26 #include "File.hh"
27 #include "pism/util/IceGrid.hh"
28 #include "pism/util/pism_utilities.hh"
29 #include "pism/util/VariableMetadata.hh"
30 #include "pism/util/ConfigInterface.hh"
31 #include "pism/util/Time.hh"
32 #include "NC_Serial.hh"
33 #include "NC4_Serial.hh"
34 
35 #include "pism/pism_config.hh"
36 
37 #if (Pism_USE_PARALLEL_NETCDF4==1)
38 #include "NC4_Par.hh"
39 #endif
40 
41 #if (Pism_USE_PNETCDF==1)
42 #include "PNCFile.hh"
43 #endif
44 
45 #if (Pism_USE_PIO==1)
46 #include "ParallelIO.hh"
47 #endif
48 
49 #include "pism/util/error_handling.hh"
50 #include "pism/util/io/io_helpers.hh"
51 
52 namespace pism {
53 
54 struct File::Impl {
55  MPI_Comm com;
58 };
59 
60 IO_Backend string_to_backend(const std::string &backend) {
61  std::map<std::string, IO_Backend> backends =
62  {
63  {"netcdf3", PISM_NETCDF3},
64  {"netcdf4_parallel", PISM_NETCDF4_PARALLEL},
65  {"netcdf4_serial", PISM_NETCDF4_SERIAL},
66  {"pio_netcdf", PISM_PIO_NETCDF},
67  {"pio_netcdf4c", PISM_PIO_NETCDF4C},
68  {"pio_netcdf4p", PISM_PIO_NETCDF4P},
69  {"pio_pnetcdf", PISM_PIO_PNETCDF},
70  {"pnetcdf", PISM_PNETCDF},
71  };
72 
73  if (backends.find(backend) != backends.end()) {
74  return backends[backend];
75  }
76 
78  "unknown or unsupported I/O backend: %s",
79  backend.c_str());
80 }
81 
82 static std::string backend_to_string(IO_Backend backend) {
83  std::map<IO_Backend, std::string> backends =
84  {
85  {PISM_GUESS, "unknown"},
86  {PISM_NETCDF3, "netcdf3"},
87  {PISM_NETCDF4_PARALLEL, "netcdf4_parallel"},
88  {PISM_NETCDF4_SERIAL, "netcdf4_serial"},
89  {PISM_PIO_NETCDF, "pio_netcdf"},
90  {PISM_PIO_NETCDF4C, "pio_netcdf4c"},
91  {PISM_PIO_NETCDF4P, "pio_netcdf4p"},
92  {PISM_PIO_PNETCDF, "pio_pnetcdf"},
93  {PISM_PNETCDF, "pnetcdf"}
94  };
95 
96  return backends[backend];
97 }
98 
99 // Chooses the best available I/O backend for reading from 'filename'.
100 static IO_Backend choose_backend(MPI_Comm com, const std::string &filename) {
101 
102  std::string format;
103  {
104  // This is the rank-0-only purely-serial mode of accessing NetCDF files, but it
105  // supports all the kinds of NetCDF, so this is fine.
106  io::NC_Serial file(com);
107 
108  file.open(filename, PISM_READONLY);
109  format = file.get_format();
110  file.close();
111  }
112 
113 #if (Pism_USE_PARALLEL_NETCDF4==1)
114  if (format == "netcdf4") {
115  return PISM_NETCDF4_PARALLEL;
116  }
117 #endif
118 
119 #if (Pism_USE_PNETCDF==1)
120  if (format != "netcdf4") {
121  return PISM_PNETCDF;
122  }
123 #endif
124 
125  // this choice is appropriate for both NetCDF-3 and NetCDF-4
126  return PISM_NETCDF3;
127 }
128 
129 static io::NCFile::Ptr create_backend(MPI_Comm com, IO_Backend backend, int iosysid) {
130  // disable a compiler warning
131  (void) iosysid;
132 
133  int size = 1;
134  MPI_Comm_size(com, &size);
135 
136  switch (backend) {
137  case PISM_NETCDF3:
138  return io::NCFile::Ptr(new io::NC_Serial(com));
139  case PISM_NETCDF4_SERIAL:
140  return io::NCFile::Ptr(new io::NC4_Serial(com));
142 #if (Pism_USE_PARALLEL_NETCDF4==1)
143  return io::NCFile::Ptr(new io::NC4_Par(com));
144 #else
145  break;
146 #endif
147 
148  case PISM_PNETCDF:
149 #if (Pism_USE_PNETCDF==1)
150  return io::NCFile::Ptr(new io::PNCFile(com));
151 #else
152  break;
153 #endif
154 
155  case PISM_PIO_PNETCDF:
156  case PISM_PIO_NETCDF4P:
157  case PISM_PIO_NETCDF4C:
158  case PISM_PIO_NETCDF:
159 #if (Pism_USE_PIO==1)
160  {
161  if (iosysid == -1) {
163  "To use ParallelIO you have to pass iosysid to File");
164  }
165  return io::NCFile::Ptr(new io::ParallelIO(com, iosysid, backend));
166  }
167 #else
168  break;
169 #endif
170 
171  case PISM_GUESS:
172  break;
173  } // end of switch (backend)
174 
175 
176  auto backend_name = backend_to_string(backend);
177 
179  "unknown or unsupported I/O backend: %s",
180  backend_name.c_str());
181 }
182 
183 File::File(MPI_Comm com, const std::string &filename, IO_Backend backend, IO_Mode mode,
184  int iosysid)
185  : m_impl(new Impl) {
186 
187  if (filename.empty()) {
189  "cannot open file: provided file name is empty");
190  }
191 
192  if (backend == PISM_GUESS) {
194  } else {
196  }
197 
198  m_impl->com = com;
199  m_impl->nc = create_backend(m_impl->com, m_impl->backend, iosysid);
200 
201  this->open(filename, mode);
202 }
203 
205  if (m_impl->nc and not filename().empty()) {
206  try {
207  // a file is still open, so we try to close it
208  this->close();
209  } catch (...) {
210  // don't ever throw from here
211  handle_fatal_errors(MPI_COMM_SELF);
212  }
213  }
214  delete m_impl;
215 }
216 
217 MPI_Comm File::com() const {
218  return m_impl->com;
219 }
220 
222  return m_impl->backend;
223 }
224 
225 void File::set_compression_level(int level) const {
226  m_impl->nc->set_compression_level(level);
227 }
228 
229 void File::open(const std::string &filename, IO_Mode mode) {
230  try {
231 
232  // opening for reading
233  if (mode == PISM_READONLY) {
234 
235  m_impl->nc->open(filename, mode);
236 
237  } else if (mode == PISM_READWRITE_CLOBBER or mode == PISM_READWRITE_MOVE) {
238 
239  if (mode == PISM_READWRITE_MOVE) {
241  } else {
243  }
244 
245  m_impl->nc->create(filename);
246 
247  int old_fill;
248  m_impl->nc->set_fill(PISM_NOFILL, old_fill);
249  } else if (mode == PISM_READWRITE) { // mode == PISM_READWRITE
250 
251  m_impl->nc->open(filename, mode);
252 
253  int old_fill;
254  m_impl->nc->set_fill(PISM_NOFILL, old_fill);
255  } else {
256  throw RuntimeError::formatted(PISM_ERROR_LOCATION, "invalid mode: %d", mode);
257  }
258  } catch (RuntimeError &e) {
259  e.add_context("opening or creating \"" + filename + "\"");
260  throw;
261  }
262 }
263 
264 void File::remove_attribute(const std::string &variable_name, const std::string &att_name) const {
265  try {
266  m_impl->nc->del_att(variable_name, att_name);
267  } catch (RuntimeError &e) {
268  e.add_context("deleting the attribute %s:%s", variable_name.c_str(), att_name.c_str());
269  throw;
270  }
271 }
272 
273 void File::close() {
274  try {
275  m_impl->nc->close();
276  } catch (RuntimeError &e) {
277  e.add_context("closing \"" + filename() + "\"");
278  throw;
279  }
280 }
281 
282 void File::sync() const {
283  try {
284  m_impl->nc->sync();
285  } catch (RuntimeError &e) {
286  e.add_context("synchronizing \"" + filename() + "\"");
287  throw;
288  }
289 }
290 
291 void File::redef() const {
292  try {
293  m_impl->nc->redef();
294  } catch (RuntimeError &e) {
295  e.add_context("switching to define mode; file \"" + filename() + "\"");
296  throw;
297  }
298 }
299 
300 
301 void File::enddef() const {
302  try {
303  m_impl->nc->enddef();
304  } catch (RuntimeError &e) {
305  e.add_context("switching to data mode; file \"" + filename() + "\"");
306  throw;
307  }
308 }
309 
310 std::string File::filename() const {
311  return m_impl->nc->filename();
312 }
313 
314 
315 //! \brief Get the number of records. Uses the length of an unlimited dimension.
316 unsigned int File::nrecords() const {
317  try {
318  std::string dim;
319  m_impl->nc->inq_unlimdim(dim);
320 
321  if (dim.empty()) {
322  return 1; // one record
323  }
324 
325  return this->dimension_length(dim);
326  } catch (RuntimeError &e) {
327  e.add_context("getting the number of records in file \"" + filename() + "\"");
328  throw;
329  }
330  return 0; // LCOV_EXCL_LINE
331 }
332 
333 //! \brief Get the number of records of a certain variable. Uses the length of
334 //! an associated "time" dimension.
335 unsigned int File::nrecords(const std::string &name, const std::string &std_name,
336  units::System::Ptr unit_system) const {
337  try {
338  auto var = find_variable(name, std_name);
339 
340  if (not var.exists) {
341  return 0;
342  }
343 
344  for (const auto &d : dimensions(var.name)) {
345  if (dimension_type(d, unit_system) == T_AXIS) {
346  return this->dimension_length(d);
347  }
348  }
349 
350  return 1; // one record
351  } catch (RuntimeError &e) {
352  e.add_context("getting the number of records of variable '%s' ('%s') in '%s'",
353  name.c_str(), std_name.c_str(), filename().c_str());
354  throw;
355  }
356  return 0; // LCOV_EXCL_LINE
357 }
358 
359 
360 //! \brief Find a variable using its standard name and/or short name.
361 /*!
362  * Sets "result" to the short name found.
363  */
364 VariableLookupData File::find_variable(const std::string &short_name, const std::string &std_name) const {
365  VariableLookupData result;
366  try {
367  result.exists = false;
368 
369  if (not std_name.empty()) {
370 
371  int n_variables = nvariables();
372 
373  for (int j = 0; j < n_variables; ++j) {
374  std::string name = variable_name(j);
375  std::string attribute = read_text_attribute(name, "standard_name");
376 
377  if (attribute.empty()) {
378  continue;
379  }
380 
381  if (attribute == std_name) {
382  if (not result.exists) {
383  result.exists = true;
384  result.found_using_standard_name = true;
385  result.name = name;
386  } else {
387  throw RuntimeError::formatted(PISM_ERROR_LOCATION, "inconsistency in '%s': variables '%s' and '%s'\n"
388  "have the same standard_name (%s)",
389  filename().c_str(), result.name.c_str(),
390  name.c_str(), attribute.c_str());
391  }
392  }
393 
394  } // end of the for loop
395  } // end of if (not std_name.empty())
396 
397  if (not result.exists) {
398  m_impl->nc->inq_varid(short_name, result.exists);
399  if (result.exists) {
400  result.name = short_name;
401  } else {
402  result.name = "";
403  }
404 
405  result.found_using_standard_name = false;
406  }
407 
408  } catch (RuntimeError &e) {
409  e.add_context("searching for variable '%s' ('%s') in '%s'", short_name.c_str(), std_name.c_str(), filename().c_str());
410  throw;
411  }
412 
413  return result;
414 }
415 
416 //! \brief Checks if a variable exists.
417 bool File::find_variable(const std::string &name) const {
418  try {
419  bool exists = false;
420  m_impl->nc->inq_varid(name, exists);
421  return exists;
422  } catch (RuntimeError &e) {
423  e.add_context("searching for variable '%s' in '%s'", name.c_str(), filename().c_str());
424  throw;
425  }
426 }
427 
428 std::vector<std::string> File::dimensions(const std::string &variable_name) const {
429  try {
430  std::vector<std::string> result;
431  m_impl->nc->inq_vardimid(variable_name, result);
432  return result;
433  } catch (RuntimeError &e) {
434  e.add_context("getting dimensions of variable '%s' in '%s'", variable_name.c_str(),
435  filename().c_str());
436  throw;
437  }
438 }
439 
440 
441 //! \brief Checks if a dimension exists.
442 bool File::find_dimension(const std::string &name) const {
443  try {
444  bool exists = false;
445  m_impl->nc->inq_dimid(name, exists);
446  return exists;
447  } catch (RuntimeError &e) {
448  e.add_context("searching for dimension '%s' in '%s'", name.c_str(), filename().c_str());
449  throw;
450  }
451 }
452 
453 //! \brief Get the length of a dimension.
454 /*!
455  * Sets result to 0 if a dimension does not exist.
456  */
457 unsigned int File::dimension_length(const std::string &name) const {
458  try {
459  if (find_dimension(name)) {
460  unsigned int result = 0;
461  m_impl->nc->inq_dimlen(name, result);
462  return result;
463  }
464 
465  return 0;
466  } catch (RuntimeError &e) {
467  e.add_context("getting the length of dimension '%s' in '%s'", name.c_str(), filename().c_str());
468  throw;
469  }
470 }
471 
472 //! \brief Get the "type" of a dimension.
473 /*!
474  * The "type" is one of X_AXIS, Y_AXIS, Z_AXIS, T_AXIS.
475  */
476 AxisType File::dimension_type(const std::string &name,
477  units::System::Ptr unit_system) const {
478  try {
479  if (not find_variable(name)) {
480  throw RuntimeError(PISM_ERROR_LOCATION, "coordinate variable " + name + " is missing");
481  }
482 
483  std::string
484  axis = read_text_attribute(name, "axis"),
485  standard_name = read_text_attribute(name, "standard_name"),
486  units = read_text_attribute(name, "units");
487 
488  // check if it has units compatible with "seconds":
489 
490  units::Unit seconds(unit_system, "seconds");
491  if (units::are_convertible(units::Unit(unit_system, units), seconds)) {
492  return T_AXIS;
493  }
494 
495  // check the standard_name attribute:
496  if (standard_name == "time") {
497  return T_AXIS;
498  }
499 
500  if (standard_name == "projection_x_coordinate") {
501  return X_AXIS;
502  }
503 
504  if (standard_name == "projection_y_coordinate") {
505  return Y_AXIS;
506  }
507 
508  // check the axis attribute:
509  if (axis == "T" or axis == "t") {
510  return T_AXIS;
511  }
512 
513  if (axis == "X" or axis == "x") {
514  return X_AXIS;
515  }
516 
517  if (axis == "Y" or axis == "y") {
518  return Y_AXIS;
519  }
520 
521  if (axis == "Z" or axis == "z") {
522  return Z_AXIS;
523  }
524 
525  // check the variable name:
526  if (name == "x" or name == "X" or
527  name.find('x') == 0 or name.find('X') == 0) {
528  return X_AXIS;
529  }
530 
531  if (name == "y" or name == "Y" or
532  name.find('y') == 0 or name.find('Y') == 0) {
533  return Y_AXIS;
534  }
535 
536  if (name == "z" or name == "Z" or
537  name.find('z') == 0 or name.find('Z') == 0) {
538  return Z_AXIS;
539  }
540 
541  if (name == "t" or name == "T" or name == "time" or
542  name.find('t') == 0 or name.find('T') == 0) {
543  return T_AXIS;
544  }
545 
546  // we have no clue:
547  return UNKNOWN_AXIS;
548  } catch (RuntimeError &e) {
549  e.add_context("getting the type of dimension '%s' in '%s'",
550  name.c_str(), filename().c_str());
551  throw;
552  }
553  return UNKNOWN_AXIS; // LCOV_EXCL_LINE
554 }
555 
556 void File::define_dimension(const std::string &name, size_t length) const {
557  try {
558  m_impl->nc->def_dim(name, length);
559  } catch (RuntimeError &e) {
560  e.add_context("defining dimension '%s' in '%s'", name.c_str(), filename().c_str());
561  throw;
562  }
563 }
564 
565 //! \brief Define a variable.
566 void File::define_variable(const std::string &name, IO_Type nctype, const std::vector<std::string> &dims) const {
567  try {
568  m_impl->nc->def_var(name, nctype, dims);
569 
570  // FIXME: I need to write and tune chunk_dimensions that would be called below before we use
571  // this.
572  //
573  /*
574  // if it's not a spatial variable, we're done
575  if (dims.size() < 2) {
576  return;
577  }
578 
579  std::vector<size_t> dim_lengths;
580  for (unsigned int k = 0; k < dims.size(); ++k) {
581  dim_lengths.push_back(this->dimension_length(dims[k]));
582  }
583 
584  std::vector<size_t> chunk_dims = chunk_dimensions(nctype, dim_lengths);
585 
586  m_impl->nc->def_var_chunking(name, chunk_dims);
587  */
588 
589  } catch (RuntimeError &e) {
590  e.add_context("defining variable '%s' in '%s'", name.c_str(), filename().c_str());
591  throw;
592  }
593 }
594 
595 //! \brief Get dimension data (a coordinate variable).
596 std::vector<double> File::read_dimension(const std::string &name) const {
597  try {
598  if (not find_variable(name)) {
599  throw RuntimeError(PISM_ERROR_LOCATION, "coordinate variable not found");
600  }
601 
602  unsigned int length = dimension_length(name);
603 
604  std::vector<double> result(length);
605 
606  read_variable(name, {0}, {length}, result.data());
607 
608  return result;
609  } catch (RuntimeError &e) {
610  e.add_context("reading dimension '%s' from '%s'", name.c_str(), filename().c_str());
611  throw;
612  }
613 }
614 
615 //! \brief Append to the history global attribute.
616 /*!
617  * Use write_attribute("PISM_GLOBAL", "history", ...) to overwrite "history".
618  */
619 void File::append_history(const std::string &history) const {
620  try {
621  std::string old_history = read_text_attribute("PISM_GLOBAL", "history");
622  redef();
623  write_attribute("PISM_GLOBAL", "history", history + old_history);
624  } catch (RuntimeError &e) {
625  e.add_context("appending to the history attribute in \"" + filename() + "\"");
626  throw;
627  }
628 }
629 
630 //! \brief Write a multiple-valued double attribute.
631 void File::write_attribute(const std::string &var_name, const std::string &att_name, IO_Type nctype,
632  const std::vector<double> &values) const {
633  try {
634  redef();
635  m_impl->nc->put_att_double(var_name, att_name, nctype, values);
636  } catch (RuntimeError &e) {
637  e.add_context("writing double attribute '%s:%s' in '%s'",
638  var_name.c_str(), att_name.c_str(), filename().c_str());
639  throw;
640  }
641 }
642 
643 //! \brief Write a text attribute.
644 void File::write_attribute(const std::string &var_name, const std::string &att_name,
645  const std::string &value) const {
646  try {
647  redef();
648  // ensure that the string is null-terminated
649  m_impl->nc->put_att_text(var_name, att_name, value + "\0");
650  } catch (RuntimeError &e) {
651  e.add_context("writing text attribute '%s:%s' in '%s'",
652  var_name.c_str(), att_name.c_str(), filename().c_str());
653  throw;
654  }
655 }
656 
657 //! \brief Get a double attribute.
658 std::vector<double> File::read_double_attribute(const std::string &var_name, const std::string &att_name) const {
659  try {
660  auto att_type = attribute_type(var_name, att_name);
661 
662  // Give an understandable error message if a string attribute was found when
663  // a number (or a list of numbers) was expected. (We've seen datasets with
664  // "valid_min" stored as a string...)
665  if (att_type == PISM_CHAR) {
666  std::string tmp = read_text_attribute(var_name, att_name);
667 
669  "attribute %s is a string '%s'; expected a number or a list of numbers",
670  att_name.c_str(), tmp.c_str());
671  }
672 
673  // In this case att_type might be PISM_NAT (if an attribute does not
674  // exist), but read_double_attribute can handle that.
675  std::vector<double> result;
676  m_impl->nc->get_att_double(var_name, att_name, result);
677  return result;
678  } catch (RuntimeError &e) {
679  e.add_context("reading double attribute '%s:%s' from '%s'",
680  var_name.c_str(), att_name.c_str(), filename().c_str());
681  throw;
682  }
683 }
684 
685 //! \brief Get a text attribute.
686 std::string File::read_text_attribute(const std::string &var_name, const std::string &att_name) const {
687  try {
688  auto att_type = attribute_type(var_name, att_name);
689  if (att_type != PISM_NAT and att_type != PISM_CHAR) {
690  // attribute exists and is not a string
692  "attribute %s is not a string", att_name.c_str());
693  }
694 
695  std::string result;
696  m_impl->nc->get_att_text(var_name, att_name, result);
697  return result;
698  } catch (RuntimeError &e) {
699  e.add_context("reading text attribute '%s:%s' from %s", var_name.c_str(), att_name.c_str(), filename().c_str());
700  throw;
701  }
702 }
703 
704 unsigned int File::nattributes(const std::string &var_name) const {
705  try {
706  int result = 0;
707  m_impl->nc->inq_varnatts(var_name, result);
708  return result;
709  } catch (RuntimeError &e) {
710  e.add_context("getting the number of attributes of variable '%s' in '%s'", var_name.c_str(), filename().c_str());
711  throw;
712  }
713 }
714 
715 
716 std::string File::attribute_name(const std::string &var_name, unsigned int n) const {
717  try {
718  std::string result;
719  m_impl->nc->inq_attname(var_name, n, result);
720  return result;
721  } catch (RuntimeError &e) {
722  e.add_context("getting the name of an attribute of variable '%s' in '%s'", var_name.c_str(), filename().c_str());
723  throw;
724  }
725 }
726 
727 
728 IO_Type File::attribute_type(const std::string &var_name, const std::string &att_name) const {
729  try {
730  IO_Type result;
731  m_impl->nc->inq_atttype(var_name, att_name, result);
732  return result;
733  } catch (RuntimeError &e) {
734  e.add_context("getting the type of an attribute of variable '%s' in '%s'", var_name.c_str(), filename().c_str());
735  throw;
736  }
737 }
738 
739 
740 void File::read_variable(const std::string &variable_name,
741  const std::vector<unsigned int> &start,
742  const std::vector<unsigned int> &count,
743  double *ip) const {
744  try {
745  m_impl->nc->get_vara_double(variable_name, start, count, ip);
746  } catch (RuntimeError &e) {
747  e.add_context("reading variable '%s' from '%s'", variable_name.c_str(), filename().c_str());
748  throw;
749  }
750 }
751 
752 
753 void File::write_variable(const std::string &variable_name,
754  const std::vector<unsigned int> &start,
755  const std::vector<unsigned int> &count,
756  const double *op) const {
757  try {
758  m_impl->nc->put_vara_double(variable_name, start, count, op);
759  } catch (RuntimeError &e) {
760  e.add_context("writing variable '%s' to '%s'", variable_name.c_str(), filename().c_str());
761  throw;
762  }
763 }
764 
765 
766 void File::write_distributed_array(const std::string &variable_name,
767  const IceGrid &grid,
768  unsigned int z_count,
769  const double *input) const {
770  try {
771  unsigned int t_length = nrecords();
772  assert(t_length > 0);
773 
774  m_impl->nc->write_darray(variable_name, grid, z_count, t_length - 1, input);
775  } catch (RuntimeError &e) {
776  e.add_context("writing distributed array '%s' to '%s'",
777  variable_name.c_str(), filename().c_str());
778  throw;
779  }
780 }
781 
782 
783 void File::read_variable_transposed(const std::string &variable_name,
784  const std::vector<unsigned int> &start,
785  const std::vector<unsigned int> &count,
786  const std::vector<unsigned int> &imap, double *ip) const {
787  try {
788  m_impl->nc->get_varm_double(variable_name, start, count, imap, ip);
789  } catch (RuntimeError &e) {
790  e.add_context("reading variable '%s' from '%s'", variable_name.c_str(), filename().c_str());
791  throw;
792  }
793 }
794 
795 unsigned int File::nvariables() const {
796  int n_vars = 0;
797 
798  try {
799  m_impl->nc->inq_nvars(n_vars);
800  } catch (RuntimeError &e) {
801  e.add_context("getting the number of variables in '%s'", filename().c_str());
802  throw;
803  }
804 
805  return n_vars;
806 }
807 
808 std::string File::variable_name(unsigned int id) const {
809  std::string result;
810  try {
811  m_impl->nc->inq_varname(id, result);
812  } catch (RuntimeError &e) {
813  e.add_context("getting the name of %d-th variable in '%s'", id, filename().c_str());
814  throw;
815  }
816 
817  return result;
818 }
819 
820 
821 } // end of namespace pism
bool find_dimension(const std::string &name) const
Checks if a dimension exists.
Definition: File.cc:442
unsigned int nvariables() const
Definition: File.cc:795
Impl * m_impl
Definition: File.hh:149
void read_variable(const std::string &variable_name, const std::vector< unsigned int > &start, const std::vector< unsigned int > &count, double *ip) const
Definition: File.cc:740
void read_variable_transposed(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: File.cc:783
void define_dimension(const std::string &name, size_t length) const
Definition: File.cc:556
std::string attribute_name(const std::string &var_name, unsigned int n) const
Definition: File.cc:716
void write_distributed_array(const std::string &variable_name, const IceGrid &grid, unsigned int z_count, const double *input) const
Definition: File.cc:766
AxisType dimension_type(const std::string &name, units::System::Ptr unit_system) const
Get the "type" of a dimension.
Definition: File.cc:476
void set_compression_level(int level) const
Definition: File.cc:225
void define_variable(const std::string &name, IO_Type nctype, const std::vector< std::string > &dims) const
Define a variable.
Definition: File.cc:566
File(MPI_Comm com, const std::string &filename, IO_Backend backend, IO_Mode mode, int iosysid=-1)
Definition: File.cc:183
void redef() const
Definition: File.cc:291
VariableLookupData find_variable(const std::string &short_name, const std::string &std_name) const
Find a variable using its standard name and/or short name.
Definition: File.cc:364
void enddef() const
Definition: File.cc:301
unsigned int nrecords() const
Get the number of records. Uses the length of an unlimited dimension.
Definition: File.cc:316
MPI_Comm com() const
Definition: File.cc:217
std::string variable_name(unsigned int id) const
Definition: File.cc:808
~File()
Definition: File.cc:204
std::string filename() const
Definition: File.cc:310
void write_variable(const std::string &variable_name, const std::vector< unsigned int > &start, const std::vector< unsigned int > &count, const double *op) const
Definition: File.cc:753
void sync() const
Definition: File.cc:282
void close()
Definition: File.cc:273
void remove_attribute(const std::string &variable_name, const std::string &att_name) const
Definition: File.cc:264
std::vector< double > read_double_attribute(const std::string &var_name, const std::string &att_name) const
Get a double attribute.
Definition: File.cc:658
void open(const std::string &filename, IO_Mode mode)
Definition: File.cc:229
IO_Backend backend() const
Definition: File.cc:221
unsigned int nattributes(const std::string &var_name) const
Definition: File.cc:704
void append_history(const std::string &history) const
Append to the history global attribute.
Definition: File.cc:619
void write_attribute(const std::string &var_name, const std::string &att_name, IO_Type nctype, const std::vector< double > &values) const
Write a multiple-valued double attribute.
Definition: File.cc:631
unsigned int dimension_length(const std::string &name) const
Get the length of a dimension.
Definition: File.cc:457
std::vector< double > read_dimension(const std::string &name) const
Get dimension data (a coordinate variable).
Definition: File.cc:596
IO_Type attribute_type(const std::string &var_name, const std::string &att_name) const
Definition: File.cc:728
std::vector< std::string > dimensions(const std::string &variable_name) const
Definition: File.cc:428
std::string read_text_attribute(const std::string &var_name, const std::string &att_name) const
Get a text attribute.
Definition: File.cc:686
Describes the PISM grid and the distribution of data across processors.
Definition: IceGrid.hh:228
void add_context(const std::string &message)
Add a message providing some context. This way we can (sort of) get a stack trace even though C++ exc...
static RuntimeError formatted(const ErrorLocation &location, const char format[],...) __attribute__((format(printf
build a RuntimeError with a formatted message
std::shared_ptr< NCFile > Ptr
Definition: NCFile.hh:59
void open(const std::string &filename, IO_Mode mode)
Definition: NCFile.cc:61
void close()
Definition: NCFile.cc:78
std::string get_format() const
Definition: NC_Serial.cc:882
PISM's PnetCDF I/O wrapper.
Definition: PNCFile.hh:32
std::shared_ptr< System > Ptr
Definition: Units.hh:47
#define PISM_ERROR_LOCATION
#define n
Definition: exactTestM.c:37
void move_if_exists(MPI_Comm com, const std::string &file_to_move, int rank_to_use)
Moves the file aside (file.nc -> file.nc~).
Definition: io_helpers.cc:1571
void remove_if_exists(MPI_Comm com, const std::string &file_to_remove, int rank_to_use)
Check if a file is present are remove it.
Definition: io_helpers.cc:1606
bool are_convertible(const Unit &u1, const Unit &u2)
Definition: Units.cc:244
static IO_Backend choose_backend(MPI_Comm com, const std::string &filename)
Definition: File.cc:100
static std::string backend_to_string(IO_Backend backend)
Definition: File.cc:82
IO_Type
Definition: IO_Flags.hh:31
@ PISM_NAT
Definition: IO_Flags.hh:32
@ PISM_CHAR
Definition: IO_Flags.hh:34
IO_Backend string_to_backend(const std::string &backend)
Definition: File.cc:60
static io::NCFile::Ptr create_backend(MPI_Comm com, IO_Backend backend, int iosysid)
Definition: File.cc:129
@ PISM_NOFILL
Definition: IO_Flags.hh:67
IO_Backend
Definition: IO_Flags.hh:41
@ PISM_GUESS
Definition: IO_Flags.hh:41
@ PISM_PIO_NETCDF
Definition: IO_Flags.hh:42
@ PISM_PIO_PNETCDF
Definition: IO_Flags.hh:42
@ PISM_PIO_NETCDF4P
Definition: IO_Flags.hh:42
@ PISM_NETCDF3
Definition: IO_Flags.hh:41
@ PISM_PIO_NETCDF4C
Definition: IO_Flags.hh:42
@ PISM_NETCDF4_PARALLEL
Definition: IO_Flags.hh:41
@ PISM_PNETCDF
Definition: IO_Flags.hh:42
@ PISM_NETCDF4_SERIAL
Definition: IO_Flags.hh:41
void handle_fatal_errors(MPI_Comm com)
IO_Mode
Definition: IO_Flags.hh:47
@ PISM_READWRITE_CLOBBER
create a file for writing, overwrite if present
Definition: IO_Flags.hh:53
@ PISM_READWRITE_MOVE
create a file for writing, move foo.nc to foo.nc~ if present
Definition: IO_Flags.hh:55
@ PISM_READWRITE
open an existing file for reading and writing
Definition: IO_Flags.hh:51
@ PISM_READONLY
open an existing file for reading only
Definition: IO_Flags.hh:49
AxisType
Definition: File.hh:31
@ UNKNOWN_AXIS
Definition: File.hh:31
@ T_AXIS
Definition: File.hh:31
@ X_AXIS
Definition: File.hh:31
@ Z_AXIS
Definition: File.hh:31
@ Y_AXIS
Definition: File.hh:31
io::NCFile::Ptr nc
Definition: File.cc:57
MPI_Comm com
Definition: File.cc:55
IO_Backend backend
Definition: File.cc:56
std::string name
Definition: File.hh:43
bool found_using_standard_name
Definition: File.hh:42
int count
Definition: test_cube.c:16