27#include "pism/util/io/File.hh"
28#include "pism/util/Grid.hh"
29#include "pism/util/io/NC_Serial.hh"
30#include "pism/util/io/NC4_Serial.hh"
32#include "pism/pism_config.hh"
34#if (Pism_USE_PARALLEL_NETCDF4==1)
35#include "pism/util/io/NC4_Par.hh"
38#if (Pism_USE_PNETCDF==1)
39#include "pism/util/io/PNCFile.hh"
42#include "pism/util/error_handling.hh"
43#include "pism/util/io/io_helpers.hh"
44#include "pism/util/io/IO_Flags.hh"
45#include "pism/util/pism_utilities.hh"
51 std::shared_ptr<io::NCFile>
nc;
57 std::map<std::string, io::Backend> backends =
65 if (backends.find(backend) != backends.end()) {
66 return backends[backend];
70 "unknown or unsupported I/O backend: %s",
75 std::map<io::Backend, std::string> backends =
84 return backends[backend];
101#if (Pism_USE_PARALLEL_NETCDF4==1)
102 if (format ==
"netcdf4") {
107#if (Pism_USE_PNETCDF==1)
108 if (format !=
"netcdf4") {
119 MPI_Comm_size(com, &size);
124 return std::make_shared<io::NC_Serial>(com);
127 return std::make_shared<io::NC4_Serial>(com);
130#if (Pism_USE_PARALLEL_NETCDF4 == 1)
131 return std::make_shared<io::NC4_Par>(com);
137#if (Pism_USE_PNETCDF == 1)
138 return std::make_shared<io::PNCFile>(com);
150 "unknown or unsupported I/O backend: %s",
151 backend_name.c_str());
157 if (filename.empty()) {
159 "cannot open file: provided file name is empty");
169 this->
open(filename, mode);
190 m_impl->
nc->set_compression_level(level);
223 e.
add_context(
"opening or creating \"" + filename +
"\"");
291 e.
add_context(
"getting the number of records in file \"" +
name() +
"\"");
299unsigned int File::nrecords(
const std::string &variable_name,
const std::string &std_name,
304 if (not var.exists) {
316 e.
add_context(
"getting the number of records of variable '%s' ('%s') in '%s'",
333 if (not std_name.empty()) {
337 for (
int j = 0; j < n_variables; ++j) {
341 if (attribute.empty()) {
345 if (attribute == std_name) {
348 result.
name = var_name;
351 "have the same standard_name (%s)",
352 name().c_str(), result.
name.c_str(),
353 var_name.c_str(), attribute.c_str());
363 result.
name = short_name;
370 e.
add_context(
"searching for variable '%s' ('%s') in '%s'", short_name.c_str(), std_name.c_str(),
name().c_str());
392 std::vector<std::string> result;
407 m_impl->
nc->inq_dimid(dimension_name, exists);
410 e.
add_context(
"searching for dimension '%s' in '%s'", dimension_name.c_str(),
423 unsigned int result = 0;
424 m_impl->
nc->inq_dimlen(dimension_name, result);
430 e.
add_context(
"getting the length of dimension '%s' in '%s'", dimension_name.c_str(),
437 if (input ==
"T" or input ==
"t") {
441 if (input ==
"X" or input ==
"x") {
445 if (input ==
"Y" or input ==
"y") {
449 if (input ==
"Z" or input ==
"z") {
473 if (long_name ==
"experiment ID") {
485 if (standard_name ==
"time") {
489 if (
set_member(standard_name, {
"projection_x_coordinate",
"grid_longitude",
"longitude" }) or
490 units ==
"degree_east") {
494 if (
set_member(standard_name, {
"projection_y_coordinate",
"grid_latitude",
"latitude" }) or
495 units ==
"degree_north") {
507 if (
set_member(dimension_name, {
"x",
"X",
"rlon",
"lon",
"longitude"}) or
508 dimension_name.find(
'x') == 0 or dimension_name.find(
'X') == 0) {
512 if (
set_member(dimension_name, {
"y",
"Y",
"rlat",
"lat",
"latitude"}) or
513 dimension_name.find(
'y') == 0 or dimension_name.find(
'Y') == 0) {
517 if (dimension_name ==
"z" or dimension_name ==
"Z" or
518 dimension_name.find(
'z') == 0 or dimension_name.find(
'Z') == 0) {
522 if (dimension_name ==
"t" or dimension_name ==
"T" or dimension_name ==
"time" or
523 dimension_name.find(
't') == 0 or dimension_name.find(
'T') == 0) {
530 e.
add_context(
"getting the type of dimension '%s' in '%s'",
531 dimension_name.c_str(),
name().c_str());
539 m_impl->
nc->def_dim(dimension_name, length);
541 e.
add_context(
"defining dimension '%s' in '%s'", dimension_name.c_str(),
549 const std::vector<std::string> &dims)
const {
589 e.
add_context(
"appending to the history attribute in \"" +
name() +
"\"");
596 const std::vector<double> &values)
const {
599 m_impl->
nc->put_att_double(var_name, att_name, nctype, values);
601 e.
add_context(
"writing double attribute '%s:%s' in '%s'",
602 var_name.c_str(), att_name.c_str(),
name().c_str());
609 const std::string &value)
const {
613 m_impl->
nc->put_att_text(var_name, att_name, value +
"\0");
615 e.
add_context(
"writing text attribute '%s:%s' in '%s'",
616 var_name.c_str(), att_name.c_str(),
name().c_str());
633 "attribute %s is a string '%s'; expected a number or a list of numbers",
634 att_name.c_str(), tmp.c_str());
639 std::vector<double> result;
640 m_impl->
nc->get_att_double(var_name, att_name, result);
643 e.
add_context(
"reading double attribute '%s:%s' from '%s'",
644 var_name.c_str(), att_name.c_str(),
name().c_str());
656 "attribute %s is not a string", att_name.c_str());
660 m_impl->
nc->get_att_text(var_name, att_name, result);
663 e.
add_context(
"reading text attribute '%s:%s' from %s", var_name.c_str(), att_name.c_str(),
name().c_str());
671 m_impl->
nc->inq_varnatts(var_name, result);
674 e.
add_context(
"getting the number of attributes of variable '%s' in '%s'", var_name.c_str(),
name().c_str());
683 m_impl->
nc->inq_attname(var_name,
n, result);
686 e.
add_context(
"getting the name of an attribute of variable '%s' in '%s'", var_name.c_str(),
name().c_str());
695 m_impl->
nc->inq_atttype(var_name, att_name, result);
698 e.
add_context(
"getting the type of an attribute of variable '%s' in '%s'", var_name.c_str(),
name().c_str());
705 const std::vector<unsigned int> &start,
706 const std::vector<unsigned int> &
count,
718 const std::vector<unsigned int> &start,
719 const std::vector<unsigned int> &
count,
720 const double *op)
const {
730 const std::vector<unsigned int> &start,
731 const std::vector<unsigned int> &
count,
732 const std::string &input)
const {
744 unsigned int z_count,
746 const double *input)
const {
749 assert(t_length > 0);
753 e.
add_context(
"writing distributed array '%s' to '%s'",
765 e.
add_context(
"getting the number of variables in '%s'",
name().c_str());
775 m_impl->
nc->inq_varname(
id, result);
777 e.
add_context(
"getting the name of %d-th variable in '%s'",
id,
name().c_str());
bool dimension_exists(const std::string &name) const
Checks if a dimension exists.
unsigned int nvariables() const
void read_variable(const std::string &variable_name, const std::vector< unsigned int > &start, const std::vector< unsigned int > &count, double *ip) const
void define_dimension(const std::string &name, size_t length) const
std::string attribute_name(const std::string &var_name, unsigned int n) const
AxisType dimension_type(const std::string &name, units::System::Ptr unit_system) const
Get the "type" of a dimension.
void set_compression_level(int level) const
void open(const std::string &filename, io::Mode mode)
void set_variable_was_written(const std::string &name) const
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.
unsigned int nrecords() const
Get the number of records. Uses the length of an unlimited dimension.
std::string variable_name(unsigned int id) const
bool variable_exists(const std::string &short_name) const
Checks if a variable exists.
void write_variable(const std::string &variable_name, const std::vector< unsigned int > &start, const std::vector< unsigned int > &count, const double *op) const
File(MPI_Comm com, const std::string &filename, io::Backend backend, io::Mode mode)
void define_variable(const std::string &name, io::Type nctype, const std::vector< std::string > &dims) const
Define a variable.
io::Type attribute_type(const std::string &var_name, const std::string &att_name) const
void write_text_variable(const std::string &variable_name, const std::vector< unsigned int > &start, const std::vector< unsigned int > &count, const std::string &input) const
void remove_attribute(const std::string &variable_name, const std::string &att_name) const
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.
void write_distributed_array(const std::string &variable_name, const grid::DistributedGridInfo &grid, unsigned int z_count, bool time_dependent, const double *input) const
std::vector< double > read_double_attribute(const std::string &var_name, const std::string &att_name) const
Get a double attribute.
bool get_variable_was_written(const std::string &name) const
unsigned int nattributes(const std::string &var_name) const
void append_history(const std::string &history) const
Append to the history global attribute.
unsigned int dimension_length(const std::string &name) const
Get the length of a dimension.
std::vector< std::string > dimensions(const std::string &variable_name) const
std::string read_text_attribute(const std::string &var_name, const std::string &att_name) const
Get a text attribute.
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
void open(const std::string &filename, io::Mode mode)
std::string get_format() const
std::shared_ptr< System > Ptr
#define PISM_ERROR_LOCATION
@ PISM_READWRITE_CLOBBER
create a file for writing, overwrite if present
@ PISM_READWRITE_MOVE
create a file for writing, move foo.nc to foo.nc~ if present
@ PISM_READONLY
open an existing file for reading only
@ PISM_READWRITE
open an existing file for reading and writing
void move_if_exists(MPI_Comm com, const std::string &file_to_move, int rank_to_use=0)
Moves the file aside (file.nc -> file.nc~).
void remove_if_exists(MPI_Comm com, const std::string &file_to_remove, int rank_to_use=0)
Check if a file is present are remove it.
bool are_convertible(const Unit &u1, const Unit &u2)
io::Backend string_to_backend(const std::string &backend)
static io::Backend choose_backend(MPI_Comm com, const std::string &filename)
void handle_fatal_errors(MPI_Comm com)
static std::string backend_to_string(io::Backend backend)
AxisType axis_type_from_string(const std::string &input)
static std::shared_ptr< io::NCFile > create_backend(MPI_Comm com, io::Backend backend)
bool set_member(const std::string &string, const std::set< std::string > &set)
std::shared_ptr< io::NCFile > nc
std::set< std::string > written_variables