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