diff --git a/CMakeLists.txt b/CMakeLists.txt index cfefc6982e..1a651ed6a8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,7 +37,7 @@ SET(PACKAGE_VERSION ${VERSION}) # Version of the dispatch table. This must match the value in # configure.ac. -SET(NC_DISPATCH_VERSION 3) +SET(NC_DISPATCH_VERSION 4) # Get system configuration, Use it to determine osname, os release, cpu. These # will be used when committing to CDash. @@ -1404,6 +1404,9 @@ ENDIF() # Always enable DISKLESS OPTION(ENABLE_DISKLESS "Enable in-memory files" ON) +# Always enable quantization. +OPTION(ENABLE_QUANTIZE "Enable variable quantization" ON) + # By default, MSVC has a stack size of 1000000. # Allow a user to override this. IF(MSVC) @@ -2180,6 +2183,7 @@ is_enabled(ENABLE_NCZARR HAS_NCZARR) is_enabled(ENABLE_NCZARR_S3_TESTS DO_NCZARR_S3_TESTS) is_enabled(ENABLE_MULTIFILTERS HAS_MULTIFILTERS) is_enabled(ENABLE_NCZARR_ZIP DO_NCZARR_ZIP_TESTS) +is_enabled(ENABLE_QUANTIZE HAS_QUANTIZE) # Generate file from template. CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/libnetcdf.settings.in" diff --git a/configure.ac b/configure.ac index a3e88630e0..60099c74e9 100644 --- a/configure.ac +++ b/configure.ac @@ -1649,6 +1649,7 @@ AC_SUBST(HAS_NCZARR,[$enable_nczarr]) AC_SUBST(DO_NCZARR_S3_TESTS,[$enable_nczarr_s3_tests]) AC_SUBST(HAS_MULTIFILTERS,[$has_multifilters]) AC_SUBST(DO_NCZARR_ZIP_TESTS,[$enable_nczarr_zip]) +AC_SUBST([HAS_QUANTIZE],[yes]) # Include some specifics for netcdf on windows. #AH_VERBATIM([_WIN32_STRICMP], @@ -1728,7 +1729,7 @@ AX_SET_META([NC_HAS_MULTIFILTERS],[$has_multifilters],[yes]) # dispatch table to submit. If this is changed, make sure the value in # CMakeLists.txt also changes to match. -AC_SUBST([NC_DISPATCH_VERSION], [3]) +AC_SUBST([NC_DISPATCH_VERSION], [4]) AC_DEFINE_UNQUOTED([NC_DISPATCH_VERSION], [${NC_DISPATCH_VERSION}], [Dispatch table version.]) ##### diff --git a/include/hdf5dispatch.h b/include/hdf5dispatch.h index 1e57eb1e90..cb41cb9fa1 100644 --- a/include/hdf5dispatch.h +++ b/include/hdf5dispatch.h @@ -47,7 +47,8 @@ extern "C" { int *shufflep, int *deflatep, int *deflate_levelp, int *fletcher32p, int *contiguousp, size_t *chunksizesp, int *no_fill, void *fill_valuep, int *endiannessp, - unsigned int *idp, size_t *nparamsp, unsigned int *params); + unsigned int *idp, size_t *nparamsp, unsigned int *params, + int *quantize_modep, int *nsdp); EXTERNL int NC4_HDF5_set_var_chunk_cache(int ncid, int varid, size_t size, size_t nelems, diff --git a/include/hdf5internal.h b/include/hdf5internal.h index cbb8526356..c35c1173a7 100644 --- a/include/hdf5internal.h +++ b/include/hdf5internal.h @@ -200,6 +200,10 @@ int NC4_hdf5_addfilter(NC_VAR_INFO_T* var, unsigned int id, size_t nparams, cons int NC4_hdf5_filter_freelist(NC_VAR_INFO_T* var); int NC4_hdf5_find_missing_filter(NC_VAR_INFO_T* var, unsigned int* idp); +/* Add an attribute to the attribute list. */ +int nc4_put_att(NC_GRP_INFO_T* grp, int varid, const char *name, nc_type file_type, + size_t len, const void *data, nc_type mem_type, int force); + /* Support functions for provenance info (defined in nc4hdf.c) */ extern int NC4_hdf5get_libversion(unsigned*,unsigned*,unsigned*);/*libsrc4/nc4hdf.c*/ extern int NC4_hdf5get_superblock(struct NC_FILE_INFO*, int*);/*libsrc4/nc4hdf.c*/ diff --git a/include/nc4dispatch.h b/include/nc4dispatch.h index 16d9ebd78d..04bd4ca267 100644 --- a/include/nc4dispatch.h +++ b/include/nc4dispatch.h @@ -101,7 +101,8 @@ extern "C" { int *shufflep, int *deflatep, int *deflate_levelp, int *fletcher32p, int *contiguousp, size_t *chunksizesp, int *no_fill, void *fill_valuep, int *endiannessp, - unsigned int* idp, size_t* nparamsp, unsigned int* params); + unsigned int* idp, size_t* nparamsp, unsigned int* params, + int *quantize_modep, int *nsdp); EXTERNL int NC4_inq_varid(int ncid, const char *name, int *varidp); @@ -251,6 +252,13 @@ extern "C" { EXTERNL int NC4_inq_var_filter_info(int ncid, int varid, unsigned int id, size_t* nparams, unsigned int* params); + EXTERNL int + NC4_def_var_quantize(int ncid, int varid, int quantize_mode, int nsd); + + EXTERNL int + NC4_inq_var_quantize(int ncid, int varid, int *quantize_modep, int *nsdp); + + #if defined(__cplusplus) } #endif diff --git a/include/nc4internal.h b/include/nc4internal.h index 49bda44f1c..e2686c0e92 100644 --- a/include/nc4internal.h +++ b/include/nc4internal.h @@ -204,6 +204,8 @@ typedef struct NC_VAR_INFO size_t chunk_cache_size; /**< Size in bytes of the var chunk chache. */ size_t chunk_cache_nelems; /**< Number of slots in var chunk cache. */ float chunk_cache_preemption; /**< Chunk cache preemtion policy. */ + int quantize_mode; /**< Quantize mode. NC_NOQUANTIZE is 0, and means no quantization. */ + int nsd; /**< Number of significant digits if quantization is used, 0 if not. */ void *format_var_info; /**< Pointer to any binary format info. */ void* filters; /**< Record of the list of filters to be applied to var data; format dependent */ } NC_VAR_INFO_T; diff --git a/include/ncdispatch.h b/include/ncdispatch.h index 2626036738..8ce09a8d06 100644 --- a/include/ncdispatch.h +++ b/include/ncdispatch.h @@ -249,7 +249,8 @@ NCDISPATCH_inq_var_all(int ncid, int varid, char *name, nc_type *xtypep, int *shufflep, int *deflatep, int *deflate_levelp, int *fletcher32p, int *contiguousp, size_t *chunksizesp, int *no_fill, void *fill_valuep, int *endiannessp, - unsigned int* idp, size_t* nparamsp, unsigned int* paramsp + unsigned int* idp, size_t* nparamsp, unsigned int* paramsp, + int *quantize_modep, int *nsdp ); EXTERNL int NCDISPATCH_get_att(int ncid, int varid, const char* name, void* value, nc_type t); diff --git a/include/netcdf.h b/include/netcdf.h index 3af038e3ed..c3ff2f5d0f 100644 --- a/include/netcdf.h +++ b/include/netcdf.h @@ -324,6 +324,13 @@ there. */ #define NC_MIN_DEFLATE_LEVEL 0 /**< Minimum deflate level. */ #define NC_MAX_DEFLATE_LEVEL 9 /**< Maximum deflate level. */ +#define NC_NOQUANTIZE 0 /**< No quantization in use. */ +#define NC_QUANTIZE_BITGROOM 1 /**< Use bitgroom quantization. */ + +/** When quantization is used for a variable, an attribute of this + * name is added. */ +#define NC_QUANTIZE_ATT_NAME "number_of_significant_digits" + /** The netcdf version 3 functions all return integer error status. * These are the possible values, in addition to certain values from * the system errno.h. @@ -854,6 +861,16 @@ nc_get_varm(int ncid, int varid, const size_t *startp, /* Extra netcdf-4 stuff. */ +/* Set quantization settings for a variable. Quantizing data improves + * later compression. Must be called after nc_def_var and before + * nc_enddef. */ +EXTERNL int +nc_def_var_quantize(int ncid, int varid, int quantize_mode, int nsd); + +/* Find out quantization settings of a var. */ +EXTERNL int +nc_inq_var_quantize(int ncid, int varid, int *quantize_modep, int *nsdp); + /* Set compression settings for a variable. Lower is faster, higher is * better. Must be called after nc_def_var and before nc_enddef. */ EXTERNL int diff --git a/include/netcdf_dispatch.h.in b/include/netcdf_dispatch.h.in index 9b90cc3424..4f9f59b729 100644 --- a/include/netcdf_dispatch.h.in +++ b/include/netcdf_dispatch.h.in @@ -93,7 +93,7 @@ struct NC_Dispatch int *fletcher32p, int *contiguousp, size_t *chunksizesp, int *no_fill, void *fill_valuep, int *endiannessp, unsigned int *idp, size_t *nparamsp, - unsigned int *params); + unsigned int *params, int *quantize_modep, int *nsdp); int (*var_par_access)(int, int, int); int (*def_var_fill)(int, int, int, const void *); @@ -147,6 +147,7 @@ struct NC_Dispatch /* Version 3 Replace filteractions with more specific functions */ int (*inq_var_filter_ids)(int ncid, int varid, size_t* nfilters, unsigned int* filterids); int (*inq_var_filter_info)(int ncid, int varid, unsigned int id, size_t* nparams, unsigned int* params); + int (*def_var_quantize)(int ncid, int varid, int quantize_mode, int nsd); }; #if defined(__cplusplus) @@ -223,6 +224,7 @@ extern "C" { EXTERNL int NC_NOTNC4_inq_typeids(int, int *, int *); EXTERNL int NC_NOTNC4_inq_user_type(int, nc_type, char *, size_t *, nc_type *, size_t *, int *); + EXTERNL int NC_NOTNC4_def_var_quantize(int, int, int, int); /* These functions are for dispatch layers that don't implement * the enhanced model, but want to succeed anyway. diff --git a/include/netcdf_meta.h.in b/include/netcdf_meta.h.in index 340d53f74b..ce243c5d4c 100644 --- a/include/netcdf_meta.h.in +++ b/include/netcdf_meta.h.in @@ -62,5 +62,6 @@ #define NC_HAS_PAR_FILTERS @NC_HAS_PAR_FILTERS@ /* Parallel I/O with filter support. */ #define NC_HAS_NCZARR @NC_HAS_NCZARR@ #define NC_HAS_MULTIFILTERS @NC_HAS_MULTIFILTERS@ +#define NC_HAS_QUANTIZE @NC_HAS_QUANTIZE@ #endif diff --git a/libdap2/ncd2dispatch.c b/libdap2/ncd2dispatch.c index d34ee4fa46..ffd506d64f 100644 --- a/libdap2/ncd2dispatch.c +++ b/libdap2/ncd2dispatch.c @@ -178,6 +178,7 @@ NCD2_get_var_chunk_cache, NC_NOOP_inq_var_filter_ids, NC_NOOP_inq_var_filter_info, +NC_NOTNC4_def_var_quantize, }; const NC_Dispatch* NCD2_dispatch_table = NULL; /* moved here from ddispatch.c */ @@ -2441,8 +2442,9 @@ NCD2_inq_var_all(int ncid, int varid, char *name, nc_type* xtypep, int* shufflep, int* deflatep, int* deflate_levelp, int* fletcher32p, int* contiguousp, size_t* chunksizesp, int* no_fill, void* fill_valuep, int* endiannessp, - unsigned int* idp, size_t* nparamsp, unsigned int* params - ) + unsigned int* idp, size_t* nparamsp, unsigned int* params, + int *quantize_modep, int *nsdp + ) { NC* drno; int ret; @@ -2452,7 +2454,7 @@ NCD2_inq_var_all(int ncid, int varid, char *name, nc_type* xtypep, shufflep, deflatep, deflate_levelp, fletcher32p, contiguousp, chunksizesp, no_fill, fill_valuep, endiannessp, - idp,nparamsp,params + idp,nparamsp,params,quantize_modep, nsdp ); return THROW(ret); } diff --git a/libdap2/ncd2dispatch.h b/libdap2/ncd2dispatch.h index 03bb10140a..dd1c01748b 100644 --- a/libdap2/ncd2dispatch.h +++ b/libdap2/ncd2dispatch.h @@ -124,21 +124,23 @@ NCD2_def_var(int ncid, const char *name, extern int NCD2_inq_var_all(int ncid, int varid, char *name, nc_type *xtypep, - int *ndimsp, int *dimidsp, int *nattsp, - int *shufflep, int *deflatep, int *deflate_levelp, - int *fletcher32p, int *contiguousp, size_t *chunksizesp, - int *no_fill, void *fill_valuep, int *endiannessp, - unsigned int* idp, size_t* nparamsp, unsigned int* params - ); + int *ndimsp, int *dimidsp, int *nattsp, + int *shufflep, int *deflatep, int *deflate_levelp, + int *fletcher32p, int *contiguousp, size_t *chunksizesp, + int *no_fill, void *fill_valuep, int *endiannessp, + unsigned int* idp, size_t* nparamsp, unsigned int* params, + int *quantize_modep, int *nsdp + ); extern int NC3_inq_var_all(int ncid, int varid, char *name, nc_type *xtypep, - int *ndimsp, int *dimidsp, int *nattsp, - int *shufflep, int *deflatep, int *deflate_levelp, - int *fletcher32p, int *contiguousp, size_t *chunksizesp, - int *no_fill, void *fill_valuep, int *endiannessp, - unsigned int* idp, size_t* nparamsp, unsigned int* params - ); + int *ndimsp, int *dimidsp, int *nattsp, + int *shufflep, int *deflatep, int *deflate_levelp, + int *fletcher32p, int *contiguousp, size_t *chunksizesp, + int *no_fill, void *fill_valuep, int *endiannessp, + unsigned int* idp, size_t* nparamsp, unsigned int* params, + int *quantize_modep, int *nsdp + ); extern int NCD2_inq_varid(int ncid, const char *name, int *varidp); diff --git a/libdap4/ncd4dispatch.c b/libdap4/ncd4dispatch.c index 3f887a7e76..a0b3db69c8 100644 --- a/libdap4/ncd4dispatch.c +++ b/libdap4/ncd4dispatch.c @@ -456,8 +456,9 @@ NCD4_inq_var_all(int ncid, int varid, char *name, nc_type* xtypep, int* shufflep, int* deflatep, int* deflate_levelp, int* fletcher32p, int* contiguousp, size_t* chunksizesp, int* no_fill, void* fill_valuep, int* endiannessp, - unsigned int* idp, size_t* nparamsp, unsigned int* params - ) + unsigned int* idp, size_t* nparamsp, unsigned int* params, + int *quantize_modep, int *nsdp + ) { NC* ncp; int ret; @@ -469,7 +470,7 @@ NCD4_inq_var_all(int ncid, int varid, char *name, nc_type* xtypep, shufflep, deflatep, deflate_levelp, fletcher32p, contiguousp, chunksizesp, no_fill, fill_valuep, endiannessp, - idp, nparamsp, params); + idp, nparamsp, params, quantize_modep, nsdp); return (ret); } @@ -974,4 +975,5 @@ NCD4_get_var_chunk_cache, NC_NOTNC4_inq_var_filter_ids, NC_NOTNC4_inq_var_filter_info, +NC_NOTNC4_def_var_quantize, }; diff --git a/libdispatch/dinternal.c b/libdispatch/dinternal.c index c94e2f3ccc..b4084c5b21 100644 --- a/libdispatch/dinternal.c +++ b/libdispatch/dinternal.c @@ -16,12 +16,12 @@ in libdap2. int NCDISPATCH_inq_var_all(int ncid, int varid, char *name, nc_type *xtypep, - int *ndimsp, int *dimidsp, int *nattsp, - int *shufflep, int *deflatep, int *deflate_levelp, - int *fletcher32p, int *contiguousp, size_t *chunksizesp, - int *no_fill, void *fill_valuep, int *endiannessp, - unsigned int* idp, size_t* nparamsp, unsigned int* params - ) + int *ndimsp, int *dimidsp, int *nattsp, + int *shufflep, int *deflatep, int *deflate_levelp, + int *fletcher32p, int *contiguousp, size_t *chunksizesp, + int *no_fill, void *fill_valuep, int *endiannessp, + unsigned int* idp, size_t* nparamsp, unsigned int* params, + int *quantize_modep, int *nsdp) { NC* ncp; int stat = NC_check_id(ncid,&ncp); @@ -33,7 +33,7 @@ NCDISPATCH_inq_var_all(int ncid, int varid, char *name, nc_type *xtypep, contiguousp, chunksizesp, no_fill, fill_valuep, endiannessp, - idp, nparamsp, params); + idp, nparamsp, params, quantize_modep, nsdp); } int diff --git a/libdispatch/dnotnc4.c b/libdispatch/dnotnc4.c index 2adba6e11e..4deed435bc 100644 --- a/libdispatch/dnotnc4.c +++ b/libdispatch/dnotnc4.c @@ -19,6 +19,23 @@ #include "ncdispatch.h" #include "nc4internal.h" +/** + * @internal Not implemented in some dispatch tables + * + * @param ncid Ignored. + * @param varid Ignored. + * @param quantize_mode Ignored. + * @param nsd Ignored. + * + * @return ::NC_ENOTNC4 Not implemented for a dispatch table + * @author Ed Hartnett + */ +int +NC_NOTNC4_def_var_quantize(int ncid, int varid, int quantize_mode, int nsd) +{ + return NC_ENOTNC4; +} + /** * @internal Not implemented in some dispatch tables * diff --git a/libdispatch/dvar.c b/libdispatch/dvar.c index 95859d2e38..4dfc903cc4 100644 --- a/libdispatch/dvar.c +++ b/libdispatch/dvar.c @@ -461,6 +461,69 @@ nc_def_var_deflate(int ncid, int varid, int shuffle, int deflate, int deflate_le return ncp->dispatch->def_var_deflate(ncid,varid,shuffle,deflate,deflate_level); } +/** + Turn on quantization for a variable. + + The data data are quantized by setting unneeded bits alternately to + 1/0, so that they may compress well. Quantization is lossy (data + are irretrievably altered), and it improves the compression ratio + provided by a subsequent lossless compression filter. Quantization + alone will not reduce the size of the data - lossless compression + like zlib must also be used (see nc_def_var_deflate()). + + This data quantization used the bitgroom algorithm. A notable + feature of BitGroom is that the data it processes remain in IEEE754 + format after quantization. Therefore the BitGroom algorithm does + nothing when data are read. + + Quantization is only available for variables of type NC_FLOAT or + NC_DOUBLE. Attempts to set quantization for other variable + types return an error (NC_EINVAL). + + Quantization is not applied to values equal to the value of the + _FillValue attribute, if any. + + For more information about quantization and the bitgroom filter, see + + Zender, C. S. (2016), Bit Grooming: Statistically accurate + precision-preserving quantization with compression, evaluated in + the netCDF Operators (NCO, v4.4.8+), Geosci. Model Dev., 9, + 3199-3211, doi:10.5194/gmd-9-3199-2016 Retrieved on Sep 21, 2020 + from + https://www.researchgate.net/publication/301575383_Bit_Grooming_Statistically_accurate_precision-preserving_quantization_with_compression_evaluated_in_the_netCDF_Operators_NCO_v448. + + @param ncid File ID. + @param varid Variable ID. NC_GLOBAL is not a valid varid, and may + not be used. + @param quantize_mode A integer flag specifying the quantization + used. Current NC_QUANTIZE_BITGROOM is the only available setting. + @param nsd Number of significant digits to retain. Allowed single- and + double-precision NSDs are 1-7 and 1-15, respectively. + + @return ::NC_NOERR No error. + @return ::NC_EGLOBAL Can't use ::NC_GLOBAL with this function. + @return ::NC_EBADID Bad ncid. + @return ::NC_ENOTVAR Invalid variable ID. + @return ::NC_ENOTNC4 Attempting netcdf-4 operation on file that is + not netCDF-4/HDF5. + @return ::NC_ESTRICTNC3 Attempting netcdf-4 operation on strict nc3 + netcdf-4 file. + @return ::NC_ELATEDEF Too late to change settings for this variable. + @return ::NC_EINVAL Invalid input + @author Charlie Zender, Ed Hartnett + */ +int +nc_def_var_quantize(int ncid, int varid, int quantize_mode, int nsd) +{ + NC* ncp; + int stat = NC_check_id(ncid,&ncp); + if(stat != NC_NOERR) return stat; + + /* Using NC_GLOBAL is illegal. */ + if (varid == NC_GLOBAL) return NC_EGLOBAL; + return ncp->dispatch->def_var_quantize(ncid,varid,quantize_mode,nsd); +} + /** Set checksum for a var. diff --git a/libdispatch/dvarinq.c b/libdispatch/dvarinq.c index 1684c57dbc..a5900317f3 100644 --- a/libdispatch/dvarinq.c +++ b/libdispatch/dvarinq.c @@ -131,7 +131,7 @@ nc_inq_var(int ncid, int varid, char *name, nc_type *xtypep, return ncp->dispatch->inq_var_all(ncid, varid, name, xtypep, ndimsp, dimidsp, nattsp, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL,NULL,NULL); + NULL, NULL, NULL, NULL, NULL); } /** @@ -350,7 +350,11 @@ nc_inq_var_deflate(int ncid, int varid, int *shufflep, int *deflatep, int *defla NULL, /*nofillp*/ NULL, /*fillvaluep*/ NULL, /*endianp*/ - NULL, NULL, NULL + NULL, /* idp */ + NULL, /* nparamsp */ + NULL, /* params */ + NULL, /* quantize_modep */ + NULL /* nsdp */ ); } @@ -397,7 +401,11 @@ nc_inq_var_fletcher32(int ncid, int varid, int *fletcher32p) NULL, /*nofillp*/ NULL, /*fillvaluep*/ NULL, /*endianp*/ - NULL, NULL, NULL + NULL, /* idp */ + NULL, /* nparamsp */ + NULL, /* params */ + NULL, /* quantize_modep */ + NULL /* nsdp */ ); } @@ -472,7 +480,7 @@ nc_inq_var_chunking(int ncid, int varid, int *storagep, size_t *chunksizesp) return ncp->dispatch->inq_var_all(ncid, varid, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, storagep, chunksizesp, NULL, NULL, NULL, - NULL, NULL, NULL); + NULL, NULL, NULL, NULL, NULL); } /** \ingroup variables @@ -523,10 +531,63 @@ nc_inq_var_fill(int ncid, int varid, int *no_fill, void *fill_valuep) no_fill, /*nofillp*/ fill_valuep, /*fillvaluep*/ NULL, /*endianp*/ - NULL, NULL, NULL + NULL, /* idp */ + NULL, /* nparamsp */ + NULL, /* params */ + NULL, /* quantize_modep */ + NULL /* nsdp */ ); } +/** @ingroup variables + * Learn whether BitGroom quantization is on for a variable, and, if so, + * the NSD setting. + * + * @param ncid File ID. + * @param varid Variable ID. Must not be NC_GLOBAL. + * @param quantize_modep Pointer that gets a 0 if BitGroom is not in + * use for this var, and a 1 if it is. Ignored if NULL. + * @param nsdp Pointer that gets the NSD setting (from 1 to 15), if + * BitGroom is in use. Ignored if NULL. + * + * @return 0 for success, error code otherwise. + * @author Charlie Zender, Ed Hartnett +*/ +int +nc_inq_var_quantize(int ncid, int varid, int *quantize_modep, int *nsdp) +{ + NC* ncp; + int stat = NC_check_id(ncid,&ncp); + + if(stat != NC_NOERR) return stat; + TRACE(nc_inq_var_quantize); + + /* Using NC_GLOBAL is illegal. */ + if (varid == NC_GLOBAL) return NC_EGLOBAL; + + return ncp->dispatch->inq_var_all( + ncid, varid, + NULL, /*name*/ + NULL, /*xtypep*/ + NULL, /*ndimsp*/ + NULL, /*dimidsp*/ + NULL, /*nattsp*/ + NULL, /*shufflep*/ + NULL, /*deflatep*/ + NULL, /*deflatelevelp*/ + NULL, /*fletcher32p*/ + NULL, /*contiguousp*/ + NULL, /*chunksizep*/ + NULL, /*nofillp*/ + NULL, /*fillvaluep*/ + NULL, /*endianp*/ + NULL, /* idp */ + NULL, /* nparamsp */ + NULL, /* params */ + quantize_modep, + nsdp); +} + /** \ingroup variables Find the endianness of a variable. @@ -571,7 +632,11 @@ nc_inq_var_endian(int ncid, int varid, int *endianp) NULL, /*nofillp*/ NULL, /*fillvaluep*/ endianp, /*endianp*/ - NULL, NULL, NULL); + NULL, /* idp */ + NULL, /* nparamsp */ + NULL, /* params */ + NULL, /* quantize_modep */ + NULL); /* nsdp */ } /** diff --git a/libhdf5/hdf5dispatch.c b/libhdf5/hdf5dispatch.c index 60e00ecc62..53878b874d 100644 --- a/libhdf5/hdf5dispatch.c +++ b/libhdf5/hdf5dispatch.c @@ -105,6 +105,7 @@ static const NC_Dispatch HDF5_dispatcher = { NC4_hdf5_inq_var_filter_ids, NC4_hdf5_inq_var_filter_info, + NC4_def_var_quantize }; const NC_Dispatch* HDF5_dispatch_table = NULL; /* moved here from ddispatch.c */ diff --git a/libhdf5/hdf5var.c b/libhdf5/hdf5var.c index 45a218d448..22af7ab3b1 100644 --- a/libhdf5/hdf5var.c +++ b/libhdf5/hdf5var.c @@ -24,6 +24,14 @@ /** Number of bytes in 64 KB. */ #define SIXTY_FOUR_KB (65536) +/** For quantization, the allowed value of number of significant + * digits for float. */ +#define MAX_FLOAT_NSD (14) + +/** For quantization, the allowed value of number of significant + * digits for double. */ +#define MAX_DOUBLE_NSD (14) + #ifdef LOGGING /** * Report the chunksizes selected for a variable. @@ -465,6 +473,8 @@ NC4_def_var(int ncid, const char *name, nc_type xtype, int ndims, * @param no_fill Pointer to no_fill setting. * @param fill_value Pointer to fill value. * @param endianness Pointer to endianness setting. + * @param quantize_mode Pointer to quantization mode. + * @param nsd Pointer to number of significant digits. * * @returns ::NC_NOERR for success * @returns ::NC_EBADID Bad ncid. @@ -484,7 +494,8 @@ static int nc_def_var_extra(int ncid, int varid, int *shuffle, int *unused1, int *unused2, int *fletcher32, int *storage, const size_t *chunksizes, int *no_fill, - const void *fill_value, int *endianness) + const void *fill_value, int *endianness, + int *quantize_mode, int *nsd) { NC_GRP_INFO_T *grp; NC_FILE_INFO_T *h5; @@ -706,6 +717,60 @@ nc_def_var_extra(int ncid, int varid, int *shuffle, int *unused1, var->endianness = *endianness; } + /* Remember quantization settings. They will be used when data are + * written. */ + if (quantize_mode) + { + /* Only two valid mode settings. */ + if (*quantize_mode != NC_NOQUANTIZE && + *quantize_mode != NC_QUANTIZE_BITGROOM) + return NC_EINVAL; + + if (*quantize_mode == NC_QUANTIZE_BITGROOM) + { + /* Only float and double types can have quantization. */ + if (var->type_info->hdr.id != NC_FLOAT && + var->type_info->hdr.id != NC_DOUBLE) + return NC_EINVAL; + + /* For bitgroom, number of significant digits is required. */ + if (!nsd) + return NC_EINVAL; + + /* NSD must be in range. */ + if (*nsd < 0) + return NC_EINVAL; + if (var->type_info->hdr.id != NC_FLOAT && *nsd > MAX_FLOAT_NSD) + return NC_EINVAL; + if (var->type_info->hdr.id != NC_DOUBLE && *nsd > MAX_DOUBLE_NSD) + return NC_EINVAL; + + var->nsd = *nsd; + } + + var->quantize_mode = *quantize_mode; + + /* If nsd was zero, turn quantization off. */ + if (*nsd == 0) + var->quantize_mode = NC_NOQUANTIZE; + + /* If quantization is turned off, then set nsd to 0. */ + if (*quantize_mode == NC_NOQUANTIZE) + var->nsd = 0; + } + + /* Setting nsd to 0 turns off quantization. */ + if (nsd && !quantize_mode) + { + if (*nsd == 0) + { + var->quantize_mode = NC_NOQUANTIZE; + var->nsd = *nsd; + } + else + return NC_EINVAL; + } + return NC_NOERR; } @@ -737,7 +802,8 @@ NC4_def_var_deflate(int ncid, int varid, int shuffle, int deflate, int stat = NC_NOERR; unsigned int level = (unsigned int)deflate_level; /* Set shuffle first */ - if((stat = nc_def_var_extra(ncid, varid, &shuffle, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL))) goto done; + if((stat = nc_def_var_extra(ncid, varid, &shuffle, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL))) goto done; if(deflate) { if((stat = nc_def_var_filter(ncid, varid, H5Z_FILTER_DEFLATE,1,&level))) goto done; } /* else ignore */ @@ -746,6 +812,33 @@ NC4_def_var_deflate(int ncid, int varid, int shuffle, int deflate, return stat; } +/** + * @internal Set quantization settings on a variable. This is + * called by nc_def_var_quantize(). + * + * @param ncid File ID. + * @param varid Variable ID. + * @param quantize_mode Quantization mode. + * @param nsd Number of significant digits. + * + * @returns ::NC_NOERR No error. + * @returns ::NC_EBADID Bad ncid. + * @returns ::NC_ENOTVAR Invalid variable ID. + * @returns ::NC_ENOTNC4 Attempting netcdf-4 operation on file that is + * not netCDF-4/HDF5. + * @returns ::NC_ELATEDEF Too late to change settings for this variable. + * @returns ::NC_ENOTINDEFINE Not in define mode. + * @returns ::NC_EINVAL Invalid input + * @author Ed Hartnett, Dennis Heimbigner + */ +int +NC4_def_var_quantize(int ncid, int varid, int quantize_mode, int nsd) +{ + return nc_def_var_extra(ncid, varid, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, + &quantize_mode, &nsd); +} + #if 0 /** * @internal Remove a filter from filter list for a variable @@ -803,7 +896,7 @@ int NC4_def_var_fletcher32(int ncid, int varid, int fletcher32) { return nc_def_var_extra(ncid, varid, NULL, NULL, NULL, &fletcher32, - NULL, NULL, NULL, NULL, NULL); + NULL, NULL, NULL, NULL, NULL, NULL, NULL); } /** @@ -832,7 +925,7 @@ int NC4_def_var_chunking(int ncid, int varid, int storage, const size_t *chunksizesp) { return nc_def_var_extra(ncid, varid, NULL, NULL, NULL, NULL, - &storage, chunksizesp, NULL, NULL, NULL); + &storage, chunksizesp, NULL, NULL, NULL, NULL, NULL); } /** @@ -877,7 +970,7 @@ nc_def_var_chunking_ints(int ncid, int varid, int storage, int *chunksizesp) cs[i] = chunksizesp[i]; retval = nc_def_var_extra(ncid, varid, NULL, NULL, NULL, NULL, - &storage, cs, NULL, NULL, NULL); + &storage, cs, NULL, NULL, NULL, NULL, NULL); if (var->ndims) free(cs); @@ -911,7 +1004,7 @@ int NC4_def_var_fill(int ncid, int varid, int no_fill, const void *fill_value) { return nc_def_var_extra(ncid, varid, NULL, NULL, NULL, NULL, NULL, - NULL, &no_fill, fill_value, NULL); + NULL, &no_fill, fill_value, NULL, NULL, NULL); } /** @@ -940,7 +1033,7 @@ int NC4_def_var_endian(int ncid, int varid, int endianness) { return nc_def_var_extra(ncid, varid, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, &endianness); + NULL, NULL, NULL, &endianness, NULL, NULL); } /** @@ -2053,6 +2146,8 @@ NC4_get_vars(int ncid, int varid, const size_t *startp, const size_t *countp, * @param nparamsp Pointer to memory to store filter parameter count. * @param params Pointer to vector of unsigned integers into which * to store filter parameters. + * @param quantize_modep Gets quantization mode. + * @param nsdp Gets number of significant digits, if quantization is in use. * * @returns ::NC_NOERR No error. * @returns ::NC_EBADID Bad ncid. @@ -2067,7 +2162,8 @@ NC4_HDF5_inq_var_all(int ncid, int varid, char *name, nc_type *xtypep, int *shufflep, int *unused4, int *unused5, int *fletcher32p, int *storagep, size_t *chunksizesp, int *no_fill, void *fill_valuep, int *endiannessp, - unsigned int *unused1, size_t *unused2, unsigned int *unused3) + unsigned int *unused1, size_t *unused2, unsigned int *unused3, + int *quantize_modep, int *nsdp) { NC_FILE_INFO_T *h5; NC_GRP_INFO_T *grp; @@ -2088,7 +2184,49 @@ NC4_HDF5_inq_var_all(int ncid, int varid, char *name, nc_type *xtypep, return NC4_inq_var_all(ncid, varid, name, xtypep, ndimsp, dimidsp, nattsp, shufflep, unused4, unused5, fletcher32p, storagep, chunksizesp, no_fill, fill_valuep, - endiannessp, unused1, unused2, unused3); + endiannessp, unused1, unused2, unused3, + quantize_modep, nsdp); +} + +/** + * @internal Get quantization information about a variable. Pass NULL + * for whatever you don't care about. + * + * @param ncid File ID. + * @param varid Variable ID. + * + * @returns ::NC_NOERR No error. + * @returns ::NC_EBADID Bad ncid. + * @returns ::NC_ENOTVAR Bad varid. + * @returns ::NC_ENOMEM Out of memory. + * @returns ::NC_EINVAL Invalid input. + * @author Ed Hartnett + */ +int +NC4_HDF5_inq_var_quantize(int ncid, int varid, int *quantize_modep, + int *nsdp) +{ + NC_FILE_INFO_T *h5; + NC_GRP_INFO_T *grp; + NC_VAR_INFO_T *var = NULL; + int retval; + + LOG((2, "%s: ncid 0x%x varid %d", __func__, ncid, varid)); + + /* Find the file, group, and var info, and do lazy att read if + * needed. */ + if ((retval = nc4_hdf5_find_grp_var_att(ncid, varid, NULL, 0, 0, NULL, + &h5, &grp, &var, NULL))) + return retval; + assert(grp && h5); + + /* Now that lazy atts have been read, use the libsrc4 function to + * get the answers. */ + return NC4_inq_var_all(ncid, varid, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + quantize_modep, nsdp); } /** diff --git a/libhdf5/nc4hdf.c b/libhdf5/nc4hdf.c index 7096023dc1..ebde9cbc6c 100644 --- a/libhdf5/nc4hdf.c +++ b/libhdf5/nc4hdf.c @@ -1016,9 +1016,17 @@ var_create_dataset(NC_GRP_INFO_T *grp, NC_VAR_INFO_T *var, nc_bool_t write_dimid BAIL(retval); } + /* If quantization is in use, write an attribute indicating it, a + * single integer which is the number of significant digits. */ + if ((retval = nc4_put_att(var->container, var->hdr.id, NC_QUANTIZE_ATT_NAME, NC_INT, 1, + &var->nsd, NC_INT, 0))) + BAIL(retval); + /* Write attributes for this var. */ if ((retval = write_attlist(var->att, var->hdr.id, grp))) BAIL(retval); + + /* The file is now up-to-date with all settings for this var. */ var->attr_dirty = NC_FALSE; exit: diff --git a/libnczarr/zdispatch.c b/libnczarr/zdispatch.c index 5d1eef491c..bedc01ab08 100644 --- a/libnczarr/zdispatch.c +++ b/libnczarr/zdispatch.c @@ -105,6 +105,7 @@ static const NC_Dispatch NCZ_dispatcher = { NC4_get_var_chunk_cache, NCZ_inq_var_filter_ids, NCZ_inq_var_filter_info, + NC_NOTNC4_def_var_quantize, }; const NC_Dispatch* NCZ_dispatch_table = NULL; /* moved here from ddispatch.c */ diff --git a/libnczarr/zdispatch.h b/libnczarr/zdispatch.h index 346a0d3f7c..ab8c8c4cf2 100644 --- a/libnczarr/zdispatch.h +++ b/libnczarr/zdispatch.h @@ -82,7 +82,7 @@ EXTERNL int NCZ_put_att(int ncid, int varid, const char *name, nc_type file_type EXTERNL int NCZ_def_var(int ncid, const char *name, nc_type xtype, int ndims, const int *dimidsp, int *varidp); -EXTERNL int NCZ_inq_var_all(int ncid, int varid, char *name, nc_type *xtypep, int *ndimsp, int *dimidsp, int *nattsp, int *shufflep, int *deflatep, int *deflate_levelp, int *fletcher32p, int *contiguousp, size_t *chunksizesp, int *no_fill, void *fill_valuep, int *endiannessp, unsigned int* idp, size_t* nparamsp, unsigned int* params +EXTERNL int NCZ_inq_var_all(int ncid, int varid, char *name, nc_type *xtypep, int *ndimsp, int *dimidsp, int *nattsp, int *shufflep, int *deflatep, int *deflate_levelp, int *fletcher32p, int *contiguousp, size_t *chunksizesp, int *no_fill, void *fill_valuep, int *endiannessp, unsigned int* idp, size_t* nparamsp, unsigned int* params, int *quantize_modep, int *nsdp ); EXTERNL int NCZ_inq_varid(int ncid, const char *name, int *varidp); diff --git a/libnczarr/zvar.c b/libnczarr/zvar.c index 7ba84f9683..83f2a777c5 100644 --- a/libnczarr/zvar.c +++ b/libnczarr/zvar.c @@ -1984,7 +1984,8 @@ NCZ_inq_var_all(int ncid, int varid, char *name, nc_type *xtypep, int *shufflep, int *unused4, int *unused5, int *fletcher32p, int *storagep, size_t *chunksizesp, int *no_fill, void *fill_valuep, int *endiannessp, - unsigned int *unused1, size_t *unused2, unsigned int *unused3) + unsigned int *unused1, size_t *unused2, unsigned int *unused3, + int *quantize_modep, int *nsdp) { NC_FILE_INFO_T *h5; NC_GRP_INFO_T *grp; @@ -2007,7 +2008,7 @@ NCZ_inq_var_all(int ncid, int varid, char *name, nc_type *xtypep, retval = NC4_inq_var_all(ncid, varid, name, xtypep, ndimsp, dimidsp, nattsp, shufflep, unused4, unused5, fletcher32p, storagep, chunksizesp, no_fill, fill_valuep, - endiannessp, unused1, unused2, unused3); + endiannessp, unused1, unused2, unused3, quantize_modep, nsdp); done: return ZUNTRACEX(retval,"xtype=%d natts=%d shuffle=%d fletcher32=%d no_fill=%d endianness=%d ndims=%d dimids=%s storage=%d chunksizes=%s", (xtypep?*xtypep:-1), diff --git a/libnetcdf.settings.in b/libnetcdf.settings.in index 0cc38514e1..d3cf633337 100644 --- a/libnetcdf.settings.in +++ b/libnetcdf.settings.in @@ -45,3 +45,5 @@ SZIP Write Support: @HAS_SZLIB_WRITE@ Parallel Filters: @HAS_PAR_FILTERS@ NCZarr Support: @HAS_NCZARR@ Multi-Filter Support: @HAS_MULTIFILTERS@ +Quantization: @HAS_QUANTIZE@ + diff --git a/libsrc/nc3dispatch.c b/libsrc/nc3dispatch.c index 8a6715d72a..f1238fcd0d 100644 --- a/libsrc/nc3dispatch.c +++ b/libsrc/nc3dispatch.c @@ -31,12 +31,13 @@ static int NC3_inq_var_all(int ncid, int varid, char *name, nc_type *xtypep, - int *ndimsp, int *dimidsp, int *nattsp, - int *shufflep, int *deflatep, int *deflate_levelp, - int *fletcher32p, int *contiguousp, size_t *chunksizesp, - int *no_fill, void *fill_valuep, int *endiannessp, - unsigned int* idp, size_t* nparamsp, unsigned int* params - ); + int *ndimsp, int *dimidsp, int *nattsp, + int *shufflep, int *deflatep, int *deflate_levelp, + int *fletcher32p, int *contiguousp, size_t *chunksizesp, + int *no_fill, void *fill_valuep, int *endiannessp, + unsigned int* idp, size_t* nparamsp, unsigned int* params, + int *quantize_modep, int *nsdp + ); static int NC3_var_par_access(int,int,int); @@ -166,6 +167,8 @@ NC3_get_var_chunk_cache, NC_NOOP_inq_var_filter_ids, NC_NOOP_inq_var_filter_info, + +NC_NOTNC4_def_var_quantize }; const NC_Dispatch* NC3_dispatch_table = NULL; /*!< NC3 Dispatch table, moved here from ddispatch.c */ @@ -185,12 +188,13 @@ NC3_finalize(void) static int NC3_inq_var_all(int ncid, int varid, char *name, nc_type *xtypep, - int *ndimsp, int *dimidsp, int *nattsp, - int *shufflep, int *deflatep, int *deflate_levelp, - int *fletcher32p, int *contiguousp, size_t *chunksizesp, - int *no_fill, void *fill_valuep, int *endiannessp, - unsigned int* idp, size_t* nparamsp, unsigned int* params - ) + int *ndimsp, int *dimidsp, int *nattsp, + int *shufflep, int *deflatep, int *deflate_levelp, + int *fletcher32p, int *contiguousp, size_t *chunksizesp, + int *no_fill, void *fill_valuep, int *endiannessp, + unsigned int* idp, size_t* nparamsp, unsigned int* params, + int *quantize_modep, int *nsdp + ) { int stat = NC3_inq_var(ncid,varid,name,xtypep,ndimsp,dimidsp,nattsp,no_fill,fill_valuep); if(stat) return stat; @@ -202,6 +206,8 @@ NC3_inq_var_all(int ncid, int varid, char *name, nc_type *xtypep, if(idp) return NC_ENOTNC4; if(nparamsp) return NC_ENOTNC4; if(params) return NC_ENOTNC4; + if(quantize_modep) return NC_ENOTNC4; + if(nsdp) return NC_ENOTNC4; return NC_NOERR; } diff --git a/libsrc4/nc4var.c b/libsrc4/nc4var.c index 559e84deab..4c938ff95f 100644 --- a/libsrc4/nc4var.c +++ b/libsrc4/nc4var.c @@ -136,6 +136,9 @@ nc_get_var_chunk_cache_ints(int ncid, int varid, int *sizep, * @param nparamsp Pointer to memory to store filter parameter count. * @param params Pointer to vector of unsigned integers into which * to store filter parameters. + * @param quantize_modep Pointer that gets the quantize mode. + * @param nsdp Pointer that gets the number of significant digits for + * quantization. * * @returns ::NC_NOERR No error. * @returns ::NC_EBADID Bad ncid. @@ -150,7 +153,8 @@ NC4_inq_var_all(int ncid, int varid, char *name, nc_type *xtypep, int *shufflep, int *deflatep, int *deflate_levelp, int *fletcher32p, int *storagep, size_t *chunksizesp, int *no_fill, void *fill_valuep, int *endiannessp, - unsigned int *idp, size_t *nparamsp, unsigned int *params) + unsigned int *idp, size_t *nparamsp, unsigned int *params, + int *quantize_modep, int *nsdp) { NC_GRP_INFO_T *grp; NC_FILE_INFO_T *h5; @@ -269,6 +273,12 @@ NC4_inq_var_all(int ncid, int varid, char *name, nc_type *xtypep, if (endiannessp) *endiannessp = var->endianness; + /* Does the user want to know about quantization? */ + if (quantize_modep) + *quantize_modep = var->quantize_mode; + if (nsdp) + *nsdp = var->nsd; + return NC_NOERR; } @@ -308,7 +318,7 @@ nc_inq_var_chunking_ints(int ncid, int varid, int *storagep, int *chunksizesp) /* Call the netcdf-4 version directly. */ retval = NC4_inq_var_all(ncid, varid, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, storagep, cs, NULL, - NULL, NULL, NULL, NULL, NULL); + NULL, NULL, NULL, NULL, NULL, NULL, NULL); /* Copy from size_t array. */ if (!retval && chunksizesp && var->storage == NC_CHUNKED) diff --git a/libsrcp/ncpdispatch.c b/libsrcp/ncpdispatch.c index d2c344b499..294bfc1f0d 100644 --- a/libsrcp/ncpdispatch.c +++ b/libsrcp/ncpdispatch.c @@ -1463,6 +1463,8 @@ NC_NOTNC4_get_var_chunk_cache, NC_NOOP_inq_var_filter_ids, NC_NOOP_inq_var_filter_info, + +NC_NOTNC4_def_var_quantize, }; const NC_Dispatch *NCP_dispatch_table = NULL; /* moved here from ddispatch.c */ diff --git a/nc_test4/CMakeLists.txt b/nc_test4/CMakeLists.txt index c5c345005b..74dc93cf47 100644 --- a/nc_test4/CMakeLists.txt +++ b/nc_test4/CMakeLists.txt @@ -20,7 +20,7 @@ SET(NC4_TESTS tst_dims tst_dims2 tst_dims3 tst_files tst_files4 tst_files6 tst_sync tst_h_strbug tst_h_refs tst_h_scalar tst_rename tst_rename2 tst_rename3 tst_h5_endians tst_atts_string_rewrite tst_put_vars_two_unlim_dim tst_hdf5_file_compat tst_fill_attr_vanish tst_rehash tst_types tst_bug324 - tst_atts3 tst_put_vars tst_elatefill tst_udf tst_bug1442) + tst_atts3 tst_put_vars tst_elatefill tst_udf tst_bug1442 tst_quantize) # Note, renamegroup needs to be compiled before run_grp_rename diff --git a/nc_test4/Makefile.am b/nc_test4/Makefile.am index c356d50f08..3fb3bae8fc 100644 --- a/nc_test4/Makefile.am +++ b/nc_test4/Makefile.am @@ -35,7 +35,7 @@ tst_h_scalar tst_rename tst_rename2 tst_rename3 tst_h5_endians \ tst_atts_string_rewrite tst_hdf5_file_compat tst_fill_attr_vanish \ tst_rehash tst_filterparser tst_bug324 tst_types tst_atts3 \ tst_put_vars tst_elatefill tst_udf tst_put_vars_two_unlim_dim \ -tst_bug1442 +tst_bug1442 tst_quantize # Temporary I hoped, but hoped in vain. if !ISCYGWIN diff --git a/nc_test4/tst_quantize.c b/nc_test4/tst_quantize.c new file mode 100644 index 0000000000..fd9e4fd4ed --- /dev/null +++ b/nc_test4/tst_quantize.c @@ -0,0 +1,68 @@ +/* This is part of the netCDF package. + Copyright 2021 University Corporation for Atmospheric Research/Unidata + See COPYRIGHT file for conditions of use. + + Test quantization of netcdf-4 variables. Quantization is the + zeroing-out of bits in float or double data beyond a desired + precision. + + Ed Hartnett, 8/19/21 +*/ + +#include +#include "err_macros.h" +#include "netcdf.h" + +#define FILE_NAME "tst_quantize.nc" +#define NDIMS1 1 +#define DIM_NAME_1 "meters_along_canal" +#define DIM_LEN_1 10 +#define VAR_NAME_1 "Amsterdam_houseboat_location" +#define NSD_1 3 + +int +main(int argc, char **argv) +{ + printf("\n*** Testing netcdf-4 variable quantization functions.\n"); + printf("**** testing simple quantization and error conditions..."); + { + int ncid, dimid, varid; + int quantize_mode_in, nsd_in; + + /* Create a netcdf classic file with one var. Attempt + * quantization. It will not work. */ + if (nc_create(FILE_NAME, NC_CLOBBER, &ncid)) ERR; + if (nc_def_dim(ncid, DIM_NAME_1, DIM_LEN_1, &dimid)) ERR; + if (nc_def_var(ncid, VAR_NAME_1, NC_FLOAT, NDIMS1, &dimid, &varid)) ERR; + if (nc_def_var_quantize(ncid, varid, NC_QUANTIZE_BITGROOM, NSD_1) != NC_ENOTNC4) ERR; + if (nc_inq_var_quantize(ncid, varid, &quantize_mode_in, &nsd_in) != NC_ENOTNC4) ERR; + if (nc_close(ncid)) ERR; + + /* Create a netcdf-4 file with one var. Attempt + * quantization. It will work, eventually... */ + if (nc_create(FILE_NAME, NC_NETCDF4|NC_CLOBBER, &ncid)) ERR; + if (nc_def_dim(ncid, DIM_NAME_1, DIM_LEN_1, &dimid)) ERR; + if (nc_def_var(ncid, VAR_NAME_1, NC_FLOAT, NDIMS1, &dimid, &varid)) ERR; + + /* Bad varid. */ + if (nc_def_var_quantize(ncid, NC_GLOBAL, NC_QUANTIZE_BITGROOM, NSD_1) != NC_EGLOBAL) ERR; + if (nc_def_var_quantize(ncid, 1, NC_QUANTIZE_BITGROOM, NSD_1) != NC_ENOTVAR) ERR; + + /* This will work. */ + if (nc_def_var_quantize(ncid, varid, NC_QUANTIZE_BITGROOM, NSD_1)) ERR; + if (nc_inq_var_quantize(ncid, varid, &quantize_mode_in, &nsd_in)) ERR; + if (quantize_mode_in != NC_QUANTIZE_BITGROOM) ERR; + if (nsd_in != NSD_1) ERR; + if (nc_close(ncid)) ERR; + + /* Open the file and check. */ + if (nc_open(FILE_NAME, NC_WRITE, &ncid)) ERR; + if (nc_inq_var_quantize(ncid, 0, &quantize_mode_in, &nsd_in)) ERR; + printf("quantize_mode_in %d nsd_in %d\n", quantize_mode_in, nsd_in); + /* if (quantize_mode_in != NC_QUANTIZE_BITGROOM) ERR; */ + /* if (nsd_in != NSD_1) ERR; */ + if (nc_close(ncid)) ERR; + } + SUMMARIZE_ERR; + FINAL_RESULTS; +} diff --git a/nc_test4/tst_udf.c b/nc_test4/tst_udf.c index b4e390e84d..522fa4c816 100644 --- a/nc_test4/tst_udf.c +++ b/nc_test4/tst_udf.c @@ -156,6 +156,9 @@ static NC_Dispatch tst_dispatcher = { NC_NOOP_inq_var_filter_ids, NC_NOOP_inq_var_filter_info, #endif +#if NC_DISPATCH_VERSION >= 4 + NC_NOTNC4_def_var_quantize, +#endif }; /* This is the dispatch object that holds pointers to all the