int /* [enm] Return status */ nco_get_dmn_info (int nc_id, int var_id, char dmn_nm[], int *dimid, long *dmn_sz) { /* Purpose: Get dimension information associated with specified variable In our case, this is lat or lon---they are presumed to be identical */ int rcd=NC_NOERR; nc_type var_typ; /* variable type */ int var_dimid[NC_MAX_VAR_DIMS]; /* dimension ids */ int var_att_nbr; /* number of attributes */ int var_dmn_nbr; /* number of dims */ /* Get dimension information */ rcd=nco_inq_var(nc_id,var_id,0,&var_typ,&var_dmn_nbr,var_dimid,&var_att_nbr); if(rcd == NC_NOERR){ *dimid=var_dimid[0]; rcd=nco_inq_dimlen(nc_id,var_dimid[0],dmn_sz); rcd=nco_inq_dimname(nc_id,var_dimid[0],dmn_nm); } /* endif */ if(rcd != NC_NOERR) nco_err_exit(rcd,"nco_get_dmn_info() unable to get dimension information"); return rcd; } /* end nco_get_dmn_info() */
nm_id_sct * /* O [sct] List of dimensions associated with input variable list */ nco_dmn_lst_ass_var /* [fnc] Create list of all dimensions associated with input variable list */ (const int nc_id, /* I [id] netCDF input-file ID */ const nm_id_sct * const var, /* I [sct] Variable list */ const int nbr_var, /* I [nbr] Number of variables in list */ int * const nbr_dmn) /* O [nbr] Number of dimensions associated with input variable list */ { /* Purpose: Create list of all dimensions associated with input variable list */ nco_bool dmn_has_been_placed_on_list; char dmn_nm[NC_MAX_NAME]; int dmn_id[NC_MAX_DIMS]; int idx_dmn_in; int idx_var; int idx_var_dmn; int idx_dmn_lst; int nbr_dmn_in; int nbr_var_dmn; nm_id_sct *dmn; *nbr_dmn=0; /* Get number of dimensions */ (void)nco_inq(nc_id,&nbr_dmn_in,(int *)NULL,(int *)NULL,(int *)NULL); /* Number of input dimensions is upper bound on number of output dimensions */ dmn=(nm_id_sct *)nco_malloc(nbr_dmn_in*sizeof(nm_id_sct)); /* ...For each dimension in file... */ for(idx_dmn_in=0;idx_dmn_in<nbr_dmn_in;idx_dmn_in++){ /* ...begin search for dimension in dimension list by... */ dmn_has_been_placed_on_list=False; /* ...looking through the set of output variables... */ for(idx_var=0;idx_var<nbr_var;idx_var++){ /* ...and searching each dimension of each output variable... */ (void)nco_inq_var(nc_id,var[idx_var].id,(char *)NULL,(nc_type *)NULL,&nbr_var_dmn,dmn_id,(int *)NULL); for(idx_var_dmn=0;idx_var_dmn<nbr_var_dmn;idx_var_dmn++){ /* ...until output variable is found which contains input dimension... */ if(idx_dmn_in == dmn_id[idx_var_dmn]){ /* ...then search each member of output dimension list... */ for(idx_dmn_lst=0;idx_dmn_lst<*nbr_dmn;idx_dmn_lst++){ /* ...until input dimension is found... */ if(idx_dmn_in == dmn[idx_dmn_lst].id) break; /* ...then search no further... */ } /* end loop over idx_dmn_lst */ /* ...and if dimension was not found on output dimension list... */ if(idx_dmn_lst == *nbr_dmn){ /* ...then add dimension to output dimension list... */ (void)nco_inq_dimname(nc_id,idx_dmn_in,dmn_nm); dmn[*nbr_dmn].id=idx_dmn_in; dmn[*nbr_dmn].nm=(char *)strdup(dmn_nm); (*nbr_dmn)++; } /* end if dimension was not found in current output dimension list */ /* ...call off the dogs for this input dimension... */ dmn_has_been_placed_on_list=True; } /* end if input dimension belongs to this output variable */ if(dmn_has_been_placed_on_list) break; /* break out of idx_var_dmn to idx_var */ } /* end loop over idx_var_dmn */ if(dmn_has_been_placed_on_list) break; /* break out of idx_var to idx_dmn_in */ } /* end loop over idx_var */ } /* end loop over idx_dmn_in */ /* We now have final list of dimensions to extract. Phew. */ /* Free unused space in output dimension list */ dmn=(nm_id_sct *)nco_realloc((void *)dmn,*nbr_dmn*sizeof(nm_id_sct)); return dmn; } /* end nco_dmn_lst_ass_var() */
nco_bool nco_find_lat_lon_trv (const int nc_id, /* I [ID] netCDF file ID */ const trv_sct * const var_trv, /* I [sct] Variable to search for "standard_name" attribute */ const char * const att_val_trg, /* I [sng] Attribute value to find ("latitude" or "longitude") */ char **var_nm_fll, /* I/O [sng] Full name of variable that has "latitude" or "longitude" attributes */ int *dmn_id, /* I/O [id] Dimension ID of "latitude" and "longitude" */ nc_type *crd_typ, /* I/O [enm] netCDF type of both "latitude" and "longitude" */ char units[]) /* I/O [sng] Units of both "latitude" and "longitude" */ { /* Purpose: Find auxiliary coordinate variables that map to latitude/longitude Find variables with standard_name = "latitude" and "longitude" Return true if both latitude and longitude standard names are found Also return needed information about these auxiliary coordinates Assumes that units and types for latitude and longitude are identical Caller responsible for memory management for variable names Memory for unit strings must be freed by caller */ const char fnc_nm[]="nco_find_lat_lon_trv()"; char att_nm[NC_MAX_NAME]; /* [sng] Attribute name */ char var_nm[NC_MAX_NAME]; int grp_id; /* [id] Group ID */ int var_id; /* [id] Variable ID */ int var_dimid[NC_MAX_VAR_DIMS]; /* [enm] Dimension ID */ int var_att_nbr; /* [nbr] Number of attributes */ int var_dmn_nbr; /* [nbr] Number of dimensions */ nc_type var_typ; /* [enm] variable type */ assert(var_trv->nco_typ == nco_obj_typ_var); /* Obtain group ID */ (void)nco_inq_grp_full_ncid(nc_id,var_trv->grp_nm_fll,&grp_id); /* Obtain variable ID */ (void)nco_inq_varid(grp_id,var_trv->nm,&var_id); /* Find number of attributes */ (void)nco_inq_var(grp_id,var_id,var_nm,&var_typ,&var_dmn_nbr,var_dimid,&var_att_nbr); assert(var_att_nbr == var_trv->nbr_att); for(int idx_att=0;idx_att<var_att_nbr;idx_att++){ /* Skip attribute if not "standard_name" */ (void)nco_inq_attname(grp_id,var_id,idx_att,att_nm); if(strcmp(att_nm,"standard_name")) continue; char att_val[NC_MAX_NAME+1]; long att_lng; (void)nco_inq_attlen(grp_id,var_id,"standard_name",&att_lng); (void)NCO_GET_ATT_CHAR(grp_id,var_id,"standard_name",att_val); att_val[att_lng]='\0'; /* Match parameter name to find ("latitude" or "longitude") */ if(!strcmp(att_val,att_val_trg)){ /* Assume same units (degrees or radians) for both lat and lon */ int rcd=nco_inq_attlen_flg(grp_id,var_id,"units",&att_lng); if(rcd != NC_NOERR){ if(nco_dbg_lvl_get() >= nco_dbg_var) (void)fprintf(stdout,"%s: %s reports CF convention requires \"latitude\" to have units attribute\n",nco_prg_nm_get(),fnc_nm); return False; } /* endif */ NCO_GET_ATT_CHAR(grp_id,var_id,"units",units); units[att_lng]='\0'; if(var_dmn_nbr > 1) (void)fprintf(stderr,"%s: WARNING %s reports latitude variable %s has %d dimensions. NCO only supports hyperslabbing of auxiliary coordinate variables with a single dimension. Continuing with unpredictable results...\n",nco_prg_nm_get(),fnc_nm,var_nm,var_dmn_nbr); /* Copy values to export */ *var_nm_fll=(char *)strdup(var_trv->nm_fll); *crd_typ=var_typ; *dmn_id=var_dimid[0]; return True; } /* strcmp() */ } /* end loop over attributes */ return False; } /* end nco_find_lat_lon_trv() */
nco_bool nco_find_lat_lon (int nc_id, char var_nm_lat[], char var_nm_lon[], char **units, int *lat_id, int *lon_id, nc_type *crd_typ) { /* Purpose: Find auxiliary coordinate variables that map to latitude/longitude Find variables with standard_name = "latitude" and "longitude" Return true if both latitude and longitude standard names are found Also return needed information about these auxiliary coordinates Assumes that units and types for latitude and longitude are identical Caller responsible for memory management for variable names Memory for unit strings must be freed by caller */ const char fnc_nm[]="nco_find_lat_lon()"; char var_nm[NC_MAX_NAME]; char att_val[NC_MAX_NAME]; int idx; int nvars=0; int rcd=NC_NOERR; int crd_nbr=0; int var_dimid[NC_MAX_VAR_DIMS]; /* [enm] Dimension ID */ int var_att_nbr; /* [nbr] Number of attributes */ int var_dmn_nbr; /* [nbr] Number of dimensions */ long att_lng; nc_type var_typ; /* [enm] variable type */ /* Make sure CF tag exists. Currently require CF-1.X value */ if(NCO_GET_ATT_CHAR(nc_id,NC_GLOBAL,"Conventions",att_val) || !strstr(att_val,"CF-1.")) if(nco_dbg_lvl_get() >= nco_dbg_dev) (void)fprintf(stderr,"%s: WARNING %s reports file \"Convention\" attribute is missing or is present but not of the form \"CF-1.X\". Auxiliary coordinate support (i.e., the -X option) cannot be expected to behave well file does not support CF-1.X metadata conventions. Continuing anyway...\n",nco_prg_nm_get(),fnc_nm); /* Get number of variables */ rcd=nco_inq_nvars(nc_id,&nvars); /* For each variable, see if standard name is latitude or longitude */ for(idx=0;idx<nvars && crd_nbr<2;idx++){ nco_inq_var(nc_id,idx,var_nm,&var_typ,&var_dmn_nbr,var_dimid,&var_att_nbr); att_lng=0; if(!nco_inq_attlen_flg(nc_id,idx,"standard_name",&att_lng)){ NCO_GET_ATT_CHAR(nc_id,idx,"standard_name",att_val); att_val[att_lng]='\0'; if(!strcmp(att_val,"latitude")){ strcpy(var_nm_lat,var_nm); *lat_id=idx; /* Get units; assume same for both lat and lon */ rcd=nco_inq_attlen(nc_id,idx,"units",&att_lng); if(rcd != NC_NOERR) nco_err_exit(rcd,"nco_find_lat_lon() reports CF convention requires \"latitude\" to have units attribute\n"); *units=(char *)nco_malloc((att_lng+1L)*sizeof(char)); NCO_GET_ATT_CHAR(nc_id,idx,"units",*units); (*units)[att_lng]='\0'; if(var_dmn_nbr > 1) (void)fprintf(stderr,"%s: WARNING %s reports latitude variable %s has %d dimensions. NCO only supports hyperslabbing of auxiliary coordinate variables with a single dimension. Continuing with unpredictable results...\n",nco_prg_nm_get(),fnc_nm,var_nm,var_dmn_nbr); /* Assign type; assumed same for both lat and lon */ *crd_typ=var_typ; crd_nbr++; } /* endif latitude */ if(!strcmp(att_val,"longitude")){ strcpy(var_nm_lon,var_nm); *lon_id=idx; crd_nbr++; } /* endif longitude */ if(nco_dbg_lvl_get() >= nco_dbg_dev) (void)fprintf(stdout,"%s: DEBUG %s variable <%s>\n",nco_prg_nm_get(),fnc_nm,var_nm); } /* endif standard_name */ } /* end loop over vars */ if(crd_nbr != 2){ if(nco_dbg_lvl_get() >= nco_dbg_dev) (void)fprintf(stdout,"%s: %s unable to identify lat/lon auxiliary coordinate variables.\n",nco_prg_nm_get(),fnc_nm); return False; }else return True; } /* end nco_find_lat_lon() */
void nco_aed_prc /* [fnc] Process single attribute edit for single variable */ (const int nc_id, /* I [id] Input netCDF file ID */ const int var_id, /* I [id] ID of variable on which to perform attribute editing */ const aed_sct aed) /* I [sct] Structure containing information necessary to edit */ { /* Purpose: Process single attribute edit for single variable */ /* If var_id == NC_GLOBAL ( = -1) then global attribute will be edited */ #ifdef NCO_NETCDF4_AND_FILLVALUE char att_nm_tmp[]="eulaVlliF_"; /* String of same length as "_FillValue" for name hack with netCDF4 */ nco_bool flg_netCDF4=False; /* [flg] File format is netCDF4 */ #endif /* !NCO_NETCDF4_AND_FILLVALUE */ char att_nm[NC_MAX_NAME]; char var_nm[NC_MAX_NAME]; /* NB: netCDF2 specifies att_sz is type int, netCDF3 and netCDF4 use size_t */ int nbr_att; /* [nbr] Number of attributes */ int rcd=NC_NOERR; /* [rcd] Return code */ long att_sz; nc_type att_typ; void *att_val_new=NULL; if(var_id == NC_GLOBAL){ /* Get number of global attributes in file */ (void)nco_inq(nc_id,(int *)NULL,(int *)NULL,&nbr_att,(int *)NULL); (void)strcpy(var_nm,"Global"); }else{ /* Get name and number of attributes for variable */ (void)nco_inq_var(nc_id,var_id,var_nm,(nc_type *)NULL,(int *)NULL,(int *)NULL,&nbr_att); } /* end else */ /* Query attribute metadata when attribute name was specified */ if(aed.att_nm) rcd=nco_inq_att_flg(nc_id,var_id,aed.att_nm,&att_typ,&att_sz); /* Before changing metadata, change missing values to new missing value if warranted This capability is add-on feature not implemented too cleanly or efficiently If every variable has "_FillValue" attribute and "_FillValue" is changed globally, then algorithm goes into and out of define mode for each variable, rather than collecting all information in first pass and replacing all data in second pass. This is because ncatted was originally designed to change only metadata and so was architected differently from other NCO operators. */ if( aed.att_nm /* Linux strcmp() dumps core if attribute name is blank */ && strcmp(aed.att_nm,nco_mss_val_sng_get()) == 0 /* Current attribute is "_FillValue" */ && var_id != NC_GLOBAL /* Current attribute is not global */ && (aed.mode == aed_modify || aed.mode == aed_overwrite) /* Modifying or overwriting existing value */ && rcd == NC_NOERR /* Only when existing _FillValue attribute is modified */ && att_sz == 1L /* Old _FillValue attribute must be of size 1 */ && aed.sz == 1L /* New _FillValue attribute must be of size 1 */ ){ int *dmn_id; long *dmn_sz; long *dmn_srt; long idx; long var_sz=long_CEWI; ptr_unn mss_val_crr; ptr_unn mss_val_new; ptr_unn var_val; var_sct *var=NULL_CEWI; if(dbg_lvl_get() >= nco_dbg_std) (void)fprintf(stdout,"%s: INFO Replacing missing value data in variable %s\n",prg_nm_get(),var_nm); /* Take file out of define mode */ (void)nco_enddef(nc_id); /* Initialize (partially) variable structure */ var=(var_sct *)nco_malloc(sizeof(var_sct)); var->nc_id=nc_id; var->id=var_id; var->sz=1L; /* Get type of variable and number of dimensions */ (void)nco_inq_var(nc_id,var_id,(char *)NULL,&var->type,&var->nbr_dim,(int *)NULL,(int *)NULL); dmn_id=(int *)nco_malloc(var->nbr_dim*sizeof(int)); dmn_sz=(long *)nco_malloc(var->nbr_dim*sizeof(long)); dmn_srt=(long *)nco_malloc(var->nbr_dim*sizeof(long)); (void)nco_inq_vardimid(nc_id,var_id,dmn_id); /* Get dimension sizes and construct variable size */ for(idx=0;idx<var->nbr_dim;idx++){ (void)nco_inq_dimlen(nc_id,dmn_id[idx],dmn_sz+idx); var->sz*=dmn_sz[idx]; dmn_srt[idx]=0L; } /* end loop over dim */ var->dmn_id=dmn_id; var->cnt=dmn_sz; var->srt=dmn_srt; /* Place nco_var_get() code inline since var struct is not truly complete */ if((var->val.vp=(void *)nco_malloc_flg(var->sz*nco_typ_lng(var->type))) == NULL){ (void)fprintf(stdout,"%s: ERROR Unable to malloc() %ld*%lu bytes in nco_aed_prc()\n",prg_nm_get(),var->sz,(unsigned long)nco_typ_lng(var->type)); nco_exit(EXIT_FAILURE); } /* end if */ if(var->sz > 1L){ (void)nco_get_vara(nc_id,var_id,var->srt,var->cnt,var->val.vp,var->type); }else{ (void)nco_get_var1(nc_id,var_id,var->srt,var->val.vp,var->type); } /* end else */ /* Get current missing value attribute */ var->mss_val.vp=NULL; var->has_mss_val=nco_mss_val_get(nc_id,var); /* Sanity check */ if(var->has_mss_val == False){ (void)fprintf(stdout,"%s: ERROR variable %s does not have \"%s\" attribute in nco_aed_prc()\n",prg_nm_get(),var_nm,nco_mss_val_sng_get()); nco_exit(EXIT_FAILURE); } /* end if */ /* Shortcuts to avoid indirection */ var_val=var->val; var_sz=var->sz; /* Get new and old missing values in same type as variable */ mss_val_crr.vp=(void *)nco_malloc(att_sz*nco_typ_lng(var->type)); mss_val_new.vp=(void *)nco_malloc(aed.sz*nco_typ_lng(var->type)); (void)nco_val_cnf_typ(var->type,var->mss_val,var->type,mss_val_crr); (void)nco_val_cnf_typ(aed.type,aed.val,var->type,mss_val_new); /* Typecast pointer to values before access */ (void)cast_void_nctype(var->type,&var_val); (void)cast_void_nctype(var->type,&mss_val_crr); (void)cast_void_nctype(var->type,&mss_val_new); switch(var->type){ case NC_FLOAT: for(idx=0L;idx<var_sz;idx++) {if(var_val.fp[idx] == *mss_val_crr.fp) var_val.fp[idx]=*mss_val_new.fp;} break; case NC_DOUBLE: for(idx=0L;idx<var_sz;idx++) {if(var_val.dp[idx] == *mss_val_crr.dp) var_val.dp[idx]=*mss_val_new.dp;} break; case NC_INT: for(idx=0L;idx<var_sz;idx++) {if(var_val.ip[idx] == *mss_val_crr.ip) var_val.ip[idx]=*mss_val_new.ip;} break; case NC_SHORT: for(idx=0L;idx<var_sz;idx++) {if(var_val.sp[idx] == *mss_val_crr.sp) var_val.sp[idx]=*mss_val_new.sp;} break; case NC_CHAR: for(idx=0L;idx<var_sz;idx++) {if(var_val.cp[idx] == *mss_val_crr.cp) var_val.cp[idx]=*mss_val_new.cp;} break; case NC_BYTE: for(idx=0L;idx<var_sz;idx++) {if(var_val.bp[idx] == *mss_val_crr.bp) var_val.bp[idx]=*mss_val_new.bp;} break; case NC_UBYTE: for(idx=0L;idx<var_sz;idx++) {if(var_val.ubp[idx] == *mss_val_crr.ubp) var_val.ubp[idx]=*mss_val_new.ubp;} break; case NC_USHORT: for(idx=0L;idx<var_sz;idx++) {if(var_val.usp[idx] == *mss_val_crr.usp) var_val.usp[idx]=*mss_val_new.usp;} break; case NC_UINT: for(idx=0L;idx<var_sz;idx++) {if(var_val.uip[idx] == *mss_val_crr.uip) var_val.uip[idx]=*mss_val_new.uip;} break; case NC_INT64: for(idx=0L;idx<var_sz;idx++) {if(var_val.i64p[idx] == *mss_val_crr.i64p) var_val.i64p[idx]=*mss_val_new.i64p;} break; case NC_UINT64: for(idx=0L;idx<var_sz;idx++) {if(var_val.ui64p[idx] == *mss_val_crr.ui64p) var_val.ui64p[idx]=*mss_val_new.ui64p;} break; case NC_STRING: for(idx=0L;idx<var_sz;idx++) {if(var_val.sngp[idx] == *mss_val_crr.sngp) var_val.sngp[idx]=*mss_val_new.sngp;} break; default: nco_dfl_case_nc_type_err(); break; } /* end switch */ /* Un-typecast the pointer to values after access */ (void)cast_nctype_void(var->type,&var_val); (void)cast_nctype_void(var->type,&mss_val_crr); (void)cast_nctype_void(var->type,&mss_val_new); /* Write to disk */ if(var->nbr_dim == 0){ (void)nco_put_var1(nc_id,var_id,var->srt,var->val.vp,var->type); }else{ /* end if variable is scalar */ (void)nco_put_vara(nc_id,var_id,var->srt,var->cnt,var->val.vp,var->type); } /* end else */ /* Free memory */ mss_val_crr.vp=nco_free(mss_val_crr.vp); mss_val_new.vp=nco_free(mss_val_new.vp); var->mss_val.vp=nco_free(var->mss_val.vp); var->val.vp=nco_free(var->val.vp); var->dmn_id=(int *)nco_free(var->dmn_id); var->srt=(long *)nco_free(var->srt); var->cnt=(long *)nco_free(var->cnt); /* 20050704 try and use nco_free() to avoid valgrind error message */ var=(var_sct *)nco_free(var); /* Put file back in define mode */ (void)nco_redef(nc_id); } /* end if replacing missing value data */ /* Change metadata (as written, this must be done after _FillValue data is replaced) */ #ifdef NCO_NETCDF4_AND_FILLVALUE /* Bold hack which gets around problem of modifying netCDF4 "_FillValue" attributes netCDF4 does not allow this by default, though netCDF3 does Change attribute name to att_nm_tmp, modify value, then restore name */ /* Check if file is netCDF3 classic with netCDF4 library If so, do not kludge. NB: create global variable for output file format? */ { /* scope for fl_fmt temporary */ int fl_fmt; (void)nco_inq_format(nc_id,&fl_fmt); flg_netCDF4=(fl_fmt==NC_FORMAT_NETCDF4 || fl_fmt==NC_FORMAT_NETCDF4_CLASSIC); } /* end scope */ if(flg_netCDF4 && !strcmp(aed.att_nm,nco_mss_val_sng_get()) && aed.mode != aed_delete){ if(aed.mode != aed_create) (void)nco_rename_att(nc_id,var_id,aed.att_nm,att_nm_tmp); strcpy(aed.att_nm,att_nm_tmp); } /* endif libnetCDF may have netCDF4 restrictions */ #endif /* !NCO_NETCDF4_AND_FILLVALUE */ switch(aed.mode){ case aed_append: if(rcd == NC_NOERR){ /* Append to existing attribute value */ if(aed.type != att_typ){ (void)fprintf(stdout,"%s: ERROR %s attribute %s is of type %s not %s, unable to append\n",prg_nm_get(),var_nm,aed.att_nm,nco_typ_sng(att_typ),nco_typ_sng(aed.type)); nco_exit(EXIT_FAILURE); } /* end if */ att_val_new=(void *)nco_malloc((att_sz+aed.sz)*nco_typ_lng(aed.type)); (void)nco_get_att(nc_id,var_id,aed.att_nm,(void *)att_val_new,aed.type); /* NB: Following assumes sizeof(char) = 1 byte */ (void)memcpy((void *)((char *)att_val_new+att_sz*nco_typ_lng(aed.type)), (void *)aed.val.vp, aed.sz*nco_typ_lng(aed.type)); (void)nco_put_att(nc_id,var_id,aed.att_nm,aed.type,att_sz+aed.sz,att_val_new); att_val_new=nco_free(att_val_new); }else{ /* Create new attribute */ (void)nco_put_att(nc_id,var_id,aed.att_nm,aed.type,aed.sz,aed.val.vp); } /* end else */ break; case aed_create: if(rcd != NC_NOERR) (void)nco_put_att(nc_id,var_id,aed.att_nm,aed.type,aed.sz,aed.val.vp); break; case aed_delete: /* Delete specified attribute if attribute name was specified... */ if(aed.att_nm){ /* ...and if attribute is known to exist from previous inquire call... */ if(rcd == NC_NOERR) (void)nco_del_att(nc_id,var_id,aed.att_nm); }else{ /* ...else delete all attributes for this variable... */ while(nbr_att){ (void)nco_inq_attname(nc_id,var_id,nbr_att-1,att_nm); (void)nco_del_att(nc_id,var_id,att_nm); nbr_att--; } /* end while */ } /* end else */ break; case aed_modify: if(rcd == NC_NOERR) (void)nco_put_att(nc_id,var_id,aed.att_nm,aed.type,aed.sz,aed.val.vp); break; case aed_overwrite: (void)nco_put_att(nc_id,var_id,aed.att_nm,aed.type,aed.sz,aed.val.vp); break; default: break; } /* end switch */ #ifdef NCO_NETCDF4_AND_FILLVALUE if(flg_netCDF4 && !strcmp(aed.att_nm,att_nm_tmp) && aed.mode != aed_delete){ (void)nco_rename_att(nc_id,var_id,att_nm_tmp,nco_mss_val_sng_get()); /* Restore original name (space already allocated) */ strcpy(aed.att_nm,nco_mss_val_sng_get()); } /* !flg_netCDF4 */ #endif /* !NCO_NETCDF4_AND_FILLVALUE */ } /* end nco_aed_prc() */
void nco_cnk_sz_set /* [fnc] Set chunksize parameters */ (const int nc_id, /* I [id] netCDF file ID */ CST_X_PTR_CST_PTR_CST_Y(lmt_all_sct,lmt_all_lst), /* I [sct] Hyperslab limits */ const int lmt_all_lst_nbr, /* I [nbr] Number of hyperslab limits */ int * const cnk_map_ptr, /* I/O [enm] Chunking map */ int * const cnk_plc_ptr, /* I/O [enm] Chunking policy */ const size_t cnk_sz_scl, /* I [nbr] Chunk size scalar */ CST_X_PTR_CST_PTR_CST_Y(cnk_sct,cnk), /* I [sct] Chunking information */ const int cnk_nbr) /* I [nbr] Number of dimensions with user-specified chunking */ { /* Purpose: Use chunking map and policy to determine chunksize list */ const char fnc_nm[]="nco_cnk_sz_set()"; /* [sng] Function name */ char dmn_nm[NC_MAX_NAME]; char var_nm[NC_MAX_NAME]; int *dmn_id; int cnk_idx; int dmn_idx; int cnk_map; /* [enm] Chunking map */ int cnk_plc; /* [enm] Chunking policy */ int chk_typ; /* [enm] Checksum type */ int deflate; /* [enm] Deflate filter is on */ int dmn_nbr; /* [nbr] Number of dimensions in variable */ int fl_fmt; /* [enm] Input file format */ int lmt_idx; int lmt_idx_rec=int_CEWI; int nbr_dmn_fl; /* [nbr] Number of dimensions in file */ int rcd_dmn_id; int srg_typ; /* [enm] Storage type */ int var_idx; int var_nbr; long dmn_sz; nco_bool flg_cnk=False; /* [flg] Chunking requested */ nco_bool is_rec_var; /* [flg] Record variable */ nco_bool is_chk_var; /* [flg] Checksummed variable */ nco_bool is_cmp_var; /* [flg] Compressed variable */ nco_bool is_chunked; /* [flg] Chunked variable */ nco_bool must_be_chunked; /* [flg] Variable must be chunked */ nc_type var_typ_dsk; size_t *cnk_sz; /* [nbr] Chunksize list */ size_t cnk_sz_dfl; /* [nbr] Chunksize default */ /* Did user explicitly request chunking? */ if(cnk_nbr > 0 || cnk_sz_scl > 0UL || *cnk_map_ptr != nco_cnk_map_nil || *cnk_plc_ptr != nco_cnk_plc_nil) flg_cnk=True; if(!flg_cnk) return; /* Set actual chunk policy and map to defaults as necessary This rather arcane procedure saves a few lines of code in calling program (because defaults not set there) while maintaining correctness of arguments */ if(*cnk_map_ptr == nco_cnk_map_nil) *cnk_map_ptr=nco_cnk_map_get((char *)NULL); if(*cnk_plc_ptr == nco_cnk_plc_nil) *cnk_plc_ptr=nco_cnk_plc_get((char *)NULL); cnk_map=*cnk_map_ptr; cnk_plc=*cnk_plc_ptr; /* Bail on unsupported options */ if(cnk_plc == nco_cnk_plc_xpl){ (void)fprintf(stderr,"%s: ERROR cnk_plc = %s not yet supported\n",prg_nm_get(),nco_cnk_plc_sng_get(cnk_plc)); nco_exit(EXIT_FAILURE); } /* endif */ /* Does output file support chunking? */ (void)nco_inq_format(nc_id,&fl_fmt); if(fl_fmt != NC_FORMAT_NETCDF4 && fl_fmt != NC_FORMAT_NETCDF4_CLASSIC){ (void)fprintf(stderr,"%s: WARNING Output file format is %s so chunking request will be ignored\n",prg_nm_get(),nco_fmt_sng(fl_fmt)); return; } /* endif dbg */ /* Vet input */ if(cnk_map == nco_cnk_map_scl && cnk_sz_scl <= 0){ (void)fprintf(stderr,"%s: ERROR cnk_sz_scl = %lu must be greater than 0\n",prg_nm_get(),(unsigned long)cnk_sz_scl); nco_exit(EXIT_FAILURE); } /* endif cnk_sz_scl */ if(dbg_lvl_get() >= nco_dbg_fl) (void)fprintf(stderr,"%s: INFO Requested chunking or unchunking\n",prg_nm_get()); if(dbg_lvl_get() >= nco_dbg_scl){ (void)fprintf(stderr,"cnk_plc: %s\n",nco_cnk_plc_sng_get(cnk_plc)); (void)fprintf(stderr,"cnk_map: %s\n",nco_cnk_map_sng_get(cnk_map)); (void)fprintf(stderr,"cnk_sz_scl: %lu\n",(unsigned long)cnk_sz_scl); if(cnk_nbr > 0){ (void)fprintf(stderr,"idx dmn_nm\tcnk_sz:\n"); for(cnk_idx=0;cnk_idx<cnk_nbr;cnk_idx++) (void)fprintf(stderr,"%2d %s\t%lu\n",cnk_idx,cnk[cnk_idx]->nm,(unsigned long)cnk[cnk_idx]->sz); } /* cnk_nbr == 0 */ } /* endif dbg */ /* Get record dimension ID */ (void)nco_inq(nc_id,&nbr_dmn_fl,&var_nbr,(int *)NULL,&rcd_dmn_id); /* Find record dimension, if any, in limit structure list first This information may be needed below */ if(rcd_dmn_id != NCO_REC_DMN_UNDEFINED){ (void)nco_inq_dimname(nc_id,rcd_dmn_id,dmn_nm); for(lmt_idx=0;lmt_idx<lmt_all_lst_nbr;lmt_idx++){ if(!strcmp(dmn_nm,lmt_all_lst[lmt_idx]->dmn_nm)){ lmt_idx_rec=lmt_idx; break; } /* end if */ } /* end loop over limit */ } /* NCO_REC_DMN_UNDEFINED */ /* NB: Assumes variable IDs range from [0..var_nbr-1] */ for(var_idx=0;var_idx<var_nbr;var_idx++){ /* Initialize storage type for this variable */ srg_typ=NC_CONTIGUOUS; /* [enm] Storage type */ cnk_sz=(size_t *)NULL; /* [nbr] Chunksize list */ is_rec_var=False; /* [flg] Record variable */ is_chk_var=False; /* [flg] Checksummed variable */ is_cmp_var=False; /* [flg] Compressed variable */ is_chunked=False; /* [flg] Chunked variable */ /* Get type and number of dimensions for variable */ (void)nco_inq_var(nc_id,var_idx,var_nm,&var_typ_dsk,&dmn_nbr,(int *)NULL,(int *)NULL); if(dmn_nbr == 0) continue; /* Skip chunking calls for scalars */ /* Allocate space to hold dimension IDs */ dmn_id=(int *)nco_malloc(dmn_nbr*sizeof(int)); /* Get dimension IDs */ (void)nco_inq_vardimid(nc_id,var_idx,dmn_id); /* Is this a record variable? */ if(rcd_dmn_id != NCO_REC_DMN_UNDEFINED){ for(dmn_idx=0;dmn_idx<dmn_nbr;dmn_idx++){ /* Is this the record dimension? */ if(dmn_id[dmn_idx] == rcd_dmn_id) break; /* ...then search no further */ } /* end loop over dmn */ if(dmn_idx < dmn_nbr) is_rec_var=True; /* [flg] Record variable */ } /* NCO_REC_DMN_UNDEFINED */ /* Is variable compressed? */ (void)nco_inq_var_deflate(nc_id,var_idx,NULL,&deflate,NULL); if(deflate) is_cmp_var=True; /* Is variable checksummed? */ (void)nco_inq_var_fletcher32(nc_id,var_idx,&chk_typ); if(chk_typ != NC_NOCHECKSUM) is_chk_var=True; /* Must variable be chunked? */ if(is_rec_var || is_chk_var || is_cmp_var) must_be_chunked=True; else must_be_chunked=False; /* Is variable currently chunked? */ is_chunked=nco_cnk_dsk_inq(nc_id,var_idx); /* Explicitly turn off chunking for arrays that are... */ if((cnk_plc == nco_cnk_plc_g2d && dmn_nbr < 2) || /* ...much too small... */ (cnk_plc == nco_cnk_plc_g3d && dmn_nbr < 3) || /* ...too small... */ (cnk_plc == nco_cnk_plc_uck) || /* ...intentionally unchunked... */ False){ /* If variable is chunked */ if(is_chunked){ if(must_be_chunked){ if(dbg_lvl_get() >= nco_dbg_var) (void)fprintf(stderr,"%s: INFO %s %s must be chunked (record, compressed, or checksummed variable)\n",prg_nm_get(),fnc_nm,var_nm); }else{ /* Turn off chunking for this variable */ if(dbg_lvl_get() >= nco_dbg_var) (void)fprintf(stderr,"%s: INFO %s unchunking %s\n",prg_nm_get(),fnc_nm,var_nm); (void)nco_def_var_chunking(nc_id,var_idx,srg_typ,cnk_sz); } /* !must_be_chunked */ }else{ /* !chunked */ if(dbg_lvl_get() >= nco_dbg_var) (void)fprintf(stderr,"%s: INFO %s not unchunking %s because it is not chunked\n",prg_nm_get(),fnc_nm,var_nm); } /* !chunked */ /* Free space holding dimension IDs before skipping to next variable */ dmn_id=(int *)nco_free(dmn_id); /* Skip to next variable in loop */ continue; } /* end if */ /* Variable will definitely be chunked */ srg_typ=NC_CHUNKED; /* [enm] Storage type */ if(dbg_lvl_get() >= nco_dbg_var) (void)fprintf(stderr,"%s: INFO %s %schunking %s\n",prg_nm_get(),fnc_nm,(is_chunked ? "re-" : "" ),var_nm); /* Allocate space to hold chunksizes */ cnk_sz=(size_t *)nco_malloc(dmn_nbr*sizeof(size_t)); /* Default "equal" chunksize for each dimension */ cnk_sz_dfl=cnk_sz_scl; if(cnk_map == nco_cnk_map_prd){ double cnk_sz_prd_dbl; /* [nbr] Chunksize product, double precision */ double cnk_sz_eql_dbl; /* [nbr] Chunksize equal, double precision */ double cnk_sz_dfl_dbl; /* [nbr] Chunksize default, double precision */ cnk_sz_prd_dbl=cnk_sz_scl; cnk_sz_eql_dbl=pow(cnk_sz_prd_dbl,1.0/dmn_nbr); cnk_sz_dfl_dbl=ceil(cnk_sz_eql_dbl); cnk_sz_dfl=(size_t)cnk_sz_dfl_dbl; } /* endif map_prd */ for(dmn_idx=0;dmn_idx<dmn_nbr;dmn_idx++){ /* Get dimension name and size */ (void)nco_inq_dim(nc_id,dmn_id[dmn_idx],dmn_nm,&dmn_sz); /* Is this the record dimension? */ if(dmn_id[dmn_idx] == rcd_dmn_id){ /* Does policy specify record dimension treatment? */ if(cnk_map == nco_cnk_map_rd1){ cnk_sz[dmn_idx]=1UL; /* This may still be over-ridden by explicitly specified chunksize */ goto cnk_xpl_override; } /* !nco_cnk_map_rd1 */ /* Record dimension size in output file is zero until first write Obtain record dimension size from lmt_all structure */ if(lmt_all_lst[lmt_idx_rec]->BASIC_DMN){ /* When not hyperslabbed, use input record dimension size ... */ cnk_sz[dmn_idx]=lmt_all_lst[lmt_idx_rec]->dmn_sz_org; }else{ /* !BASIC_DMN */ /* ... and when hyperslabbed, use user-specified count */ cnk_sz[dmn_idx]=lmt_all_lst[lmt_idx_rec]->dmn_cnt; } /* !BASIC_DMN */ }else{ /* !record dimension */ /* Set non-record dimensions to default, possibly over-ride later */ cnk_sz[dmn_idx]=dmn_sz; if(dmn_sz == 0L){ (void)fprintf(stderr,"%s: ERROR %s reports variable %s has dim_sz == 0L for non-record dimension %s. This should not occur and it will cause chunking to fail...\n",prg_nm_get(),fnc_nm,var_nm,dmn_nm); } /* endif err */ } /* !record dimension */ /* Propagate scalar chunksize, if specified */ if(cnk_sz_dfl > 0UL){ if(dmn_id[dmn_idx] == rcd_dmn_id){ if(lmt_all_lst[lmt_idx_rec]->BASIC_DMN){ /* When not hyperslabbed, use input record dimension size ... */ cnk_sz[dmn_idx]=(cnk_sz_dfl <= (size_t)lmt_all_lst[lmt_idx_rec]->dmn_sz_org) ? cnk_sz_dfl : (size_t)lmt_all_lst[lmt_idx_rec]->dmn_sz_org; }else{ /* !BASIC_DMN */ /* ... and when hyperslabbed, use user-specified count */ cnk_sz[dmn_idx]=(cnk_sz_dfl <= (size_t)lmt_all_lst[lmt_idx_rec]->dmn_cnt) ? cnk_sz_dfl : (size_t)lmt_all_lst[lmt_idx_rec]->dmn_cnt; } /* !BASIC_DMN */ }else{ /* !rcd_dmn_id */ /* Non-record sizes default to cnk_sz_dfl or to dimension size */ cnk_sz[dmn_idx]=(cnk_sz_dfl <= (size_t)dmn_sz) ? cnk_sz_dfl : (size_t)dmn_sz; } /* !rcd_dmn_id */ } /* !cnk_sz_dfl */ cnk_xpl_override: /* end goto */ /* Explicit chunk specifications override all else */ for(cnk_idx=0;cnk_idx<cnk_nbr;cnk_idx++){ /* Match on name not ID */ if(!strcmp(cnk[cnk_idx]->nm,dmn_nm)){ cnk_sz[dmn_idx]=cnk[cnk_idx]->sz; if(dmn_id[dmn_idx] == rcd_dmn_id){ if(lmt_all_lst[lmt_idx_rec]->BASIC_DMN){ if(cnk_sz[dmn_idx] > (size_t)lmt_all_lst[lmt_idx_rec]->dmn_sz_org){ (void)fprintf(stderr,"%s: WARNING %s allowing user-specified record dimension chunksize = %lu for %s to exceed record dimension size in input file = %lu. May fail if output file is not concatenated from multiple inputs.\n",prg_nm_get(),fnc_nm,(unsigned long)cnk[cnk_idx]->sz,dmn_nm,lmt_all_lst[lmt_idx_rec]->dmn_sz_org); } /* endif too big */ }else{ /* !BASIC_DMN */ if(cnk_sz[dmn_idx] > (size_t)lmt_all_lst[lmt_idx_rec]->dmn_cnt){ (void)fprintf(stderr,"%s: WARNING %s allowing user-specified record dimension chunksize = %lu for %s to exceed user-specified record dimension hyperslab size in input file = %lu. May fail if output file is not concatenated from multiple inputs.\n",prg_nm_get(),fnc_nm,(unsigned long)cnk[cnk_idx]->sz,dmn_nm,lmt_all_lst[lmt_idx_rec]->dmn_cnt); } /* endif too big */ } /* !BASIC_DMN */ }else{ /* !rcd_dmn_id */ if(cnk_sz[dmn_idx] > (size_t)dmn_sz){ /* dmn_sz of record dimension may (will) be zero in output file Non-record dimensions, though, must have cnk_sz <= dmn_sz */ (void)fprintf(stderr,"%s: WARNING %s trimming user-specified chunksize = %lu to %s size = %lu\n",prg_nm_get(),fnc_nm,(unsigned long)cnk[cnk_idx]->sz,dmn_nm,dmn_sz); /* Trim else out-of-bounds sizes will fail in HDF library in nc_enddef() */ cnk_sz[dmn_idx]=(size_t)dmn_sz; } /* endif */ } /* !rcd_dmn_id */ break; } /* cnk_nm != dmn_nm */ } /* end loop over cnk */ } /* end loop over dmn */ if(dbg_lvl_get() >= nco_dbg_scl){ (void)fprintf(stderr,"idx nm\tdmn_sz\tcnk_sz for %s:\n",var_nm); for(dmn_idx=0;dmn_idx<dmn_nbr;dmn_idx++){ (void)nco_inq_dimlen(nc_id,dmn_id[dmn_idx],&dmn_sz); (void)nco_inq_dimname(nc_id,dmn_id[dmn_idx],dmn_nm); (void)fprintf(stderr,"%2d %s\t%lu\t%lu\n",dmn_idx,dmn_nm,dmn_sz,(unsigned long)cnk_sz[dmn_idx]); } /* end loop over dmn */ } /* endif dbg */ /* Turn chunking on for this variable */ (void)nco_def_var_chunking(nc_id,var_idx,srg_typ,cnk_sz); /* Free space holding dimension IDs and chunksizes */ dmn_id=(int *)nco_free(dmn_id); cnk_sz=(size_t *)nco_free(cnk_sz); } /* end loop over var */ return; } /* end nco_cnk_sz_set() */