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