/* MDAL - Mesh Data Abstraction Library (MIT License) Copyright (C) 2019 Peter Petrik (zilolv at gmail dot com) */ #include #include #include #include #include #include #include "mdal_netcdf.hpp" #include "mdal.h" #include "mdal_utils.hpp" #include "mdal_logger.hpp" NetCDFFile::NetCDFFile(): mNcid( 0 ) {} NetCDFFile::~NetCDFFile() { if ( mNcid != 0 ) { nc_close( mNcid ); mNcid = 0; } } int NetCDFFile::handle() const { return mNcid; } void NetCDFFile::openFile( const std::string &fileName, bool write ) { int res = nc_open( MDAL::systemFileName( fileName ).c_str(), write ? NC_WRITE : NC_NOWRITE, &mNcid ); if ( res != NC_NOERR ) { throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not open file " + fileName ); } mFileName = fileName; } std::vector NetCDFFile::readIntArr( const std::string &name, size_t dim ) const { assert( mNcid != 0 ); int arr_id; if ( nc_inq_varid( mNcid, name.c_str(), &arr_id ) != NC_NOERR ) throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Internal error in Netcfd - unknown format" ); std::vector arr_val( dim ); if ( nc_get_var_int( mNcid, arr_id, arr_val.data() ) != NC_NOERR ) throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Internal error in Netcfd - unknown format" ); return arr_val; } std::vector NetCDFFile::readIntArr( int arr_id, size_t start_dim1, size_t start_dim2, size_t count_dim1, size_t count_dim2 ) const { assert( mNcid != 0 ); const std::vector startp = {start_dim1, start_dim2}; const std::vector countp = {count_dim1, count_dim2}; const std::vector stridep = {1, 1}; std::vector arr_val( count_dim1 * count_dim2 ); int res = nc_get_vars_int( mNcid, arr_id, startp.data(), countp.data(), stridep.data(), arr_val.data() ); if ( res != NC_NOERR ) throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not read numeric array" ); return arr_val; } std::vector NetCDFFile::readIntArr( int arr_id, size_t start_dim, size_t count_dim ) const { assert( mNcid != 0 ); const std::vector startp = {start_dim}; const std::vector countp = {count_dim}; const std::vector stridep = {1}; std::vector arr_val( count_dim ); int res = nc_get_vars_int( mNcid, arr_id, startp.data(), countp.data(), stridep.data(), arr_val.data() ); if ( res != NC_NOERR ) throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not read numeric array" ); return arr_val; } std::vector NetCDFFile::readDoubleArr( const std::string &name, size_t dim ) const { assert( mNcid != 0 ); int arr_id; if ( nc_inq_varid( mNcid, name.c_str(), &arr_id ) != NC_NOERR ) throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not read double array" ); std::vector arr_val( dim ); nc_type typep; if ( nc_inq_varid( mNcid, name.c_str(), &arr_id ) != NC_NOERR ) throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not read double array" ); if ( nc_inq_vartype( mNcid, arr_id, &typep ) != NC_NOERR ) throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not read double array" ); if ( typep == NC_FLOAT ) { std::vector arr_val_f( dim ); if ( nc_get_var_float( mNcid, arr_id, arr_val_f.data() ) != NC_NOERR ) throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not read double array" ); for ( size_t i = 0; i < dim; ++i ) { const float val = arr_val_f[i]; if ( std::isnan( val ) ) arr_val[i] = std::numeric_limits::quiet_NaN(); else arr_val[i] = static_cast( val ); } } else if ( typep == NC_DOUBLE ) { if ( nc_get_var_double( mNcid, arr_id, arr_val.data() ) != NC_NOERR ) throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not read double array" ); } else if ( typep == NC_INT ) { std::vector arr_val_int( dim ); if ( nc_get_var_int( mNcid, arr_id, arr_val_int.data() ) != NC_NOERR ) throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not read integer array" ); for ( size_t i = 0; i < dim; ++i ) { arr_val[i] = arr_val_int[i]; } } else if ( typep == NC_UINT ) { std::vector arr_val_uint( dim ); if ( nc_get_var_uint( mNcid, arr_id, arr_val_uint.data() ) != NC_NOERR ) throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not read integer array" ); for ( size_t i = 0; i < dim; ++i ) { arr_val[i] = arr_val_uint[i]; } } else if ( typep == NC_INT64 ) { std::vector arr_val_int( dim ); if ( nc_get_var_longlong( mNcid, arr_id, arr_val_int.data() ) != NC_NOERR ) throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not read integer array" ); for ( size_t i = 0; i < dim; ++i ) { arr_val[i] = static_cast( arr_val_int[i] ); } } else if ( typep == NC_UINT64 ) { std::vector arr_val_int( dim ); if ( nc_get_var_ulonglong( mNcid, arr_id, arr_val_int.data() ) != NC_NOERR ) throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not read integer array" ); for ( size_t i = 0; i < dim; ++i ) { arr_val[i] = static_cast( arr_val_int[i] ); } } else { throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not read double array" ); } return arr_val; } std::vector NetCDFFile::readDoubleArr( int arr_id, size_t start_dim1, size_t start_dim2, size_t count_dim1, size_t count_dim2 ) const { assert( mNcid != 0 ); const std::vector startp = {start_dim1, start_dim2}; const std::vector countp = {count_dim1, count_dim2}; const std::vector stridep = {1, 1}; std::vector arr_val( count_dim1 * count_dim2 ); nc_type typep; if ( nc_inq_vartype( mNcid, arr_id, &typep ) != NC_NOERR ) throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not read double array" ); if ( typep == NC_FLOAT ) { std::vector arr_val_f( count_dim1 * count_dim2 ); if ( nc_get_vars_float( mNcid, arr_id, startp.data(), countp.data(), stridep.data(), arr_val_f.data() ) != NC_NOERR ) throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not read double array" ); for ( size_t i = 0; i < count_dim1 * count_dim2; ++i ) { const float val = arr_val_f[i]; if ( std::isnan( val ) ) arr_val[i] = std::numeric_limits::quiet_NaN(); else arr_val[i] = static_cast( val ); } } else if ( typep == NC_BYTE ) { std::vector arr_val_b( count_dim1 * count_dim2 ); if ( nc_get_vars_uchar( mNcid, arr_id, startp.data(), countp.data(), stridep.data(), arr_val_b.data() ) != NC_NOERR ) throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not read double array" ); for ( size_t i = 0; i < count_dim1 * count_dim2; ++i ) { const unsigned char val = arr_val_b[i]; if ( val == 129 ) arr_val[i] = std::numeric_limits::quiet_NaN(); else arr_val[i] = double( int( val ) ); } } else if ( typep == NC_DOUBLE ) { if ( nc_get_vars_double( mNcid, arr_id, startp.data(), countp.data(), stridep.data(), arr_val.data() ) != NC_NOERR ) throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not read double array" ); } else if ( typep == NC_INT ) { std::vector arr_val_int( count_dim1 * count_dim2 ); if ( nc_get_vars_int( mNcid, arr_id, startp.data(), countp.data(), stridep.data(), arr_val_int.data() ) != NC_NOERR ) throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not read double array" ); for ( size_t i = 0; i < count_dim1 * count_dim2; ++i ) { arr_val[i] = arr_val_int[i]; } } else if ( typep == NC_UINT ) { std::vector arr_val_uint( count_dim1 * count_dim2 ); if ( nc_get_vars_uint( mNcid, arr_id, startp.data(), countp.data(), stridep.data(), arr_val_uint.data() ) != NC_NOERR ) throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not read double array" ); for ( size_t i = 0; i < count_dim1 * count_dim2; ++i ) { arr_val[i] = arr_val_uint[i]; } } else if ( typep == NC_INT64 ) { std::vector arr_val_int( count_dim1 * count_dim2 ); if ( nc_get_vars_longlong( mNcid, arr_id, startp.data(), countp.data(), stridep.data(), arr_val_int.data() ) != NC_NOERR ) throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not read double array" ); for ( size_t i = 0; i < count_dim1 * count_dim2; ++i ) { arr_val[i] = static_cast( arr_val_int[i] ); } } else if ( typep == NC_UINT64 ) { std::vector arr_val_int( count_dim1 * count_dim2 ); if ( nc_get_vars_ulonglong( mNcid, arr_id, startp.data(), countp.data(), stridep.data(), arr_val_int.data() ) != NC_NOERR ) throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not read double array" ); for ( size_t i = 0; i < count_dim1 * count_dim2; ++i ) { arr_val[i] = static_cast( arr_val_int[i] ); } } else { throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not read double array" ); } return arr_val; } std::vector NetCDFFile::readDoubleArr( int arr_id, size_t start_dim, size_t count_dim ) const { assert( mNcid != 0 ); const std::vector startp = {start_dim}; const std::vector countp = {count_dim}; const std::vector stridep = {1, 1}; std::vector arr_val( count_dim ); nc_type typep; if ( nc_inq_vartype( mNcid, arr_id, &typep ) != NC_NOERR ) throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not read double array" ); if ( typep == NC_FLOAT ) { std::vector arr_val_f( count_dim ); if ( nc_get_vars_float( mNcid, arr_id, startp.data(), countp.data(), stridep.data(), arr_val_f.data() ) != NC_NOERR ) throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not read double array" ); for ( size_t i = 0; i < count_dim; ++i ) { const float val = arr_val_f[i]; if ( std::isnan( val ) ) arr_val[i] = std::numeric_limits::quiet_NaN(); else arr_val[i] = static_cast( val ); } } else if ( typep == NC_INT ) { std::vector arr_val_int( count_dim ); if ( nc_get_vars_int( mNcid, arr_id, startp.data(), countp.data(), stridep.data(), arr_val_int.data() ) != NC_NOERR ) throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not read double array" ); for ( size_t i = 0; i < count_dim; ++i ) { arr_val[i] = static_cast( arr_val_int[i] ); } } else if ( typep == NC_DOUBLE ) { if ( nc_get_vars_double( mNcid, arr_id, startp.data(), countp.data(), stridep.data(), arr_val.data() ) != NC_NOERR ) throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not read double array" ); } else if ( typep == NC_UINT ) { std::vector arr_val_uint( count_dim ); if ( nc_get_vars_uint( mNcid, arr_id, startp.data(), countp.data(), stridep.data(), arr_val_uint.data() ) != NC_NOERR ) throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not read double array" ); for ( size_t i = 0; i < count_dim; ++i ) { arr_val[i] = arr_val_uint[i]; } } else if ( typep == NC_INT64 ) { std::vector arr_val_int( count_dim ); if ( nc_get_vars_longlong( mNcid, arr_id, startp.data(), countp.data(), stridep.data(), arr_val_int.data() ) != NC_NOERR ) throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not read double array" ); for ( size_t i = 0; i < count_dim; ++i ) { arr_val[i] = static_cast( arr_val_int[i] ); } } else if ( typep == NC_UINT64 ) { std::vector arr_val_int( count_dim ); if ( nc_get_vars_ulonglong( mNcid, arr_id, startp.data(), countp.data(), stridep.data(), arr_val_int.data() ) != NC_NOERR ) throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not read double array" ); for ( size_t i = 0; i < count_dim; ++i ) { arr_val[i] = static_cast( arr_val_int[i] ); } } else { throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not read double array" ); } return arr_val; } bool NetCDFFile::hasArr( const std::string &name ) const { assert( mNcid != 0 ); int arr_id; return nc_inq_varid( mNcid, name.c_str(), &arr_id ) == NC_NOERR; } int NetCDFFile::arrId( const std::string &name ) const { int arr_id = -1; if ( nc_inq_varid( mNcid, name.c_str(), &arr_id ) != NC_NOERR ) { arr_id = -1; } return arr_id; } std::vector NetCDFFile::readArrNames() const { assert( mNcid != 0 ); std::vector res; int nvars; if ( nc_inq_varids( mNcid, &nvars, nullptr ) != NC_NOERR ) throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not read variable names" ); std::vector varids( static_cast( nvars ) ); if ( nc_inq_varids( mNcid, &nvars, varids.data() ) != NC_NOERR ) throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not read variable names" ); for ( size_t i = 0; i < static_cast( nvars ); ++i ) { std::vector cname( NC_MAX_NAME + 1 ); if ( nc_inq_varname( mNcid, varids[i], cname.data() ) != NC_NOERR ) throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not read variable names" ); res.push_back( cname.data() ); } return res; } bool NetCDFFile::hasAttrInt( const std::string &name, const std::string &attr_name ) const { assert( mNcid != 0 ); int arr_id; if ( nc_inq_varid( mNcid, name.c_str(), &arr_id ) != NC_NOERR ) return false; int val; if ( nc_get_att_int( mNcid, arr_id, attr_name.c_str(), &val ) ) return false; return true; } int NetCDFFile::getAttrInt( const std::string &name, const std::string &attr_name ) const { assert( mNcid != 0 ); int arr_id; if ( nc_inq_varid( mNcid, name.c_str(), &arr_id ) != NC_NOERR ) throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not get numeric attribute" ); int val; if ( nc_get_att_int( mNcid, arr_id, attr_name.c_str(), &val ) ) throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not get numeric attribute" ); return val; } std::string NetCDFFile::getAttrStr( const std::string &name, const std::string &attr_name ) const { assert( mNcid != 0 ); int arr_id; if ( nc_inq_varid( mNcid, name.c_str(), &arr_id ) ) throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not get string attribute" ); return getAttrStr( attr_name, arr_id ); } std::string NetCDFFile::getAttrStr( const std::string &attr_name, int varid ) const { assert( mNcid != 0 ); size_t attlen = 0; if ( nc_inq_attlen( mNcid, varid, attr_name.c_str(), &attlen ) ) { // attribute is missing return std::string(); } char *string_attr = static_cast( malloc( attlen + 1 ) ); if ( nc_get_att_text( mNcid, varid, attr_name.c_str(), string_attr ) ) throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not get string attribute" ); string_attr[attlen] = '\0'; std::string res( string_attr ); free( string_attr ); return res; } double NetCDFFile::getFillValue( int varid ) const { return getAttrDouble( varid, "_FillValue" ); } void NetCDFFile::setFillValue( int varid, double fillValue ) { putAttrDouble( varid, "_FillValue", fillValue ); } bool NetCDFFile::hasAttrDouble( int varid, const std::string &attr_name ) const { double res; if ( nc_get_att_double( mNcid, varid, attr_name.c_str(), &res ) ) return false; return true; } double NetCDFFile::getAttrDouble( int varid, const std::string &attr_name ) const { double res; if ( nc_get_att_double( mNcid, varid, attr_name.c_str(), &res ) ) res = std::numeric_limits::quiet_NaN(); // not present/set return res; } int NetCDFFile::getVarId( const std::string &name ) { int ncid_val; if ( nc_inq_varid( mNcid, name.c_str(), &ncid_val ) != NC_NOERR ) throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not get variable id" ); return ncid_val; } void NetCDFFile::getDimension( const std::string &name, size_t *val, int *ncid_val ) const { assert( mNcid != 0 ); if ( nc_inq_dimid( mNcid, name.c_str(), ncid_val ) != NC_NOERR ) throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not get dimension, invalid dimension ID or name" ); if ( nc_inq_dimlen( mNcid, *ncid_val, val ) != NC_NOERR ) throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not get dimension, invalid dimension ID or name" ); } void NetCDFFile::getDimensions( const std::string &variableName, std::vector &dimensions, std::vector &dimensionIds ) { assert( mNcid != 0 ); int n; int varId; if ( nc_inq_varid( mNcid, variableName.c_str(), &varId ) ) throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not get dimensions" ); if ( nc_inq_varndims( mNcid, varId, &n ) ) throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not get dimensions" ); dimensionIds.resize( size_t( n ) ); dimensions.resize( size_t( n ) ); if ( nc_inq_vardimid( mNcid, varId, dimensionIds.data() ) ) throw MDAL::Error( MDAL_Status::Err_UnknownFormat, "Could not get dimensions" ); for ( int i = 0; i < n; ++i ) { nc_inq_dimlen( mNcid, dimensionIds[size_t( i )], &dimensions[size_t( i )] ); } } bool NetCDFFile::hasDimension( const std::string &name ) const { int ncid_val; return nc_inq_dimid( mNcid, name.c_str(), &ncid_val ) == NC_NOERR; } void NetCDFFile::createFile( const std::string &fileName ) { int res = nc_create( MDAL::systemFileName( fileName ).c_str(), NC_CLOBBER, &mNcid ); if ( res != NC_NOERR ) { throw MDAL::Error( MDAL_Status::Err_FailToWriteToDisk, nc_strerror( res ) ); } } int NetCDFFile::defineDimension( const std::string &name, size_t size ) { int dimId = 0; int res = nc_def_dim( mNcid, name.c_str(), size, &dimId ); if ( res != NC_NOERR ) { throw MDAL::Error( MDAL_Status::Err_FailToWriteToDisk, nc_strerror( res ) ); } return dimId; } int NetCDFFile::defineVar( const std::string &varName, int ncType, int dimensionCount, const int *dimensions ) { int varIdp; int res = nc_def_var( mNcid, varName.c_str(), ncType, dimensionCount, dimensions, &varIdp ); if ( res != NC_NOERR ) { throw MDAL::Error( MDAL_Status::Err_FailToWriteToDisk, nc_strerror( res ) ); } return varIdp; } void NetCDFFile::putAttrStr( int varId, const std::string &attrName, const std::string &value ) { int res = nc_put_att_text( mNcid, varId, attrName.c_str(), value.size(), value.c_str() ); if ( res != NC_NOERR ) { throw MDAL::Error( MDAL_Status::Err_FailToWriteToDisk, nc_strerror( res ) ); } } void NetCDFFile::putAttrInt( int varId, const std::string &attrName, int value ) { int res = nc_put_att_int( mNcid, varId, attrName.c_str(), NC_INT, 1, &value ); if ( res != NC_NOERR ) { throw MDAL::Error( MDAL_Status::Err_FailToWriteToDisk, nc_strerror( res ) ); } } void NetCDFFile::putAttrDouble( int varId, const std::string &attrName, double value ) { int res = nc_put_att_double( mNcid, varId, attrName.c_str(), NC_DOUBLE, 1, &value ); if ( res != NC_NOERR ) { throw MDAL::Error( MDAL_Status::Err_FailToWriteToDisk, nc_strerror( res ) ); } } void NetCDFFile::putDataDouble( int varId, const size_t index, const double value ) { int res = nc_put_var1_double( mNcid, varId, &index, &value ); if ( res != NC_NOERR ) { throw MDAL::Error( MDAL_Status::Err_FailToWriteToDisk, nc_strerror( res ) ); } } void NetCDFFile::putDataArrayDouble( int varId, const size_t index, const std::vector &values ) { std::array indexes = {index, 0}; std::array sizes = {1, values.size()}; int res = nc_put_vara_double( mNcid, varId, indexes.data(), sizes.data(), values.data() ); if ( res != NC_NOERR ) { throw MDAL::Error( MDAL_Status::Err_FailToWriteToDisk, nc_strerror( res ) ); } } void NetCDFFile::putDataArrayInt( int varId, size_t line, size_t faceVerticesMax, int *values ) { // Configuration of these two vectors determines how is value array read and stored in the file // https://www.unidata.ucar.edu/software/netcdf/docs/programming_notes.html#specify_hyperslabfileNameToSave const size_t start[] = { line, 0 }; const size_t count[] = { 1, faceVerticesMax }; int res = nc_put_vara_int( mNcid, varId, start, count, values ); if ( res != NC_NOERR ) { throw MDAL::Error( MDAL_Status::Err_FailToWriteToDisk, nc_strerror( res ) ); } } std::string NetCDFFile::getFileName() const { return mFileName; }